diff --git a/source-server/.gitattributes b/source-server/.gitattributes new file mode 100644 index 000000000..fe701cc7b --- /dev/null +++ b/source-server/.gitattributes @@ -0,0 +1,22 @@ +### default behavior +* text=auto eol=lf +## note: eol in text auto is fixed since git 2.10 (31 aug 2016) + +### windows (always crlf) +*.bat eol=crlf +*.sln eol=crlf +*.filters eol=crlf +*.users eol=crlf +*.vcxproj eol=crlf +*.vssscc eol=crlf + +### unix (always lf) +*.sh eol=lf +*.bash eol=lf +*.py eol=lf + +### binaries +*.suo binary + +### others +*.txt eol=crlf diff --git a/source-server/.gitignore b/source-server/.gitignore new file mode 100644 index 000000000..15bca9e41 --- /dev/null +++ b/source-server/.gitignore @@ -0,0 +1,69 @@ +Extern +__test +__trash + +.vs + +*.ncb +*.opensdf +*.sdf +#+easy with .suo +#*.suo + +*.bsc +*.exe +*.ilk +*.map +*.pdb +*.idb + +obj +ipch +Debug +Release +MfcRelease +*___Win32_MfcRelease +MfcDebug +*___Win32_MfcDebug +Distribute + +bin + +OBJDIR +.obj + +Depend +Depend.bak + +*.a +*.dll +*.lib +*.o +*.so +*.iobj +*.ipdb + +db_r* +game_r* + +db_symlink +game_symlink + +pre_qc +*.pyc + +*.bak +/.project + +### archives +*.rar +*.7z +*.zip +!server-Extern.zip +*.gz +*.bz +*.xz +*.tar +*.tgz +*.tbz +*.txz diff --git a/source-server/README-SERVER.txt b/source-server/README-SERVER.txt new file mode 100644 index 000000000..1be88303f --- /dev/null +++ b/source-server/README-SERVER.txt @@ -0,0 +1,448 @@ +# README # + +### What is this repository for? ### + +* Quick summary +* Version +* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) +#### +# git config --global http.sslVerify false +# git config --global credential.helper "cache --timeout=3600" +### +# git add -A +# git commit -m "base added" +### or +# git commit -am "files updated" +### +# git push +### +# git pull +#### + +### How do I get set up? ### + +* Summary of set up +// mysql dep +# cd /usr/ports/databases/mysql56-client +# make WITH_XCHARSET=all install clean +## OR +# pkg install mysql56-client + +// extern extract +# cd ./Srcs +# tar -xzf Extern.tgz + +// cryptopp dep +# cd ./Srcs/Extern/cryptopp +# gmake libcryptopp.a -j4 + +//how to compile all-the-server (db+game+dep) +# cd ./Srcs/Server +# gmake all -j4 + +//how to compile just game (not necessary) +# cd ./Srcs/Server/game/src +# gmake -j4 + +//how to compile just db (not necessary) +# cd ./Srcs/Server/db/src +# gmake -j4 + +* Configuration +@#GENERAL MACROS +#define __OBSOLETE__ //useless and pointless code removed +#define __UNIMPLEMENTED__ //drafts of new things to be implemented + +@/Src/Server/common/CommonDefines.h +>>default +#define _IMPROVED_PACKET_ENCRYPTION_ //enable cryptopp mixed encryption for client<>server comunication +#define USE_IMPROVED_PACKET_DECRYPTED_BUFFER //enable a buffer for decrypting incoming packets sent at different times (for IMPROVED_PACKET_ENCRYPTION only) +#define __UDP_BLOCK__ +#define ENABLE_QUEST_CATEGORY //enable quest category+new packet types (unimplemented) +>>systems +#define ENABLE_D_NJGUILD //enable d.new_jump_all_guild+cpp relative functions (untested) +#define ENABLE_NEWSTUFF //enable new stuff (new lua funcs, new CONFIG options, ecc) +#define ENABLE_FULL_NOTICE //enable new big notice features +#define ENABLE_PORT_SECURITY // block db_port, p2p_port, and remote adminpage exploits +#define ENABLE_BELT_INVENTORY_EX // move the belt items into the BELT_INVENTORY window and prevents unknown belt positions if you de/increase wear/inventory slots +#define ENABLE_CMD_WARP_IN_DUNGEON // /warp will warp successfully even if the player is inside a dungeon (be sure a .quest login event won't still warp you out) +#define ENABLE_USE_COSTUME_ATTR // enable the items reset costume and enchant costume +#define ENABLE_ITEM_ATTR_COSTUME // enable costume_hair, costume_body, costume_weapon item_attr/_rare parts +#define ENABLE_SEQUENCE_SYSTEM // enable sequence system +#define MAP_ALLOW_LIMIT // define how many maps are allowed per game core (default 32) +#define ENABLE_PLAYER_PER_ACCOUNT5 // enable 5 characters (per account) in the select phase (instead of 4) +#define ENABLE_DICE_SYSTEM // enable dice system: if the mob is king or boss and you're in party, the dropped item is randomly rolled +#define ENABLE_EXTEND_INVEN_SYSTEM //enable 4 inventory pages +#define ENABLE_MOUNT_COSTUME_SYSTEM // enable mount costume slot +#define ENABLE_WEAPON_COSTUME_SYSTEM //enable weapon costume slot +#define ENABLE_PET_SYSTEM_EX // enable item pet without quest [@warme666] +#define ENABLE_SKILL_FLAG_PARTY //enable skill flag party (separated from lycan define) +#define ENABLE_NO_DSS_QUALIFICATION //disable dragon soul qualification [@warme666] +#define ENABLE_NO_SELL_PRICE_DIVIDED_BY_5 // disable dividing the sell price by 5 [@warme666] +#define ENABLE_CHECK_SELL_PRICE // it checks if the sell price is higher than the buy price; if yes, sell price will be equal to the buy price +#define ENABLE_GOTO_LAG_FIX // it force-clears the entities when switching sectree +#define ENABLE_MOUNT_COSTUME_EX_SYSTEM //mount load via item_proto with APPLY_MOUNT (118) bonus like official +#define ENABLE_PENDANT_SYSTEM // pendant equip implementation +#define ENABLE_GLOVE_SYSTEM // glove equip implementation +#define ENABLE_MOVE_CHANNEL // enable channel switch +#define ENABLE_REDUCED_ENTITY_VIEW // it reduces the complexity of the entity view +>>lycan +#define ENABLE_WOLFMAN_CHARACTER // enable wolfman character and the relative new features (claws, bleeding and so on) [@warme666] +#define DISABLE_WOLFMAN_ON_CREATE // disable wolfman on create phase [@warme666] +#define USE_MOB_BLEEDING_AS_POISON // if enabled, the mob_proto structure won't change and the bleeding % will be get from the poison field +#define USE_MOB_CLAW_AS_DAGGER // if enabled, the mob_proto structure won't change and the claw % will be get from the dagger field +#define USE_ITEM_BLEEDING_AS_POISON // if enabled, the poison reduce bonus can also reduce the bleeding damage as if it's bleeding reduce itself +#define USE_ITEM_CLAW_AS_DAGGER // if enabled, the resist dagger bonus can also reduce the claw damage as if it's resist claw itself +#define USE_WOLFMAN_STONES // if enabled, lycan stones can be dropped from the metin stones +#define USE_WOLFMAN_BOOKS // if enabled, lycan skill books can be dropped +>>magic reduction stone +#define ENABLE_MAGIC_REDUCTION_SYSTEM // enable resist magic reduction bonus +#define USE_MAGIC_REDUCTION_STONES // enable resist magic reduction stone drops from metins +>>pet system +#define __PET_SYSTEM__ // enable traditional pet system +#define USE_ACTIVE_PET_SEAL_EFFECT // it will activate the pet slot if it's active +#define USE_PET_SEAL_ON_LOGIN // it will automatically spawn the pet after a rewarp + +@/Src/Server/db/src/ClientManager.h +#define ENABLE_PROTO_FROM_DB //read protos from db if "PROTO_FROM_DB = 1" is specified inside conf.txt + //mirror protos to db if "MIRROR2DB = 1" is specified inside conf.txt + +@/Src/Server/db/src/ClientManager.cpp +#define ENABLE_DEFAULT_PRIV //enable default priv loading from common.priv_settings +#define ENABLE_ITEMAWARD_REFRESH //enable a select query every 5 seconds into player.item_award + +@/Src/Server/db/src/ClientManagerBoot.cpp +#define ENABLE_AUTODETECT_VNUMRANGE //if protos are loaded from db, it will automatically detect the vnum range for ds items + +@/Src/Server/db/src/ProtoReader.cpp +#define ENABLE_NUMERIC_FIELD //txt protos now can read numbers instead of tags as well + +@/Srcs/Server/game/src/stdafx.h +>moved to CommonDefines.h + +@/Srcs/Server/game/src/stdafx.h +#define ENABLE_LIMIT_TIME //enable game timestamp expiration + +@/Srcs/Server/game/src/guild.h +#define ENABLE_NEWGUILDMAKE //enable pc.make_guild0 and disable CInputMain::AnswerMakeGuild + +@/Srcs/Server/game/src/horse_rider.h +#define ENABLE_INFINITE_HORSE_HEALTH_STAMINA // full horse health and stamina all the times + +@/Srcs/Server/game/src/quest.h +#define ENABLE_QUEST_DIE_EVENT //add quest event "die" +#define ENABLE_QUEST_BOOT_EVENT //add quest event "boot" +#define ENABLE_QUEST_DND_EVENT //add quest event "dnd" + +@/Srcs/Server/game/src/config.cpp +#define ENABLE_CMD_PLAYER //enable PLAYER grade inside CMD +#define ENABLE_EXPTABLE_FROMDB //read the exp table from the db +#define ENABLE_AUTODETECT_INTERNAL_IP //autodetect internal ip if the public one is missing +#define ENABLE_GENERAL_CMD //if enabled, it reads a general CMD from "locale/%s/conf/GENERAL_CMD", "locale/%s/conf/GENERAL_CMD_CHANNEL_%d", and/or "locale/%s/conf/GENERAL_CMD_CHANNEL_%d_HOSTNAME_%s" +#define ENABLE_GENERAL_CONFIG //if enabled, it reads a general CONFIG from "locale/%s/conf/GENERAL_CONFIG", "locale/%s/conf/GENERAL_CONFIG_CHANNEL_%d", and/or "locale/%s/conf/GENERAL_CONFIG_CHANNEL_%d_HOSTNAME_%s" + //in the GENERAL_CONFIG, all the options are valid except: HOSTNAME, CHANNEL, PLAYER_SQL, COMMON_SQL, LOG_SQL, PORT, P2P_PORT, MAP_ALLOW, AUTH_SERVER, TEEN_ADDR, TEEN_PORT + +@/Srcs/Server/game/src/char_resist.cpp +#define ENABLE_IMMUNE_PERC //enable 90% of success instead of 100% regarding immunes (antistun/slow/fall) + +@/Srcs/Server/game/src/item.cpp +#define ENABLE_IMMUNE_FIX //fix immune bug where you need to equip shield at last (or refresh compute e.g. un/riding horse) + +@/Srcs/Server/game/src/item_manager.h +#define ENABLE_ITEM_PROTO_MAP //improves GetTable load by O(1) + +@/Srcs/Server/game/src/db.cpp +#define ENABLE_SPAMDB_REFRESH //enable a select query every 10 minutes into common.spam_db + +@/Srcs/Server/game/src/questlua.cpp +#define ENABLE_TRANSLATE_LUA //enable translate.lua loading +#define ENABLE_QUESTLIB_EXTRA_LUA //enable questlib_extra.lua loading + +@/Srcs/Server/game/src/questlua_dungeon.cpp +#define D_JOIN_AS_JUMP_PARTY //d.join will internally work as d.new_jump_party + +@/Srcs/Server/game/src/questlua_pc.cpp +#define ENABLE_LOCALECHECK_CHANGENAME //enable check that unable change name on Europe Locales +#define ENABLE_PC_OPENSHOP //enable pc.open_shop0(idshop) but buy/sell not work yet + +@/Srcs/Server/game/src/shop.cpp +#define ENABLE_SHOP_BLACKLIST //enable ignore 70024 (Blessing Marble) and 70035 (Magic Copper Ore) + +@/Srcs/Server/game/src/cmd.cpp +#define ENABLE_BLOCK_CMD_SHORTCUT //if enabled, people won't be able to shorten commands + +@/Srcs/Server/game/src/cmd_gm.cpp +#define ENABLE_STATPLUS_NOLIMIT //disable only 90 points for con+/int+/str+/dex+ commands +#define ENABLE_SET_STATE_WITH_TARGET // enable set_state target as 3rd arg +#define ENABLE_CMD_IPURGE_EX // /ipurge 2nd arg can remove items from a specific window (inv/equip/ds/belt/all) + +@/Srcs/Server/game/src/char_skill.cpp +#define ENABLE_FORCE2MASTERSKILL //skill always pass to m1 when b17 instead of b(number(17-20)) +#define ENABLE_NO_MOUNT_CHECK //check whether horse mount vnum should be checked when skilling +#define ENABLE_NULLIFYAFFECT_LIMIT //sura skill 66 won't nullify players with level < or > of yours by 9 +#define ENABLE_MASTER_SKILLBOOK_NO_STEPS //if enabled, you will only need a book to increase a master skill, and not as many as the level-20 + +@/Srcs/Server/game/src/char_item.cpp +#define ENABLE_FIREWORK_STUN //enable stun affect when using firework items +#define ENABLE_ADDSTONE_FAILURE //enable add stone failure +#define ENABLE_EFFECT_EXTRAPOT //enable extrapot effects when using green/purple potions +#define ENABLE_BOOKS_STACKFIX //enable stackable books instead of remove all the pile +#define ENABLE_ITEM_RARE_ATTR_LEVEL_PCT //enable 1-5 level pct for item rare attr add/change +#define ENABLE_UNIQUE_ITEM_AUTOSPLIT //enable autosplit when equipping stacked unique items + +@/Srcs/Server/game/src/char_battle.cpp +#define ENABLE_NEWEXP_CALCULATION //recalculate exp rate so you won't get random negative exp/marriage points +#define ENABLE_EFFECT_PENETRATE //enable penetrate effect when performing a penetration +#define ENABLE_NO_DAMAGE_QUEST_RUNNING //if enabled, no damage will be dealt to monsters while running quests (otherwise the quest kill event wouldn't be triggered) + +@/Srcs/Server/game/src/char.h +#define NEW_ICEDAMAGE_SYSTEM //add new system for nemere dungeon and so on +#define ENABLE_ANTI_CMD_FLOOD //limit player's command execution to 10 commands per second, otherwise it'll be disconnected! +#define ENABLE_OPEN_SHOP_WITH_ARMOR //if enabled, people can open a personal shop with the armor equipped +#define ENABLE_SKILL_COOLDOWN_CHECK //if enabled, it will add a check on the skill usage to prevent some damage hacks + +@/Srcs/Server/game/src/char.cpp +#define ENABLE_SHOWNPCLEVEL //show Lv %d level even for NPCs (not applicable on mob/stone/warp) +#define ENABLE_GOHOME_IF_MAP_NOT_ALLOWED //you'll go back to your village if you're not allowed to go in that map +#define ENABLE_GM_FLAG_IF_TEST_SERVER //show the gm flag if it's on test server mode +#define ENABLE_GM_FLAG_FOR_LOW_WIZARD //show the gm flag for low wizard too +#define ENABLE_MOUNT_ENTITY_REFRESH //it will refresh entities when riding/unriding (it's also a quick work-around when the client desyncs) + +@/Srcs/Server/game/src/input_db.cpp +#define ENABLE_GOHOME_IF_MAP_NOT_EXIST //you'll go back to your village if the map doesn't exist + +@/Srcs/Server/game/src/guild.cpp +#define ENABLE_GUILD_COMMENT_ANTIFLOOD //prevent flood in guild comments + +@/Srcs/Server/game/src/mining.cpp +#define ENABLE_PICKAXE_RENEWAL //if the upgrading of the pickaxe will fail, it won't turn back of 1 grade, but just lose 10% mastering points. + +@/Srcs/Server/game/src/fishing.cpp +#define ENABLE_FISHINGROD_RENEWAL //if the upgrading of the fishing rod will fail, it won't turn back of 1 grade, but just lose 10% mastering points. + +@/Srcs/Server/game/src/questmanager.cpp +#define ENABLE_PARTYKILL //re-enable PartyKill + +@/Srcs/Server/game/src/input_auth.cpp +#define ENABLE_ACCOUNT_W_SPECIALCHARS //enable special characters in account names (account.account.login) + +@/Srcs/Server/game/src/input_main.cpp +#define ENABLE_CHAT_COLOR_SYSTEM //enable chat colors based on IsGm or GetEmpire (+colored empire name) +#define ENABLE_CHAT_SPAMLIMIT //limit chat spam to 4 messages for 5 seconds, if you spam it for 10 times, you'll be disconnected! +#define ENABLE_WHISPER_CHAT_SPAMLIMIT //limit whisper chat to 10 messages per 5 seconds, otherwise you'll be disconnected! +#define ENABLE_CHAT_LOGGING //enable chat logging (which saves all the gm chats) +#define ENABLE_CHECK_GHOSTMODE //enable check that blocks the movements if the character is dead +#define ENABLE_TP_SPEED_CHECK //enable speed check teleport back + +@/Srcs/Server/game/src/input_login.cpp +#define USE_LYCAN_CREATE_POSITION // if enabled, the lycan will be warped to his own village at character creation + + +@/Src/Server/game/src/quest/qc.cc +#define ENABLE_WHEN_PARENTHESIS //it enables () [] usage in the when cause for the quest file + + +* Dependencies +@/Srcs/Extern/include/boost +@/Srcs/Extern/include/cryptopp +@/Srcs/Extern/include/IL +@/Srcs/Extern/lib: + libIL.a //from DevIL + libcryptopp.a //from cryptopp + libjasper.a + libpng.a + libtiff.a + libjbig.a + libmng.a + liblcms.a + libjpeg.a +@/usr/lib/liblzma.a //or lib32 + +* Database configuration +* How to run tests +* Deployment instructions + +### Contribution guidelines ### + +* Writing tests +* Code review +#@general +@warme001: be aware about PLAYER_MAX_LEVEL_CONST (common/length.h) and gPlayerMaxLevel (game/config.h) +@warme002: be aware about ITEM_MAX_COUNT (common/item_length.h) and g_bItemCountLimit (game/config.h) +@warme003: do_click_safebox can be used by PLAYER in every map! +@warme004: `when vnum.kill begin` and `when kill begin` are both triggered +@warme005: different locale stuff +@warme006: not implemented stuff from another locale +@warme007: on db/src/ClientManager.cpp; commented locale set from common.locale due to its uselessness and bugginess (./close && ./start) + it processes a NULL mysql connection (dat ymir threading) if there was a bit of overload before starting the process up again +@warme008: on char_item.cpp; now 27996 (poison bottle) can inflict poison +@warme009: on char_resist.cpp; if bleeding is used as poison, the bleeding enchantment is poison enchantment/50 (so mobs can bleed players) +@warme010: on char_state.cpp; test_server is used as "BOOL g_test_server" +@warme011: on dungeon.cpp; you should never use d.join instead of d.new_jump_party since it causes random crashes due to a wrong implementation of the party hash check +@warme012: trivial errors are now considered as simple logs +@warme013: unneccessary errors are now simply commented +@warme014: wrong lua return arguments number +@warme015: increased buffer size for prevention +@warme016: improved info in the relative print + +#@common +@fixme400: on tables.h; TPlayerTable hp/mp from short to int (hp/mp >32767 should be fixed) + +#@db/src +@fixme201: on ProtoReader.cpp; changed 'SAMLL' into 'SMALL' +@fixme202: on ClientManagerGuild.cpp; fixed the guild remove member time issue if the player was offline + (withdraw_time -> new_withdraw_time) +@fixme203: on ClientManagerPlayer.cpp; dandling pointer for "command" +@fixme204: on Cache.cpp; myshop_pricelist primary key duplication error if there are many items of the same vnum in the personal shop +@fixme205: on Cache.cpp, ClientManager.cpp; "replace into item" was deleting any foreign keys pointed to player.item + +#@game/src +@fixme101: on log.cpp; fixed '%s' for invalid_server_log +@fixme102: on cmd_general.cpp; inside ACMD(do_war) fixed the unsigned bug +@fixme103: on config, input_login, input_main.cpp; fixed clientcheckversion (version > date) to (version != date) and delay from 10 to 0 +@fixme104: on char.cpp, questlua_pc.cpp; fixed get status point after lv90 changing 90 with gPlayerMaxLevel +@fixme105: on cmd.cpp; disabled every korean command +@fixme106: on input_main.cpp; if a full-speeded player is on a mount (es. lion), he'll be brought back due to the distance range +@fixme107: on char_battle.cpp; if character (player|mob) has negative hp on dead, sura&co will absorb hp/mp losing 'em themselves +@fixme108: on char.cpp; if you change a mount but the previous is not 0, all the entities (npcs&co) in the player client + (not others) are vanished until another refresh (not exists mounts still bug you after second mount call) +@fixme109: on questmanager.cpp; if you kill a player (war m), `when kill begin` will be triggered twice +@fixme110: on char_affect.cpp; if you attack when semi-transparent (revived or ninja skill or white flag) you'll still be transparent +@fixme111: on test.cpp; ConvertAttribute2 has x and y inverted (before y->x after x->y) +@fixme112: on char_item.cpp; you can change bonuses in equipped items too (until re-login) + bonus values will not be refreshed by ChangePoint and unequipping it will remove back only the new bonuses set on + e.g. you had a 2500hp bonus shoes, you changed it to 50mp when equipped and you'll unequipped + what will it happen? instead of remove 2500hp, you won't receive 50mp and you also lose 50mp when unequipped +@fixme113: on char_item.cpp; same thing of #112 + you can remove stones from equipped items w/o losing bonuses + e.g. have an item with antiwar+4 equipped: + 1) remove all the stones 2) unequip it 3) re-add stone 4) re-equip it 5) repeat it thrice + result? an item with no stones but you'll have 75% of antiwar +@fixme114: on char_item.cpp; gathering of #111, #112 and few others. +@fixme115: on char_item.cpp; you can retrieve all the item on the ground if you're in a party and the owner is not in yours. +@fixme116: on char_skill.cpp; normal horse mount skills cannot inflict damage +@fixme117: on char_item.cpp; you can't swap equipment from inventory if full, and also prevent unmotivated belt swap if its inventory is not empty +@fixme118: on char.cpp; when ComputePoints is called: + you'll gain as many hp/mp as many you have in your equipment bonuses + affect hp/mp will be lost when login or updating +@fixme119: on input_main.cpp; you can put items from safebox/mall to belt inventory w/o checking the type (items with size>1 are not placeable anyway) +@fixme120: on input_login.cpp; few packet IDs not checked +@fixme121: on char_item.cpp; the refine scroll item value 1 from the magic stone was generating useless syserrs +@fixme122: on arena.cpp; few other potions were not checked on arena map +@fixme123: on char_item.cpp; USE_CHANGE_ATTRIBUTE2 (24) sub type check bug (the condition could never be true) +@fixme124: on char_item.cpp; no check on 6-7 add/change items about costume stuff +@fixme125: on char.cpp; dungeon regen pointing to a dangling pointer (not required -> removed) +@fixme126: on marriage.cpp; fix lovepoints overflow +@fixme127: on cube.cpp; /cube r_info exploit fix; it can cause a crash due to an unchecked cube npc masters vnums + e.g. 1) you open the Baek-Go cube's console 2) click on an npc/kill a mob without close the cube console + 3) digit /cube r_info 4) crash core +@fixme128: on char.cpp; mining hack fix; you can mine a vein anywhere in the map because there's no check on the character + which means, you can stay at 0x0y and mining a vein in 666x999y and get the stuff beside him or in the pc's inventory +@fixme129: on PetSystem.cpp; the azrael pets (53005->34004 normal/53006->34009 gold) don't give the buff if not in dungeon at summon up and remove them anyway when unsummoned +@fixme130: on messenger_manager.cpp; and cmd_general.cpp if you do /messenger_auth n XXX, the player with the name XXX will receive a "refused friend invite" print from you + which means, if you flood this packet, the "victim" will be disconnected or at maximum could get lag +@fixme131: on char.cpp; fix annoying sync packets sendable even on unfightable pc/npc entities + e.g. wallhack against players' shops inside the village's squares (where the NOPK attr is set) to move them out and kill them +@fixme132: on shop.cpp; if two people buy the same item at the same time from a pc's shop, the slower one will receive a wrong return packet (crash client) +@fixme133: on input_main.cpp; banword check and hyper text feature were processing the final chat string instead of the raw one +@fixme134: on questlua_pc.cpp; the pc.mount_bonus was addable even if the mount wasn't spawn (only /unmount pc.unmount can remove it) +@fixme135: on char.cpp; if the Sync is made before a move packet and the sectree differs of few x/y coordinates, the sectree will be changed without update (crash character) (troublesome -> removed) +@fixme136: on char.cpp; there are no checks about the zero division exception: e.g. if you set a mob's max hp to 0 in the mob proto, you'll get random crashes. +@fixme137: on char_battle.cpp; when a player dies, the HP could have a negative value. Now it's 0 like the official. +@fixme138: on db.cpp, input_auth.cpp; the account's password was shown in the mysql history queries as clear text at every login attempt (mysql full granted user required -> now hashed) +@fixme139: on shop.h; CShop class destructor wasn't virtual. If a derived class like CShopEx was deleted, a memory leak would have been generated. +@fixme140: on input_main.cpp; the belt could be put into the safebox even though the belt inventory isn't empty. +@fixme141: on char_item.cpp; the items in the belt inventory could be used even if their slot were not available +@fixme142: on messenger_manager.cpp; sql injection fix about net.SendMessengerRemovePacket +@fixme143: on guild_manager.cpp; sql injection fix about net.SendAnswerMakeGuildPacket +@fixme144: on sectree_manager.cpp; if map/index doesn't end with a newline, the game will crash (before refactory) +@fixme145: on input_main.cpp; guild_add_member can add any vid as guild's member even if it's a mob or an npc +@fixme147: on char_item.cpp; ramadan candy item can be used even if the relative affect is still up +@fixme148: on item_manager_read_tables.cpp; type quest, special, attr not handled in ConvSpecialDropItemFile +@fixme149: on char.cpp; refine material skip exploit if items are swapped +@fixme150: on exchange.cpp; char_item.cpp; prevent item module swapping if the quest is suspended +@fixme152: on questlua_pc.cpp; pc.get_special_ride_vnum was checking socket2 instead of socket0 +@fixme153: on threeway_war.cpp; kills made of people over lvl99 weren't counted +@fixme154: on cmd_gm.cpp; the /all_skill_master command will now set the right amount of points to the sub skills +@fixme156: on char_affect.cpp; prevent doubling the affect values before they are loaded (such as pc.mount_bonus at login; because the quest is loaded before the quests) +@fixme157: on OxEvent.cpp; the attender list wasn't cleared after stopping the ox event +@fixme158: on input_main.cpp; the deviltower refiner won't set the flag to 0 anymore if you have no money, and it will decrease it by 1 for allowing multiple refine attempts +@fixme159: on exchange.cpp; when exchanging, a wrong check in the ds items was not allowing the exchange due to "not enough space in ds inventory" if the first sub ds inventory slot was not empty +@fixme160: on DragonSoul.cpp; when removing a ds stone, if the destination slot wasn't empty, the ds item in there would have been replaced and lost +@fixme168: on questevent.cpp; if the quest info name is already null, the std::string constructor will throw an exception +@fixme169: on char_item.cpp; missing checks for mythical peach like a second vnum and nullptr +@fixme170: on item.cpp; missing condition on special mineral slots for just 2 bonuses +@fixme171: on dungeon.cpp; FCountMonster was also counting horses and pets +@fixme172: on dungeon.cpp; FNotice was sending ChatPackets to even non-player characters +@fixme173: on item.cpp; by pressing \\z to pick up items, sometimes, it would fail due to far distance +@fixme174: on questmanager.cpp; in case of quest error in server_timer, it would try to print the error to the current player causing a core crash +@fixme177: on cmd_gm.cpp; /priv_guild snprintf had an empty format +@fixme179: on ClientManagerBoot.cpp, ProtoReader.cpp; the item size with 0 length will cause game cores +@fixme180: on cmd_general.cpp; /costume will cause game core crashes if the relative costume bonus ids aren't present inside fn_string or have no %d +@fixme182: on input_login.cpp; you'd get no horse level if you relogged in with no job set +@fixme183: on input_main.cpp, messenger_manager.cpp; player rename wasn't clearing the previous nickname from the friendlist +@fixme184: on length.h; some slots of the dragon soul inventory weren't working properly +@fixme185: on ClientManagerBoot.cpp; the material count var isn't initialized and cause crashes on certain circumstances +@fixme186: on item.cpp; the ClearMountAttributeAndAffect without owner would trigger a core crash +@fixme187: on input.cpp; the input buffer will keep increasing until it saturates the ram if people keep sending broken packets +@fixme188: on char.h, questlua_npc|dungeon|global.cpp, dungeon.cpp; the scripted npc->Dead() was triggering player rewards if the mob was previously damaged +@fixme189: on item_manager.cpp; the Stones array had an out of range error +@fixme190: on input_login.cpp; exploit by accessing non-existing players +@fixme191: on safebox.cpp; switching safebox size level was causing a memory leak +@fixme192: on char_battle.cpp; the deathblow wasn't affecting warrior and wolfman races +@fixme193: on guild_manager.cpp; observer players can be killed to increment the guild war kills +@fixme194: on char_battle.cpp; mobs killed in non attackable zones weren't dropping yangs/items +@fixme195: on regen.cpp; missing 'ma' 'ra' 'sa' handling regen type +@fixme196: on char_item.cpp; possible item duplication bug while moving splitted items onto the same slot +@fixme197: on cmd_general.cpp; stucked connections while fully quitting from game would leave the character online +@fixme198: on char_battle.cpp; karma drop duplication bug: the dropped item gets duplicated after warping to another game core +@fixme199: on char.h, char.cpp, char_item.cpp, char_quickslot.cpp; the mobs were allocating unused memory +@fixme300: on cmd_gm.cpp; /full and /ipurge were causing a glitch in the status page by having negative status +@fixme301: on input_main.cpp, char.cpp; leaders and party members can leave the party while being on other channels +@fixme302: on questpc.cpp; HEADER_GC_QUEST_INFO had a wrong calculated size +@fixme303: on item_manager_read_tables.cpp; the load of items had O(n) complexity for each vnum of any Group +@fixme304: on char.h, char.cpp; yielded quest states were pointing to dangling item pointers +@fixme305: on char_skill.cpp; skill affects were not cleared after a skill reset +@fixme306: on char.cpp; real time items were not expired if inside the safebox +@fixme307: on cmd_general.cpp; crash core if the item for feeding the horse didn't exist in the item proto +@fixme308: on sectree_manager.cpp; when loading map/index, if the map is already loaded, the regen will duplicate +@fixme309: on questlua_pc.cpp; flushing the cache after changing the player name was causing item duplication +@fixme310: on ClientManager.cpp; only the first 3 players of the account had a full empire select check +@fixme311: on questmanager.cpp; CancelServerTimers not properly erasing the element from the container +@fixme312: on char_skill.cpp; mobs could ran away on death instead of playing the death animation +@fixme313: on char.cpp; very fast mounts would reach coordinates like -2147483648 and being unable to sync +@fixme314: on char_item.cpp; unique items can now be be stackable without issues +@fixme315: on building.cpp; skip guild land load from other maps +@fixme316: on char_item.cpp; items with sockets set at runtime weren't automatically stacked properly + + +#@/Server (general) +@fixme401: fixed the guild disband time issue + on db/src/ClientManagerGuild.cpp + (withdraw_time -> new_disband_time) + on game/src/guild.cpp + (new_withdraw_time -> new_disband_time) +@fixme402: fixed the usage of the affect system before its loading + on game/src/char_item.cpp + added an IsAffectLoaded check when using an item +@fixme403: fixed player.myshop_pricelist corrupted data + on db/src/ClientManager.cpp; db/src/ClientManager.h; game/src/char.cpp + TPacketMyshopPricelistHeader to TItemPriceListTable + +#@/General +@fixme501: on game/src/char.h, game/src/char.cpp, game/src/packet.h; mob race word to dword +@fixme502: on common/tables.h, game/src/packet.h; character part word to dword + + +* Other guidelines + +## Trick to replace ALUA macro (notepad++) for all questlua*.cpp files (CTRL+F) +# Find (Search Type -> Regular Expression) +int[ \t]*([A-Za-z0-9\_]+)[ \t]*\([ \t]*lua_State[ \t]*\*[ \t]*[\/\*]*L[\*\/]*[ \t]*\) +# Replace with (File -> Save all) [26 files, 621 replaces] +ALUA\($1\) + +### Who do I talk to? ### + +* Repo owner or admin +martysama0134 diff --git a/source-server/Srcs/Server/Makefile b/source-server/Srcs/Server/Makefile new file mode 100644 index 000000000..ba7882b08 --- /dev/null +++ b/source-server/Srcs/Server/Makefile @@ -0,0 +1,149 @@ +CC = clang +CXX = clang++ + +PLATFORM = $(shell file /bin/ls | cut -d' ' -f3 | cut -d'-' -f1) +BSD_VERSION = $(shell uname -v 2>&1 | cut -d' ' -f2 | cut -d'.' -f1) +SVR_VERSION = $(shell cat __REVISION__) + +.PHONY: liblua libsql libgame libpoly libthecore game db + +default: liblua libsql libgame libpoly libthecore game db + @echo "--------------------------------------" + @echo "Build Done" + @echo "--------------------------------------" + +quick: . + $(MAKE) -C liblua/5.0 + $(MAKE) -C libsql + $(MAKE) -C libgame/src + $(MAKE) -C libpoly + $(MAKE) -C libthecore/src + touch game/src/main.cpp + $(MAKE) -C game/src + $(MAKE) -C game/src symlink + touch db/src/Main.cpp + $(MAKE) -C db/src + $(MAKE) -C db/src symlink + +liblua: . + $(MAKE) -C $@/5.0 clean + $(MAKE) -C $@/5.0 + +libsql: . + @touch $@/Depend + $(MAKE) -C $@ dep + $(MAKE) -C $@ clean + $(MAKE) -C $@ + +libgame: . + @touch $@/src/Depend + $(MAKE) -C $@/src dep + $(MAKE) -C $@/src clean + $(MAKE) -C $@/src + +libpoly: . + @touch $@/Depend + $(MAKE) -C $@ dep + $(MAKE) -C $@ clean + $(MAKE) -C $@ + +libthecore: . + @touch $@/src/Depend + $(MAKE) -C $@/src dep + $(MAKE) -C $@/src clean + $(MAKE) -C $@/src + +game: . + @touch $@/src/Depend + $(MAKE) -C $@/src dep + $(MAKE) -C $@/src clean + # $(MAKE) -C $@/src limit_time + $(MAKE) -C $@/src + $(MAKE) -C $@/src symlink + +db: . + @touch $@/src/Depend + $(MAKE) -C $@/src dep + $(MAKE) -C $@/src clean + $(MAKE) -C $@/src + $(MAKE) -C $@/src symlink + +ver: + @$(CC) -v + +ver2: + @$(CC) -v + $(MAKE) -C game/src ver + +symlink: + $(MAKE) -C game/src symlink + $(MAKE) -C db/src symlink + +strip: + $(MAKE) -C game/src strip + $(MAKE) -C db/src strip + +clean: + $(MAKE) -C liblua/5.0 clean + $(MAKE) -C libsql clean + $(MAKE) -C libgame/src clean + $(MAKE) -C libpoly clean + $(MAKE) -C libthecore/src clean + $(MAKE) -C game/src clean + $(MAKE) -C db/src clean + +all: + @echo "--------------------------------------" + @echo "Update Revision" + @echo "--------------------------------------" + @expr $(SVR_VERSION) + 1 > __REVISION__ + @cat __REVISION__ + + @echo "--------------------------------------" + @echo "Full Build Start" + @echo "--------------------------------------" + + $(MAKE) -C liblua/5.0 clean + $(MAKE) -C liblua/5.0 + + # $(MAKE) -C liblua/5.2 clean + # $(MAKE) -C liblua/5.2 freebsd + # $(MAKE) -C liblua/5.2 local + + @touch libsql/Depend + $(MAKE) -C libsql dep + $(MAKE) -C libsql clean + $(MAKE) -C libsql + + @touch libgame/src/Depend + $(MAKE) -C libgame/src dep + $(MAKE) -C libgame/src clean + $(MAKE) -C libgame/src + + @touch libpoly/Depend + $(MAKE) -C libpoly dep + $(MAKE) -C libpoly clean + $(MAKE) -C libpoly + + @touch libthecore/src/Depend + $(MAKE) -C libthecore/src dep + $(MAKE) -C libthecore/src clean + $(MAKE) -C libthecore/src + + @touch game/src/Depend + $(MAKE) -C game/src dep + $(MAKE) -C game/src clean + # $(MAKE) -C game/src limit_time + $(MAKE) -C game/src + $(MAKE) -C game/src symlink + # $(MAKE) -C game/src strip + + @touch db/src/Depend + $(MAKE) -C db/src dep + $(MAKE) -C db/src clean + $(MAKE) -C db/src + $(MAKE) -C db/src symlink + # $(MAKE) -C db/src strip + @echo "--------------------------------------" + @echo "Full Build End" + @echo "--------------------------------------" diff --git a/source-server/Srcs/Server/__REVISION__ b/source-server/Srcs/Server/__REVISION__ new file mode 100644 index 000000000..4a623ea51 --- /dev/null +++ b/source-server/Srcs/Server/__REVISION__ @@ -0,0 +1 @@ +41023 diff --git a/source-server/Srcs/Server/common/CommonDefines.h b/source-server/Srcs/Server/common/CommonDefines.h new file mode 100644 index 000000000..f0931096e --- /dev/null +++ b/source-server/Srcs/Server/common/CommonDefines.h @@ -0,0 +1,105 @@ +#ifndef __INC_METIN2_COMMON_DEFINES_H__ +#define __INC_METIN2_COMMON_DEFINES_H__ +#pragma once +////////////////////////////////////////////////////////////////////////// +// ### Standard Features ### +#define _IMPROVED_PACKET_ENCRYPTION_ +#ifdef _IMPROVED_PACKET_ENCRYPTION_ +#define USE_IMPROVED_PACKET_DECRYPTED_BUFFER +#endif +#define __UDP_BLOCK__ +//#define ENABLE_QUEST_CATEGORY + +// ### END Standard Features ### +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// ### New Features ### +#define ENABLE_NO_MOUNT_CHECK +#define ENABLE_D_NJGUILD +#define ENABLE_FULL_NOTICE +#define ENABLE_NEWSTUFF +#define ENABLE_PORT_SECURITY +#define ENABLE_BELT_INVENTORY_EX +#define ENABLE_CMD_WARP_IN_DUNGEON +// #define ENABLE_ITEM_ATTR_COSTUME +// #define ENABLE_SEQUENCE_SYSTEM +#define ENABLE_PLAYER_PER_ACCOUNT5 +#define ENABLE_DICE_SYSTEM +#define ENABLE_EXTEND_INVEN_SYSTEM +#define ENABLE_MOUNT_COSTUME_SYSTEM +#define ENABLE_WEAPON_COSTUME_SYSTEM +#define ENABLE_QUEST_DIE_EVENT +#define ENABLE_QUEST_BOOT_EVENT +#define ENABLE_QUEST_DND_EVENT +#define ENABLE_PET_SYSTEM_EX +#define ENABLE_SKILL_FLAG_PARTY +#define ENABLE_NO_DSS_QUALIFICATION +// #define ENABLE_NO_SELL_PRICE_DIVIDED_BY_5 +#define ENABLE_CHECK_SELL_PRICE +#define ENABLE_GOTO_LAG_FIX +#define ENABLE_MOUNT_COSTUME_EX_SYSTEM +#define ENABLE_PENDANT_SYSTEM +#define ENABLE_GLOVE_SYSTEM +#define ENABLE_MOVE_CHANNEL +#define ENABLE_QUIVER_SYSTEM +#define ENABLE_REDUCED_ENTITY_VIEW +#define ENABLE_GUILD_TOKEN_AUTH +#define ENABLE_DS_GRADE_MYTH +#define ENABLE_DB_SQL_LOG + +#define __PET_SYSTEM__ +#ifdef __PET_SYSTEM__ +#define USE_ACTIVE_PET_SEAL_EFFECT +#define PET_SEAL_ACTIVE_SOCKET_IDX 2 +#define USE_PET_SEAL_ON_LOGIN +#endif + +enum eCommonDefines { + MAP_ALLOW_LIMIT = 32, // 32 default +}; + +#define ENABLE_WOLFMAN_CHARACTER +#ifdef ENABLE_WOLFMAN_CHARACTER +// #define DISABLE_WOLFMAN_ON_CREATE +#define USE_MOB_BLEEDING_AS_POISON +#define USE_MOB_CLAW_AS_DAGGER +// #define USE_ITEM_BLEEDING_AS_POISON +// #define USE_ITEM_CLAW_AS_DAGGER +#define USE_WOLFMAN_STONES +#define USE_WOLFMAN_BOOKS +#endif + +// #define ENABLE_MAGIC_REDUCTION_SYSTEM +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM +// #define USE_MAGIC_REDUCTION_STONES +#endif + +// ### END New Features ### +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// ### Ex Features ### +#define DISABLE_STOP_RIDING_WHEN_DIE // if DISABLE_TOP_RIDING_WHEN_DIE is defined, the player doesn't lose the horse after dying +#define ENABLE_ACCE_COSTUME_SYSTEM //fixed version +// #define USE_ACCE_ABSORB_WITH_NO_NEGATIVE_BONUS //enable only positive bonus in acce absorb +#define ENABLE_HIGHLIGHT_NEW_ITEM //if you want to see highlighted a new item when dropped or when exchanged +#define ENABLE_KILL_EVENT_FIX //if you want to fix the 0 exp problem about the when kill lua event (recommended) +// #define ENABLE_SYSLOG_PACKET_SENT // debug purposes + +#define ENABLE_EXTEND_ITEM_AWARD //slight adjustement +#ifdef ENABLE_EXTEND_ITEM_AWARD + // #define USE_ITEM_AWARD_CHECK_ATTRIBUTES //it prevents bonuses higher than item_attr lvl1-lvl5 min-max range limit +#endif + +#define ENABLE_CHEQUE_SYSTEM +#ifdef ENABLE_CHEQUE_SYSTEM +#define ENABLE_SHOP_USE_CHEQUE +#define DISABLE_CHEQUE_DROP +#define ENABLE_WON_EXCHANGE_WINDOW +#endif +// ### END Ex Features ### +////////////////////////////////////////////////////////////////////////// + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/PulseManager.h b/source-server/Srcs/Server/common/PulseManager.h new file mode 100644 index 000000000..5d647d41c --- /dev/null +++ b/source-server/Srcs/Server/common/PulseManager.h @@ -0,0 +1,266 @@ +// martysama0134's PulseManager class for quick anti-flood code implementation +#ifndef __COMMON__PULSEMANAGER_H__ +#define __COMMON__PULSEMANAGER_H__ +#pragma once + +#include +#include +#include +#include + +enum class ePulse { + GuildComment, + CommandRequest, + ItemDrop, + BoxOpening, + BuySell, + SafeboxMove, + ChannelStatus, + RideMount, + SharedRequest, +}; + +// #define __PULSEMANAGER__SECOND_SUPPORT__ +#define __PULSEMANAGER__CLOCK_SUPPORT__ +// #define __PULSEMANAGER__M2_SUPPORT__ + +#ifdef __PULSEMANAGER__M2_SUPPORT__ +#include "../libthecore/include/stdafx.h" +#endif + +#define PULSEMANAGER_CLOCK_TO_SEC(diff) (std::chrono::duration_cast(diff).count()/1000.0) +#define PULSEMANAGER_CLOCK_TO_SEC2(key1, key2) (PULSEMANAGER_CLOCK_TO_SEC(PulseManager::Instance().DiffClock(key1, key2))) + +class PulseManager +{ +public: + using SubKeyT = ePulse; + using MainKeyT = uint32_t; + +#ifdef __PULSEMANAGER__M2_SUPPORT__ + using PulseT = int; + using PulseMapT = std::unordered_map; + using MainPulseMapT = std::unordered_map; + PulseT passesPerSec{ 25 }; + MainPulseMapT pulseMap; +#endif + +#ifdef __PULSEMANAGER__SECOND_SUPPORT__ + using SecondT = time_t;//uint32_t; + using SecondMapT = std::unordered_map; + using MainSecondMapT = std::unordered_map; + MainSecondMapT secondMap; +#endif + +#ifdef __PULSEMANAGER__CLOCK_SUPPORT__ + using TypeClock = std::chrono::high_resolution_clock; + using ClockT = TypeClock::time_point; + using DurationT = TypeClock::duration; + using CountT = int64_t; + using PairValueT = std::pair; + using ClockMapT = std::unordered_map; + using MainClockMapT = std::unordered_map; + MainClockMapT clockMap; +#endif + + static PulseManager& Instance() { + thread_local PulseManager _this; + return _this; + } + +#ifdef __PULSEMANAGER__SECOND_SUPPORT__ + inline static SecondT GetTime() { + return time(0); //get_global_time();//get_dword_time(); + } + + /* SECOND BLOCK */ + SecondT GetSecond(MainKeyT key1, SubKeyT key2) { + auto it1 = secondMap.find(key1); + if (it1 == secondMap.end()) + return 0; + auto it2 = it1->second.find(key2); + if (it2 == it1->second.end()) + return 0; + + return it2->second; + } + + void SetSecond(MainKeyT key1, SubKeyT key2, SecondT value, bool appendCurrent = true) { + if (appendCurrent) + value += GetTime(); + secondMap[key1][key2] = value; + } + + bool CheckSecond(MainKeyT key1, SubKeyT key2, SecondT nextLapse) { + if (GetSecond(key1, key2) > GetTime()) + return false; + SetSecond(key1, key2, nextLapse, true); + return true; + } +#endif + +#ifdef __PULSEMANAGER__CLOCK_SUPPORT__ + inline static DurationT GetChrono() { + return std::chrono::high_resolution_clock::now().time_since_epoch(); + } + + /* CLOCK BLOCK */ + PairValueT GetPair(MainKeyT key1, SubKeyT key2) const { + const auto it1 = clockMap.find(key1); + if (it1 == clockMap.end()) + return {TypeClock::duration::zero(), 0}; + + const auto it2 = it1->second.find(key2); + if (it2 == it1->second.end()) + return {TypeClock::duration::zero(), 0}; + + return it2->second; + } + + DurationT GetClock(MainKeyT key1, SubKeyT key2) const { + return GetPair(key1, key2).first; + } + + CountT GetCount(MainKeyT key1, SubKeyT key2) const { + return GetPair(key1, key2).second; + } + + void SetPair(MainKeyT key1, SubKeyT key2, PairValueT pair, bool appendCurrent = true) { + if (appendCurrent) + pair.first += GetChrono(); + clockMap[key1][key2] = pair; + } + + void SetClock(MainKeyT key1, SubKeyT key2, DurationT value, bool appendCurrent = true) { + if (appendCurrent) + value += GetChrono(); + clockMap[key1][key2].first = value; + } + + void SetCount(MainKeyT key1, SubKeyT key2, CountT count, bool decreaseValue = true) { + if (decreaseValue) + clockMap[key1][key2].second -= count; + else + clockMap[key1][key2].second = count; + } + + bool CheckClock(MainKeyT key1, SubKeyT key2) { + if (GetClock(key1, key2) > GetChrono()) + return false; + return true; + } + + bool IncreaseClock(MainKeyT key1, SubKeyT key2, DurationT nextLapse) { + if (!CheckClock(key1, key2)) + return false; + SetClock(key1, key2, nextLapse, true); + return true; + } + + bool CheckCount(MainKeyT key1, SubKeyT key2) { + //std::cout << "CHECK COUNT " << GetCount(key1, key2) << std::endl; + SetCount(key1, key2, 1, true); + if (GetCount(key1, key2) <= 0) + return false; + return true; + } + + bool IncreaseCount(MainKeyT key1, SubKeyT key2, DurationT nextLapse, CountT maxCount) { + if (!CheckClock(key1, key2)) { // if clock fails, check if max count is reached + if (!CheckCount(key1, key2)) + return false; + return true; + } + SetPair(key1, key2, { nextLapse, maxCount }, true); + return true; + } + + DurationT DiffClock(MainKeyT key1, SubKeyT key2) { + return GetClock(key1, key2) - GetChrono(); + } + + void ClearClock(MainKeyT key1) { + auto it1 = clockMap.find(key1); + if (it1 == clockMap.end()) + return; + clockMap.erase(it1); + } + + void ClearClock(MainKeyT key1, SubKeyT key2) { + auto it1 = clockMap.find(key1); + if (it1 == clockMap.end()) + return; + auto it2 = it1->second.find(key2); + if (it2 == it1->second.end()) + return; + it1->second.erase(it2); + } +#endif + +#ifdef __PULSEMANAGER__M2_SUPPORT__ + void SetPassesPerSec(PulseT v) { + passesPerSec = v; + } + + PulseT Sec2Pulse(SecondT v) { + return v * passesPerSec; + } + + SecondT Pulse2Sec(PulseT v) { + return v / passesPerSec; + } + + inline static PulseT GetCorePulse() { + return thecore_pulse(); + } + + /* CLOCK BLOCK */ + bool HasPulse(MainKeyT key1, SubKeyT key2) { + auto it1 = pulseMap.find(key1); + if (it1 == pulseMap.end()) + return false; + auto it2 = it1->second.find(key2); + if (it2 == it1->second.end()) + return false; + return true; + } + + PulseT GetPulse(MainKeyT key1, SubKeyT key2) { + auto it1 = pulseMap.find(key1); + if (it1 == pulseMap.end()) + return 0; + auto it2 = it1->second.find(key2); + if (it2 == it1->second.end()) + return 0; + return it2->second; + } + + void SetPulse(MainKeyT key1, SubKeyT key2, PulseT value, bool appendCurrent = true) { + if (appendCurrent) + value += GetCorePulse(); + pulseMap[key1][key2] = value; + } + + bool CheckPulse(MainKeyT key1, SubKeyT key2, PulseT nextLapse) { + if (GetPulse(key1, key2) > GetCorePulse()) + return false; + SetPulse(key1, key2, nextLapse, true); + return true; + } + + SecondT GetPSecond(MainKeyT key1, SubKeyT key2) { + auto v = GetPulse(key1, key2); + return Pulse2Sec(v); + } + + void SetPSecond(MainKeyT key1, SubKeyT key2, SecondT value, bool appendCurrent = true) { + auto pulses = Sec2Pulse(value); + // if (appendCurrent) + // pulses += GetTime(); + SetPulse(key1, key2, pulses, true); + } +#endif + +}; +#endif // __COMMON__PULSEMANAGER_H__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/VnumHelper.h b/source-server/Srcs/Server/common/VnumHelper.h new file mode 100644 index 000000000..e2379710b --- /dev/null +++ b/source-server/Srcs/Server/common/VnumHelper.h @@ -0,0 +1,38 @@ +#ifndef __HEADER_VNUM_HELPER__ +#define __HEADER_VNUM_HELPER__ + +class CItemVnumHelper +{ +public: + + static const bool IsPhoenix(DWORD vnum) { return 53001 == vnum; } + + static const bool IsRamadanMoonRing(DWORD vnum) { return 71135 == vnum; } + + static const bool IsHalloweenCandy(DWORD vnum) { return 71136 == vnum; } + + static const bool IsHappinessRing(DWORD vnum) { return 71143 == vnum; } + + static const bool IsLovePendant(DWORD vnum) { return 71145 == vnum; } +}; + +class CMobVnumHelper +{ +public: + + static bool IsPhoenix(DWORD vnum) { return 34001 == vnum; } + static bool IsIcePhoenix(DWORD vnum) { return 34003 == vnum; } + + static bool IsPetUsingPetSystem(DWORD vnum) { return (IsPhoenix(vnum) || IsReindeerYoung(vnum)) || IsIcePhoenix(vnum); } + + static bool IsReindeerYoung(DWORD vnum) { return 34002 == vnum; } + + static bool IsRamadanBlackHorse(DWORD vnum) { return 20119 == vnum || 20219 == vnum || 22022 == vnum; } +}; + +class CVnumHelper +{ +}; + +#endif //__HEADER_VNUM_HELPER__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/billing.h b/source-server/Srcs/Server/common/billing.h new file mode 100644 index 000000000..39ca7dad6 --- /dev/null +++ b/source-server/Srcs/Server/common/billing.h @@ -0,0 +1,16 @@ +#ifndef __INC_METIN_II_COMMON_BILLING_H__ +#define __INC_METIN_II_COMMON_BILLING_H__ + +enum EBillingTypes +{ + BILLING_NONE, + BILLING_IP_FREE, + BILLING_FREE, + BILLING_IP_TIME, + BILLING_IP_DAY, + BILLING_TIME, + BILLING_DAY, +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/building.h b/source-server/Srcs/Server/common/building.h new file mode 100644 index 000000000..da6c5e2dc --- /dev/null +++ b/source-server/Srcs/Server/common/building.h @@ -0,0 +1,64 @@ +#ifndef __METIN_II_COMMON_BUILDING_H__ +#define __METIN_II_COMMON_BUILDING_H__ + +namespace building +{ + enum + { + OBJECT_MATERIAL_MAX_NUM = 5, + }; + + typedef struct SLand + { + DWORD dwID; + long lMapIndex; + long x, y; + long width, height; + DWORD dwGuildID; + BYTE bGuildLevelLimit; + DWORD dwPrice; + } TLand; + + typedef struct SObjectMaterial + { + DWORD dwItemVnum; + DWORD dwCount; + } TObjectMaterial; + + typedef struct SObjectProto + { + DWORD dwVnum; + DWORD dwPrice; + + TObjectMaterial kMaterials[OBJECT_MATERIAL_MAX_NUM]; + + DWORD dwUpgradeVnum; + DWORD dwUpgradeLimitTime; + long lLife; + long lRegion[4]; + + DWORD dwNPCVnum; + long lNPCX; + long lNPCY; + + DWORD dwGroupVnum; + DWORD dwDependOnGroupVnum; + } TObjectProto; + + typedef struct SObject + { + DWORD dwID; + DWORD dwLandID; + DWORD dwVnum; + long lMapIndex; + long x, y; + + float xRot; + float yRot; + float zRot; + long lLife; + } TObject; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/cache.h b/source-server/Srcs/Server/common/cache.h new file mode 100644 index 000000000..9d46a0aa5 --- /dev/null +++ b/source-server/Srcs/Server/common/cache.h @@ -0,0 +1,69 @@ +#ifndef __INC_COMMON_CACHE_H__ +#define __INC_COMMON_CACHE_H__ + +template class cache +{ + public: + cache() + : m_bNeedQuery(false), m_expireTime(600), m_lastUpdateTime(0) + { + m_lastFlushTime = time(0); + + memset( &m_data, 0, sizeof(m_data) ); + } + + T * Get(bool bUpdateTime = true) + { + if (bUpdateTime) + m_lastUpdateTime = time(0); + + return &m_data; + } + + void Put(T * pNew, bool bSkipQuery = false) + { + thecore_memcpy(&m_data, pNew, sizeof(T)); + m_lastUpdateTime = time(0); + + if (!bSkipQuery) + m_bNeedQuery = true; + } + + bool CheckFlushTimeout() + { + if (m_bNeedQuery && time(0) - m_lastFlushTime > m_expireTime) + return true; + + return false; + } + + bool CheckTimeout() + { + if (time(0) - m_lastUpdateTime > m_expireTime) + return true; + + return false; + } + + void Flush() + { + if (!m_bNeedQuery) + return; + + OnFlush(); + m_bNeedQuery = false; + m_lastFlushTime = time(0); + } + + virtual void OnFlush() = 0; + + protected: + T m_data; + bool m_bNeedQuery; + time_t m_expireTime; + time_t m_lastUpdateTime; + time_t m_lastFlushTime; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/d3dtype.h b/source-server/Srcs/Server/common/d3dtype.h new file mode 100644 index 000000000..fc022cbf0 --- /dev/null +++ b/source-server/Srcs/Server/common/d3dtype.h @@ -0,0 +1,40 @@ +#ifndef __INC_METIN_II_D3DTYPE_H__ +#define __INC_METIN_II_D3DTYPE_H__ + +typedef struct D3DXVECTOR2 +{ + float x, y; +} D3DXVECTOR2, *LPD3DXVECTOR2; + +typedef struct D3DXVECTOR3 +{ + float x, y, z; +} D3DXVECTOR3, *LPD3DXVECTOR3; + +typedef struct D3DXVECTOR4 +{ + float x, y, z, w; +} D3DXVECTOR4, *LPD3DXVECTOR4; + +typedef struct D3DXQUATERNION +{ + float x, y, z, w; +} D3DXQUATERNION, *LPD3DXQUATERNION; + +typedef struct D3DXCOLOR +{ + float r, g, b, a; +} D3DXCOLOR, *LPD3DXCOLOR; + +typedef struct _D3DCOLORVALUE +{ + float r; + float g; + float b; + float a; +} D3DCOLORVALUE; + +typedef D3DXVECTOR3 D3DVECTOR; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/item_length.h b/source-server/Srcs/Server/common/item_length.h new file mode 100644 index 000000000..7d7fab6cc --- /dev/null +++ b/source-server/Srcs/Server/common/item_length.h @@ -0,0 +1,447 @@ +#ifndef __INC_METIN2_ITEM_LENGTH_H__ +#define __INC_METIN2_ITEM_LENGTH_H__ + +#include "CommonDefines.h" + +enum EItemMisc +{ + ITEM_NAME_MAX_LEN = 24, + ITEM_VALUES_MAX_NUM = 6, + ITEM_SMALL_DESCR_MAX_LEN = 256, + ITEM_LIMIT_MAX_NUM = 2, + ITEM_APPLY_MAX_NUM = 3, + ITEM_SOCKET_MAX_NUM = 3, + ITEM_MAX_COUNT = 200, + + ITEM_ATTRIBUTE_NORM_NUM = 5, + ITEM_ATTRIBUTE_RARE_NUM = 2, + + ITEM_ATTRIBUTE_NORM_START = 0, + ITEM_ATTRIBUTE_NORM_END = ITEM_ATTRIBUTE_NORM_START + ITEM_ATTRIBUTE_NORM_NUM, + + ITEM_ATTRIBUTE_RARE_START = ITEM_ATTRIBUTE_NORM_END, + ITEM_ATTRIBUTE_RARE_END = ITEM_ATTRIBUTE_RARE_START + ITEM_ATTRIBUTE_RARE_NUM, + + ITEM_ATTRIBUTE_MAX_NUM = ITEM_ATTRIBUTE_RARE_END, // 7 + ITEM_ATTRIBUTE_MAX_LEVEL = 5, + ITEM_AWARD_WHY_MAX_LEN = 50, + + REFINE_MATERIAL_MAX_NUM = 5, + + ITEM_ELK_VNUM = 50026, +}; + +const BYTE ITEM_SOCKET_REMAIN_SEC = 0; +enum EItemValueIdice +{ + ITEM_VALUE_DRAGON_SOUL_POLL_OUT_BONUS_IDX = 0, + ITEM_VALUE_CHARGING_AMOUNT_IDX = 0, + ITEM_VALUE_SECONDARY_COIN_UNIT_IDX = 0, +}; +enum EItemDragonSoulSockets +{ + ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX = 2, + ITEM_SOCKET_CHARGING_AMOUNT_IDX = 2, +}; + +enum EItemUniqueSockets +{ + ITEM_SOCKET_UNIQUE_SAVE_TIME = ITEM_SOCKET_MAX_NUM - 2, + ITEM_SOCKET_UNIQUE_REMAIN_TIME = ITEM_SOCKET_MAX_NUM - 1 +}; + +enum EItemTypes +{ + ITEM_NONE, //0 + ITEM_WEAPON, + ITEM_ARMOR, + ITEM_USE, + ITEM_AUTOUSE, //4 + ITEM_MATERIAL, //5 + ITEM_SPECIAL, + ITEM_TOOL, //7 + ITEM_LOTTERY, + ITEM_ELK, + ITEM_METIN, //10 + ITEM_CONTAINER, //11 + ITEM_FISH, + ITEM_ROD, //13 + ITEM_RESOURCE, //14 + ITEM_CAMPFIRE, //15 + ITEM_UNIQUE, //16 + ITEM_SKILLBOOK, //17 + ITEM_QUEST, //18 + ITEM_POLYMORPH, //19 + ITEM_TREASURE_BOX, + ITEM_TREASURE_KEY, + ITEM_SKILLFORGET, //22 + ITEM_GIFTBOX, //23 + ITEM_PICK, //24 + ITEM_HAIR, + ITEM_TOTEM, + ITEM_BLEND, + ITEM_COSTUME, + ITEM_DS, + ITEM_SPECIAL_DS, + ITEM_EXTRACT, + ITEM_SECONDARY_COIN, + ITEM_RING, + ITEM_BELT, + ITEM_PET, +}; + +enum EMetinSubTypes +{ + METIN_NORMAL, + METIN_GOLD, +}; + +enum EWeaponSubTypes +{ + WEAPON_SWORD, + WEAPON_DAGGER, + WEAPON_BOW, + WEAPON_TWO_HANDED, + WEAPON_BELL, + WEAPON_FAN, + WEAPON_ARROW, + WEAPON_MOUNT_SPEAR, +#ifdef ENABLE_WOLFMAN_CHARACTER + WEAPON_CLAW = 8, +#endif +#ifdef ENABLE_QUIVER_SYSTEM + WEAPON_QUIVER = 9, +#endif + WEAPON_NUM_TYPES, +}; + +enum EArmorSubTypes +{ + ARMOR_BODY, + ARMOR_HEAD, + ARMOR_SHIELD, + ARMOR_WRIST, + ARMOR_FOOTS, + ARMOR_NECK, + ARMOR_EAR, + ARMOR_PENDANT, //7 + ARMOR_GLOVE, //8 + ARMOR_NUM_TYPES +}; + +enum ECostumeSubTypes +{ + COSTUME_BODY = ARMOR_BODY, + COSTUME_HAIR = ARMOR_HEAD, +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + COSTUME_MOUNT = 2, +#endif +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + COSTUME_ACCE = 3, +#endif +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + COSTUME_WEAPON = 4, +#endif + COSTUME_NUM_TYPES, +}; + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM +enum EAcceInfo +{ + ACCE_GRADE_VALUE_FIELD = 0, + ACCE_ABSORPTION_SOCKET = 0, + ACCE_ABSORBED_SOCKET = 1, + ACCE_GRADE_1_ABS = 1, + ACCE_GRADE_2_ABS = 5, + ACCE_GRADE_3_ABS = 10, + ACCE_GRADE_4_ABS_MIN = 11, + ACCE_GRADE_4_ABS_MAX = 25, + ACCE_GRADE_4_ABS_MAX_COMB = 19, + ACCE_GRADE_4_ABS_RANGE = 5, + ACCE_EFFECT_FROM_ABS = 19, + ACCE_CLEAN_ATTR_VALUE0 = 7, + ACCE_WINDOW_MAX_MATERIALS = 2, + ACCE_GRADE_1_PRICE = 100000, + ACCE_GRADE_2_PRICE = 200000, + ACCE_GRADE_3_PRICE = 300000, + ACCE_GRADE_4_PRICE = 500000, + ACCE_COMBINE_GRADE_1 = 80, + ACCE_COMBINE_GRADE_2 = 70, + ACCE_COMBINE_GRADE_3 = 50, + ACCE_COMBINE_GRADE_4 = 30, + ACCE_REVERSAL_VNUM_1 = 39046, + ACCE_REVERSAL_VNUM_2 = 90000, + ACCE_BASE_VNUM = 85000, + ACCE_EFFECT_VNUM = 500, +}; +#endif + +enum EDragonSoulSubType +{ + DS_SLOT1, + DS_SLOT2, + DS_SLOT3, + DS_SLOT4, + DS_SLOT5, + DS_SLOT6, + DS_SLOT_MAX, +}; + +enum EDragonSoulGradeTypes +{ + DRAGON_SOUL_GRADE_NORMAL, + DRAGON_SOUL_GRADE_BRILLIANT, + DRAGON_SOUL_GRADE_RARE, + DRAGON_SOUL_GRADE_ANCIENT, + DRAGON_SOUL_GRADE_LEGENDARY, +#ifdef ENABLE_DS_GRADE_MYTH + DRAGON_SOUL_GRADE_MYTH, +#endif + DRAGON_SOUL_GRADE_MAX, +}; + +enum EDragonSoulStepTypes +{ + DRAGON_SOUL_STEP_LOWEST, + DRAGON_SOUL_STEP_LOW, + DRAGON_SOUL_STEP_MID, + DRAGON_SOUL_STEP_HIGH, + DRAGON_SOUL_STEP_HIGHEST, + DRAGON_SOUL_STEP_MAX, +}; +#define DRAGON_SOUL_STRENGTH_MAX 7 + +enum EDSInventoryMaxNum +{ + DRAGON_SOUL_INVENTORY_MAX_NUM = DS_SLOT_MAX * DRAGON_SOUL_GRADE_MAX * DRAGON_SOUL_BOX_SIZE, +}; + +enum EFishSubTypes +{ + FISH_ALIVE, + FISH_DEAD, +}; + +enum EResourceSubTypes +{ + RESOURCE_FISHBONE, + RESOURCE_WATERSTONEPIECE, + RESOURCE_WATERSTONE, + RESOURCE_BLOOD_PEARL, + RESOURCE_BLUE_PEARL, + RESOURCE_WHITE_PEARL, + RESOURCE_BUCKET, + RESOURCE_CRYSTAL, + RESOURCE_GEM, + RESOURCE_STONE, + RESOURCE_METIN, + RESOURCE_ORE, +}; + +enum EUniqueSubTypes +{ + UNIQUE_NONE, + UNIQUE_BOOK, + UNIQUE_SPECIAL_RIDE, + UNIQUE_SPECIAL_MOUNT_RIDE, +}; + +enum EUseSubTypes +{ + USE_POTION, // 0 + USE_TALISMAN, + USE_TUNING, + USE_MOVE, + USE_TREASURE_BOX, + USE_MONEYBAG, + USE_BAIT, + USE_ABILITY_UP, + USE_AFFECT, + USE_CREATE_STONE, + USE_SPECIAL, // 10 + USE_POTION_NODELAY, + USE_CLEAR, + USE_INVISIBILITY, + USE_DETACHMENT, + USE_BUCKET, + USE_POTION_CONTINUE, + USE_CLEAN_SOCKET, + USE_CHANGE_ATTRIBUTE, + USE_ADD_ATTRIBUTE, + USE_ADD_ACCESSORY_SOCKET, // 20 + USE_PUT_INTO_ACCESSORY_SOCKET, + USE_ADD_ATTRIBUTE2, + USE_RECIPE, + USE_CHANGE_ATTRIBUTE2, + USE_BIND, + USE_UNBIND, + USE_TIME_CHARGE_PER, + USE_TIME_CHARGE_FIX, // 28 + USE_PUT_INTO_BELT_SOCKET, + USE_PUT_INTO_RING_SOCKET, + USE_CHANGE_COSTUME_ATTR, // 31 + USE_RESET_COSTUME_ATTR, // 32 +}; + +enum EExtractSubTypes +{ + EXTRACT_DRAGON_SOUL, + EXTRACT_DRAGON_HEART, +}; + +enum EAutoUseSubTypes +{ + AUTOUSE_POTION, + AUTOUSE_ABILITY_UP, + AUTOUSE_BOMB, + AUTOUSE_GOLD, + AUTOUSE_MONEYBAG, + AUTOUSE_TREASURE_BOX +}; + +enum EMaterialSubTypes +{ + MATERIAL_LEATHER, + MATERIAL_BLOOD, + MATERIAL_ROOT, + MATERIAL_NEEDLE, + MATERIAL_JEWEL, + MATERIAL_DS_REFINE_NORMAL, + MATERIAL_DS_REFINE_BLESSED, + MATERIAL_DS_REFINE_HOLLY, +}; + +enum ESpecialSubTypes +{ + SPECIAL_MAP, + SPECIAL_KEY, + SPECIAL_DOC, + SPECIAL_SPIRIT, +}; + +enum EToolSubTypes +{ + TOOL_FISHING_ROD +}; + +enum ELotterySubTypes +{ + LOTTERY_TICKET, + LOTTERY_INSTANT +}; + +enum EItemFlag +{ + ITEM_FLAG_REFINEABLE = (1 << 0), + ITEM_FLAG_SAVE = (1 << 1), + ITEM_FLAG_STACKABLE = (1 << 2), + ITEM_FLAG_COUNT_PER_1GOLD = (1 << 3), + ITEM_FLAG_SLOW_QUERY = (1 << 4), + ITEM_FLAG_UNUSED01 = (1 << 5), // UNUSED + ITEM_FLAG_UNIQUE = (1 << 6), + ITEM_FLAG_MAKECOUNT = (1 << 7), + ITEM_FLAG_IRREMOVABLE = (1 << 8), + ITEM_FLAG_CONFIRM_WHEN_USE = (1 << 9), + ITEM_FLAG_QUEST_USE = (1 << 10), + ITEM_FLAG_QUEST_USE_MULTIPLE = (1 << 11), + ITEM_FLAG_QUEST_GIVE = (1 << 12), + ITEM_FLAG_LOG = (1 << 13), + ITEM_FLAG_APPLICABLE = (1 << 14), +}; + +enum EItemAntiFlag +{ + ITEM_ANTIFLAG_FEMALE = (1 << 0), + ITEM_ANTIFLAG_MALE = (1 << 1), + ITEM_ANTIFLAG_WARRIOR = (1 << 2), + ITEM_ANTIFLAG_ASSASSIN = (1 << 3), + ITEM_ANTIFLAG_SURA = (1 << 4), + ITEM_ANTIFLAG_SHAMAN = (1 << 5), + ITEM_ANTIFLAG_GET = (1 << 6), + ITEM_ANTIFLAG_DROP = (1 << 7), + ITEM_ANTIFLAG_SELL = (1 << 8), + ITEM_ANTIFLAG_EMPIRE_A = (1 << 9), + ITEM_ANTIFLAG_EMPIRE_B = (1 << 10), + ITEM_ANTIFLAG_EMPIRE_C = (1 << 11), + ITEM_ANTIFLAG_SAVE = (1 << 12), + ITEM_ANTIFLAG_GIVE = (1 << 13), + ITEM_ANTIFLAG_PKDROP = (1 << 14), + ITEM_ANTIFLAG_STACK = (1 << 15), + ITEM_ANTIFLAG_MYSHOP = (1 << 16), + ITEM_ANTIFLAG_SAFEBOX = (1 << 17), +#ifdef ENABLE_WOLFMAN_CHARACTER + ITEM_ANTIFLAG_WOLFMAN = (1 << 18), +#endif +}; + +enum EItemWearableFlag +{ + WEARABLE_BODY = (1 << 0), + WEARABLE_HEAD = (1 << 1), + WEARABLE_FOOTS = (1 << 2), + WEARABLE_WRIST = (1 << 3), + WEARABLE_WEAPON = (1 << 4), + WEARABLE_NECK = (1 << 5), + WEARABLE_EAR = (1 << 6), + WEARABLE_UNIQUE = (1 << 7), + WEARABLE_SHIELD = (1 << 8), + WEARABLE_ARROW = (1 << 9), + WEARABLE_HAIR = (1 << 10), + WEARABLE_ABILITY = (1 << 11), + WEARABLE_PENDANT = (1 << 12), + WEARABLE_GLOVE = (1 << 13), +}; + +enum ELimitTypes +{ + LIMIT_NONE, + LIMIT_LEVEL, + LIMIT_STR, + LIMIT_DEX, + LIMIT_INT, + LIMIT_CON, + LIMIT_PCBANG, //reserved for backwards compatibility + LIMIT_REAL_TIME, + LIMIT_REAL_TIME_START_FIRST_USE, + LIMIT_TIMER_BASED_ON_WEAR, + LIMIT_MAX_NUM, +}; + +enum EAttrAddonTypes +{ + ATTR_ADDON_NONE, + // positive values are reserved for set + ATTR_DAMAGE_ADDON = -1, +}; + +enum ERefineType +{ + REFINE_TYPE_NORMAL, + REFINE_TYPE_NOT_USED1, + REFINE_TYPE_SCROLL, + REFINE_TYPE_HYUNIRON, + REFINE_TYPE_MONEY_ONLY, + REFINE_TYPE_MUSIN, + REFINE_TYPE_BDRAGON, +}; + +enum EPetType +{ + PET_EGG, //0 + PET_UPBRINGING, //1 + PET_BAG, //2 + PET_FEEDSTUFF, //3 + PET_SKILL, //4 + PET_SKILL_DEL_BOOK, //5 + PET_NAME_CHANGE, //6 + PET_EXPFOOD, //7 + PET_SKILL_ALL_DEL_BOOK, //8 + PET_EXPFOOD_PER, //9 + PET_ATTR_DETERMINE, //10 + PET_ATTR_CHANGE, //11 + PET_PAY, //12 + PET_PRIMIUM_FEEDSTUFF, //13 +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/length.h b/source-server/Srcs/Server/common/length.h new file mode 100644 index 000000000..b3300b0f6 --- /dev/null +++ b/source-server/Srcs/Server/common/length.h @@ -0,0 +1,873 @@ +#ifndef __INC_METIN_II_LENGTH_H__ +#define __INC_METIN_II_LENGTH_H__ + +#include "CommonDefines.h" + +#define WORD_MAX 0xffff +enum EMisc +{ + MAX_HOST_LENGTH = 15, + IP_ADDRESS_LENGTH = 15, + LOGIN_MAX_LEN = 30, + PASSWD_MAX_LEN = 16, +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + PLAYER_PER_ACCOUNT = 5, +#else + PLAYER_PER_ACCOUNT = 4, +#endif + ACCOUNT_STATUS_MAX_LEN = 8, + CHARACTER_NAME_MAX_LEN = 24, + SHOP_SIGN_MAX_LEN = 32, + INVENTORY_PAGE_COLUMN = 5, // 5 (default) + INVENTORY_PAGE_ROW = 9, // 9 (default) + INVENTORY_PAGE_SIZE = INVENTORY_PAGE_COLUMN*INVENTORY_PAGE_ROW, +#ifdef ENABLE_EXTEND_INVEN_SYSTEM + INVENTORY_PAGE_COUNT = 4, // 2 (default) +#else + INVENTORY_PAGE_COUNT = 2, // 2 (default) +#endif + INVENTORY_MAX_NUM = INVENTORY_PAGE_SIZE*INVENTORY_PAGE_COUNT, // 90 (default) + ABILITY_MAX_NUM = 50, + EMPIRE_MAX_NUM = 4, + BANWORD_MAX_LEN = 24, + SOCIAL_ID_MAX_LEN = 18, + + GUILD_NAME_MAX_LEN = 12, + + SHOP_HOST_ITEM_MAX_NUM = 40, + SHOP_GUEST_ITEM_MAX_NUM = 18, + + SHOP_PRICELIST_MAX_NUM = 40, + + CHAT_MAX_LEN = 512, + + QUICKSLOT_MAX_NUM = 36, + + JOURNAL_MAX_NUM = 2, + + QUERY_MAX_LEN = 8192, + + FILE_MAX_LEN = 128, + + PLAYER_EXP_TABLE_MAX = 120, + PLAYER_MAX_LEVEL_CONST = 250, + + GUILD_MAX_LEVEL = 20, + MOB_MAX_LEVEL = 100, + + ATTRIBUTE_MAX_VALUE = 20, + CHARACTER_PATH_MAX_NUM = 64, + SKILL_MAX_NUM = 255, + SKILLBOOK_DELAY_MIN = 64800, + SKILLBOOK_DELAY_MAX = 108000, + SKILL_MAX_LEVEL = 40, + + APPLY_NAME_MAX_LEN = 32, + EVENT_FLAG_NAME_MAX_LEN = 32, + + MOB_SKILL_MAX_NUM = 5, + + POINT_MAX_NUM = 255, + DRAGON_SOUL_BOX_SIZE = 32, + DRAGON_SOUL_BOX_COLUMN_NUM = 8, + DRAGON_SOUL_BOX_ROW_NUM = DRAGON_SOUL_BOX_SIZE / DRAGON_SOUL_BOX_COLUMN_NUM, + DRAGON_SOUL_REFINE_GRID_SIZE = 15, + MAX_AMOUNT_OF_MALL_BONUS = 20, + + WEAR_MAX_NUM = 32, + + //LIMIT_GOLD + GOLD_MAX = 2000000000, +#ifdef ENABLE_CHEQUE_SYSTEM + CHEQUE_MAX = 1000, + CHEQUE_VNUM = 2, + #ifndef ENABLE_IKASHOP_RENEWAL + YANG_PER_CHEQUE = 100000000, + #endif +#endif + //END_LIMIT_GOLD + + SHOP_TAB_NAME_MAX = 32, + SHOP_TAB_COUNT_MAX = 3, + + BELT_INVENTORY_SLOT_WIDTH = 4, + BELT_INVENTORY_SLOT_HEIGHT= 4, + + BELT_INVENTORY_SLOT_COUNT = BELT_INVENTORY_SLOT_WIDTH * BELT_INVENTORY_SLOT_HEIGHT, +}; + +enum EWearPositions +{ + WEAR_BODY, // 0 + WEAR_HEAD, // 1 + WEAR_FOOTS, // 2 + WEAR_WRIST, // 3 + WEAR_WEAPON, // 4 + WEAR_NECK, // 5 + WEAR_EAR, // 6 + WEAR_UNIQUE1, // 7 + WEAR_UNIQUE2, // 8 + WEAR_ARROW, // 9 + WEAR_SHIELD, // 10 + WEAR_ABILITY1, // 11 + WEAR_ABILITY2, // 12 + WEAR_ABILITY3, // 13 + WEAR_ABILITY4, // 14 + WEAR_ABILITY5, // 15 + WEAR_ABILITY6, // 16 + WEAR_ABILITY7, // 17 + WEAR_ABILITY8, // 18 + WEAR_COSTUME_BODY, // 19 + WEAR_COSTUME_HAIR, // 20 + +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + WEAR_COSTUME_MOUNT, // 21 +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + WEAR_COSTUME_ACCE, // 22 +#endif + + WEAR_BELT, // 23 + +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + WEAR_COSTUME_WEAPON,// 24 +#endif + + WEAR_PENDANT, // 25 + WEAR_GLOVE, // 26 + + WEAR_MAX = 32 +}; + +enum EDragonSoulDeckType +{ + DRAGON_SOUL_DECK_0, + DRAGON_SOUL_DECK_1, + DRAGON_SOUL_DECK_MAX_NUM = 2, + + DRAGON_SOUL_DECK_RESERVED_MAX_NUM = 3, +}; + +enum ESex +{ + SEX_MALE, + SEX_FEMALE +}; + +enum EDirection +{ + DIR_NORTH, + DIR_NORTHEAST, + DIR_EAST, + DIR_SOUTHEAST, + DIR_SOUTH, + DIR_SOUTHWEST, + DIR_WEST, + DIR_NORTHWEST, + DIR_MAX_NUM +}; + +#define ABILITY_MAX_LEVEL 10 + +enum EAbilityDifficulty +{ + DIFFICULTY_EASY, + DIFFICULTY_NORMAL, + DIFFICULTY_HARD, + DIFFICULTY_VERY_HARD, + DIFFICULTY_NUM_TYPES +}; + +enum EAbilityCategory +{ + CATEGORY_PHYSICAL, + CATEGORY_MENTAL, + CATEGORY_ATTRIBUTE, + CATEGORY_NUM_TYPES +}; + +enum EJobs +{ + JOB_WARRIOR, + JOB_ASSASSIN, + JOB_SURA, + JOB_SHAMAN, +#ifdef ENABLE_WOLFMAN_CHARACTER + JOB_WOLFMAN, +#endif + JOB_MAX_NUM +}; + +enum ESkillGroups +{ + SKILL_GROUP_MAX_NUM = 2, +}; + +enum ERaceFlags +{ + RACE_FLAG_ANIMAL = (1 << 0), + RACE_FLAG_UNDEAD = (1 << 1), + RACE_FLAG_DEVIL = (1 << 2), + RACE_FLAG_HUMAN = (1 << 3), + RACE_FLAG_ORC = (1 << 4), + RACE_FLAG_MILGYO = (1 << 5), + RACE_FLAG_INSECT = (1 << 6), + RACE_FLAG_FIRE = (1 << 7), + RACE_FLAG_ICE = (1 << 8), + RACE_FLAG_DESERT = (1 << 9), + RACE_FLAG_TREE = (1 << 10), + RACE_FLAG_ATT_ELEC = (1 << 11), + RACE_FLAG_ATT_FIRE = (1 << 12), + RACE_FLAG_ATT_ICE = (1 << 13), + RACE_FLAG_ATT_WIND = (1 << 14), + RACE_FLAG_ATT_EARTH = (1 << 15), + RACE_FLAG_ATT_DARK = (1 << 16), + RACE_FLAG_CZ = (1 << 17), // 1<<11 official +}; + +enum ELoads +{ + LOAD_NONE, + LOAD_LIGHT, + LOAD_NORMAL, + LOAD_HEAVY, + LOAD_MASSIVE +}; + +enum +{ + QUICKSLOT_TYPE_NONE, + QUICKSLOT_TYPE_ITEM, + QUICKSLOT_TYPE_SKILL, + QUICKSLOT_TYPE_COMMAND, + QUICKSLOT_TYPE_MAX_NUM, +}; + +enum EParts +{ + PART_MAIN, + PART_WEAPON, + PART_HEAD, + PART_HAIR, +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + PART_ACCE, +#endif + PART_MAX_NUM, + PART_WEAPON_SUB, +}; + +enum EChatType +{ + CHAT_TYPE_TALKING, + CHAT_TYPE_INFO, + CHAT_TYPE_NOTICE, + CHAT_TYPE_PARTY, + CHAT_TYPE_GUILD, + CHAT_TYPE_COMMAND, + CHAT_TYPE_SHOUT, + CHAT_TYPE_WHISPER, + CHAT_TYPE_BIG_NOTICE, + CHAT_TYPE_MONARCH_NOTICE, +#ifdef ENABLE_DICE_SYSTEM + CHAT_TYPE_DICE_INFO, //11 +#endif + CHAT_TYPE_MAX_NUM +}; + +enum EWhisperType +{ + WHISPER_TYPE_NORMAL = 0, + WHISPER_TYPE_NOT_EXIST = 1, + WHISPER_TYPE_TARGET_BLOCKED = 2, + WHISPER_TYPE_SENDER_BLOCKED = 3, + WHISPER_TYPE_ERROR = 4, + WHISPER_TYPE_GM = 5, + WHISPER_TYPE_SYSTEM = 0xFF +}; + +enum ECharacterPosition +{ + POSITION_GENERAL, + POSITION_BATTLE, + POSITION_DYING, + POSITION_SITTING_CHAIR, + POSITION_SITTING_GROUND, + POSITION_INTRO, + POSITION_MAX_NUM +}; + +enum EGMLevels +{ + GM_PLAYER, + GM_LOW_WIZARD, + GM_WIZARD, + GM_HIGH_WIZARD, + GM_GOD, + GM_IMPLEMENTOR, + GM_DISABLE, +}; + +enum EMobRank +{ + MOB_RANK_PAWN, + MOB_RANK_S_PAWN, + MOB_RANK_KNIGHT, + MOB_RANK_S_KNIGHT, + MOB_RANK_BOSS, + MOB_RANK_KING, + MOB_RANK_MAX_NUM +}; + +enum ECharType +{ + CHAR_TYPE_MONSTER, + CHAR_TYPE_NPC, + CHAR_TYPE_STONE, + CHAR_TYPE_WARP, + CHAR_TYPE_DOOR, + CHAR_TYPE_BUILDING, + CHAR_TYPE_PC, + CHAR_TYPE_POLYMORPH_PC, + CHAR_TYPE_HORSE, + CHAR_TYPE_GOTO +}; + +enum EBattleType +{ + BATTLE_TYPE_MELEE, + BATTLE_TYPE_RANGE, + BATTLE_TYPE_MAGIC, + BATTLE_TYPE_SPECIAL, + BATTLE_TYPE_POWER, + BATTLE_TYPE_TANKER, + BATTLE_TYPE_SUPER_POWER, + BATTLE_TYPE_SUPER_TANKER, + BATTLE_TYPE_MAX_NUM +}; + +enum EApplyTypes +{ + APPLY_NONE, // 0 + APPLY_MAX_HP, // 1 + APPLY_MAX_SP, // 2 + APPLY_CON, // 3 + APPLY_INT, // 4 + APPLY_STR, // 5 + APPLY_DEX, // 6 + APPLY_ATT_SPEED, // 7 + APPLY_MOV_SPEED, // 8 + APPLY_CAST_SPEED, // 9 + APPLY_HP_REGEN, // 10 + APPLY_SP_REGEN, // 11 + APPLY_POISON_PCT, // 12 + APPLY_STUN_PCT, // 13 + APPLY_SLOW_PCT, // 14 + APPLY_CRITICAL_PCT, // 15 + APPLY_PENETRATE_PCT, // 16 + APPLY_ATTBONUS_HUMAN, // 17 + APPLY_ATTBONUS_ANIMAL, // 18 + APPLY_ATTBONUS_ORC, // 19 + APPLY_ATTBONUS_MILGYO, // 20 + APPLY_ATTBONUS_UNDEAD, // 21 + APPLY_ATTBONUS_DEVIL, // 22 + APPLY_STEAL_HP, // 23 + APPLY_STEAL_SP, // 24 + APPLY_MANA_BURN_PCT, // 25 + APPLY_DAMAGE_SP_RECOVER, // 26 + APPLY_BLOCK, // 27 + APPLY_DODGE, // 28 + APPLY_RESIST_SWORD, // 29 + APPLY_RESIST_TWOHAND, // 30 + APPLY_RESIST_DAGGER, // 31 + APPLY_RESIST_BELL, // 32 + APPLY_RESIST_FAN, // 33 + APPLY_RESIST_BOW, // 34 + APPLY_RESIST_FIRE, // 35 + APPLY_RESIST_ELEC, // 36 + APPLY_RESIST_MAGIC, // 37 + APPLY_RESIST_WIND, // 38 + APPLY_REFLECT_MELEE, // 39 + APPLY_REFLECT_CURSE, // 40 + APPLY_POISON_REDUCE, // 41 + APPLY_KILL_SP_RECOVER, // 42 + APPLY_EXP_DOUBLE_BONUS, // 43 + APPLY_GOLD_DOUBLE_BONUS, // 44 + APPLY_ITEM_DROP_BONUS, // 45 + APPLY_POTION_BONUS, // 46 + APPLY_KILL_HP_RECOVER, // 47 + APPLY_IMMUNE_STUN, // 48 + APPLY_IMMUNE_SLOW, // 49 + APPLY_IMMUNE_FALL, // 50 + APPLY_SKILL, // 51 + APPLY_BOW_DISTANCE, // 52 + APPLY_ATT_GRADE_BONUS, // 53 + APPLY_DEF_GRADE_BONUS, // 54 + APPLY_MAGIC_ATT_GRADE, // 55 + APPLY_MAGIC_DEF_GRADE, // 56 + APPLY_CURSE_PCT, // 57 + APPLY_MAX_STAMINA, // 58 + APPLY_ATTBONUS_WARRIOR, // 59 + APPLY_ATTBONUS_ASSASSIN, // 60 + APPLY_ATTBONUS_SURA, // 61 + APPLY_ATTBONUS_SHAMAN, // 62 + APPLY_ATTBONUS_MONSTER, // 63 + APPLY_MALL_ATTBONUS, + APPLY_MALL_DEFBONUS, + APPLY_MALL_EXPBONUS, + APPLY_MALL_ITEMBONUS, + APPLY_MALL_GOLDBONUS, + APPLY_MAX_HP_PCT, + APPLY_MAX_SP_PCT, + APPLY_SKILL_DAMAGE_BONUS, + APPLY_NORMAL_HIT_DAMAGE_BONUS, + APPLY_SKILL_DEFEND_BONUS, + APPLY_NORMAL_HIT_DEFEND_BONUS, + APPLY_PC_BANG_EXP_BONUS, //reserved for backwards compatibility + APPLY_PC_BANG_DROP_BONUS, //reserved for backwards compatibility + + APPLY_EXTRACT_HP_PCT, + + APPLY_RESIST_WARRIOR, + APPLY_RESIST_ASSASSIN, + APPLY_RESIST_SURA, + APPLY_RESIST_SHAMAN, + APPLY_ENERGY, + APPLY_DEF_GRADE, + APPLY_COSTUME_ATTR_BONUS, + APPLY_MAGIC_ATTBONUS_PER, + APPLY_MELEE_MAGIC_ATTBONUS_PER, + + APPLY_RESIST_ICE, + APPLY_RESIST_EARTH, + APPLY_RESIST_DARK, + + APPLY_ANTI_CRITICAL_PCT, + APPLY_ANTI_PENETRATE_PCT, + +#ifdef ENABLE_WOLFMAN_CHARACTER + APPLY_BLEEDING_REDUCE = 92, //92 + APPLY_BLEEDING_PCT = 93, //93 + APPLY_ATTBONUS_WOLFMAN = 94, + APPLY_RESIST_WOLFMAN = 95, + APPLY_RESIST_CLAW = 96, +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + APPLY_ACCEDRAIN_RATE = 97, //97 +#endif + +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + APPLY_RESIST_MAGIC_REDUCTION = 98, //98 +#endif + + APPLY_ENCHANT_ELECT = 99, + APPLY_ENCHANT_FIRE = 100, + APPLY_ENCHANT_ICE = 101, + APPLY_ENCHANT_WIND = 102, + APPLY_ENCHANT_EARTH = 103, + APPLY_ENCHANT_DARK = 104, + + APPLY_ATTBONUS_CZ = 105, + APPLY_ATTBONUS_INSECT = 106, + APPLY_ATTBONUS_DESERT = 107, + APPLY_ATTBONUS_SWORD = 108, + APPLY_ATTBONUS_TWOHAND = 109, + APPLY_ATTBONUS_DAGGER = 110, + APPLY_ATTBONUS_BELL = 111, + APPLY_ATTBONUS_FAN = 112, + APPLY_ATTBONUS_BOW = 113, +#ifdef ENABLE_WOLFMAN_CHARACTER + APPLY_ATTBONUS_CLAW = 114, +#endif + + APPLY_RESIST_HUMAN = 115, + APPLY_RESIST_MOUNT_FALL = 116, + APPLY_MOUNT = 118, + + MAX_APPLY_NUM = 127, +}; + +enum EOnClickEvents +{ + ON_CLICK_NONE, + ON_CLICK_SHOP, + ON_CLICK_TALK, + ON_CLICK_MAX_NUM +}; + +enum EOnIdleEvents +{ + ON_IDLE_NONE, + ON_IDLE_GENERAL, + ON_IDLE_MAX_NUM +}; + +enum EWindows +{ + RESERVED_WINDOW, + INVENTORY, + EQUIPMENT, + SAFEBOX, + MALL, + DRAGON_SOUL_INVENTORY, + BELT_INVENTORY, + GROUND, + WINDOW_TYPE_MAX, +}; + +enum EMobSizes +{ + MOBSIZE_RESERVED, + MOBSIZE_SMALL, + MOBSIZE_MEDIUM, + MOBSIZE_BIG +}; + +enum EAIFlags +{ + AIFLAG_AGGRESSIVE = (1 << 0), + AIFLAG_NOMOVE = (1 << 1), + AIFLAG_COWARD = (1 << 2), + AIFLAG_NOATTACKSHINSU = (1 << 3), + AIFLAG_NOATTACKJINNO = (1 << 4), + AIFLAG_NOATTACKCHUNJO = (1 << 5), + AIFLAG_ATTACKMOB = (1 << 6), + AIFLAG_BERSERK = (1 << 7), + AIFLAG_STONESKIN = (1 << 8), + AIFLAG_GODSPEED = (1 << 9), + AIFLAG_DEATHBLOW = (1 << 10), + AIFLAG_REVIVE = (1 << 11), + AIFLAG_HEALER = (1 << 12), + AIFLAG_COUNT = (1 << 13), + AIFLAG_NORECOVERY = (1 << 14), + AIFLAG_REFLECT = (1 << 15), + AIFLAG_FALL = (1 << 16), + AIFLAG_VIT = (1 << 17), + AIFLAG_RATTSPEED = (1 << 18), + AIFLAG_RCASTSPEED = (1 << 19), + AIFLAG_RHP_REGEN = (1 << 20), + AIFLAG_TIMEVIT = (1 << 21), +}; + +enum EMobStatType +{ + MOB_STATTYPE_POWER, + MOB_STATTYPE_TANKER, + MOB_STATTYPE_SUPER_POWER, + MOB_STATTYPE_SUPER_TANKER, + MOB_STATTYPE_RANGE, + MOB_STATTYPE_MAGIC, + MOB_STATTYPE_MAX_NUM +}; + +enum EImmuneFlags +{ + IMMUNE_STUN = (1 << 0), + IMMUNE_SLOW = (1 << 1), + IMMUNE_FALL = (1 << 2), + IMMUNE_CURSE = (1 << 3), + IMMUNE_POISON = (1 << 4), + IMMUNE_TERROR = (1 << 5), + IMMUNE_REFLECT = (1 << 6), +}; + +enum EMobEnchants +{ + MOB_ENCHANT_CURSE, + MOB_ENCHANT_SLOW, + MOB_ENCHANT_POISON, + MOB_ENCHANT_STUN, + MOB_ENCHANT_CRITICAL, + MOB_ENCHANT_PENETRATE, +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + MOB_ENCHANT_BLEEDING, +#endif + MOB_ENCHANTS_MAX_NUM +}; + +enum EMobResists +{ + MOB_RESIST_SWORD, + MOB_RESIST_TWOHAND, + MOB_RESIST_DAGGER, + MOB_RESIST_BELL, + MOB_RESIST_FAN, + MOB_RESIST_BOW, + MOB_RESIST_FIRE, + MOB_RESIST_ELECT, + MOB_RESIST_MAGIC, + MOB_RESIST_WIND, + MOB_RESIST_POISON, +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_CLAW_AS_DAGGER) + MOB_RESIST_CLAW, +#endif +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + MOB_RESIST_BLEEDING, +#endif + MOB_RESISTS_MAX_NUM +}; + +enum +{ + SKILL_ATTR_TYPE_NORMAL = 1, + SKILL_ATTR_TYPE_MELEE, + SKILL_ATTR_TYPE_RANGE, + SKILL_ATTR_TYPE_MAGIC + /* + SKILL_ATTR_TYPE_FIRE, + SKILL_ATTR_TYPE_ICE, + SKILL_ATTR_TYPE_ELEC, + SKILL_ATTR_TYPE_DARK, + */ +}; + +enum +{ + SKILL_NORMAL, + SKILL_MASTER, + SKILL_GRAND_MASTER, + SKILL_PERFECT_MASTER, +}; + +enum EGuildWarType +{ + GUILD_WAR_TYPE_FIELD, + GUILD_WAR_TYPE_BATTLE, + GUILD_WAR_TYPE_FLAG, + GUILD_WAR_TYPE_MAX_NUM +}; + +enum EGuildWarState +{ + GUILD_WAR_NONE, + GUILD_WAR_SEND_DECLARE, + GUILD_WAR_REFUSE, + GUILD_WAR_RECV_DECLARE, + GUILD_WAR_WAIT_START, + GUILD_WAR_CANCEL, + GUILD_WAR_ON_WAR, + GUILD_WAR_END, + GUILD_WAR_OVER, + GUILD_WAR_RESERVE, + + GUILD_WAR_DURATION = 30*60, + GUILD_WAR_WIN_POINT = 1000, + GUILD_WAR_LADDER_HALF_PENALTY_TIME = 12*60*60, +}; + +enum EAttributeSet +{ + ATTRIBUTE_SET_WEAPON, + ATTRIBUTE_SET_BODY, + ATTRIBUTE_SET_WRIST, + ATTRIBUTE_SET_FOOTS, + ATTRIBUTE_SET_NECK, + ATTRIBUTE_SET_HEAD, + ATTRIBUTE_SET_SHIELD, + ATTRIBUTE_SET_EAR, +#ifdef ENABLE_ITEM_ATTR_COSTUME + ATTRIBUTE_SET_COSTUME_BODY, + ATTRIBUTE_SET_COSTUME_HAIR, +#if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + ATTRIBUTE_SET_COSTUME_WEAPON, +#endif +#endif +#ifdef ENABLE_PENDANT_SYSTEM + ATTRIBUTE_SET_PENDANT, +#endif +#ifdef ENABLE_GLOVE_SYSTEM + ATTRIBUTE_SET_GLOVE, +#endif + ATTRIBUTE_SET_MAX_NUM +}; + +enum EPrivType +{ + PRIV_NONE, + PRIV_ITEM_DROP, + PRIV_GOLD_DROP, + PRIV_GOLD10_DROP, + PRIV_EXP_PCT, + MAX_PRIV_NUM, +}; + +enum EMoneyLogType +{ + MONEY_LOG_RESERVED, + MONEY_LOG_MONSTER, + MONEY_LOG_SHOP, + MONEY_LOG_REFINE, + MONEY_LOG_QUEST, + MONEY_LOG_GUILD, + MONEY_LOG_MISC, + MONEY_LOG_MONSTER_KILL, + MONEY_LOG_DROP, + MONEY_LOG_TYPE_MAX_NUM, +}; + +enum EPremiumTypes +{ + PREMIUM_EXP, + PREMIUM_ITEM, + PREMIUM_SAFEBOX, + PREMIUM_AUTOLOOT, + PREMIUM_FISH_MIND, + PREMIUM_MARRIAGE_FAST, + PREMIUM_GOLD, + PREMIUM_MAX_NUM = 9 +}; + +enum SPECIAL_EFFECT +{ + SE_NONE, + + SE_HPUP_RED, + SE_SPUP_BLUE, + SE_SPEEDUP_GREEN, + SE_DXUP_PURPLE, + SE_CRITICAL, + SE_PENETRATE, + SE_BLOCK, + SE_DODGE, + SE_CHINA_FIREWORK, + SE_SPIN_TOP, + SE_SUCCESS, + SE_FAIL, + SE_FR_SUCCESS, + SE_LEVELUP_ON_14_FOR_GERMANY, + SE_LEVELUP_UNDER_15_FOR_GERMANY, + SE_PERCENT_DAMAGE1, + SE_PERCENT_DAMAGE2, + SE_PERCENT_DAMAGE3, + + SE_AUTO_HPUP, + SE_AUTO_SPUP, + + SE_EQUIP_RAMADAN_RING, + SE_EQUIP_HALLOWEEN_CANDY, + SE_EQUIP_HAPPINESS_RING, + SE_EQUIP_LOVE_PENDANT, + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + SE_EFFECT_ACCE_SUCESS_ABSORB, + SE_EFFECT_ACCE_EQUIP, +#endif +}; + +#include "item_length.h" + +enum EDragonSoulRefineWindowSize +{ + DRAGON_SOUL_REFINE_GRID_MAX = 15, +}; + +enum EMisc2 +{ + DRAGON_SOUL_EQUIP_SLOT_START = INVENTORY_MAX_NUM + WEAR_MAX_NUM, + DRAGON_SOUL_EQUIP_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_START + (DS_SLOT_MAX * DRAGON_SOUL_DECK_MAX_NUM), + DRAGON_SOUL_EQUIP_RESERVED_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_END + (DS_SLOT_MAX * DRAGON_SOUL_DECK_RESERVED_MAX_NUM), + + BELT_INVENTORY_SLOT_START = DRAGON_SOUL_EQUIP_RESERVED_SLOT_END, + BELT_INVENTORY_SLOT_END = BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT, + + INVENTORY_AND_EQUIP_SLOT_MAX = BELT_INVENTORY_SLOT_END, +}; + +#pragma pack(push, 1) + +typedef struct SItemPos +{ + BYTE window_type; + WORD cell; + SItemPos () + { + window_type = INVENTORY; + cell = WORD_MAX; + } + + SItemPos (BYTE _window_type, WORD _cell) + { + window_type = _window_type; + cell = _cell; + } + + bool IsValidItemPosition() const + { + switch (window_type) + { + case RESERVED_WINDOW: + return false; + case INVENTORY: + case EQUIPMENT: + case BELT_INVENTORY: + return cell < INVENTORY_AND_EQUIP_SLOT_MAX; + case DRAGON_SOUL_INVENTORY: + return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM); + + case SAFEBOX: + case MALL: + return false; + default: + return false; + } + return false; + } + + bool IsSamePosition(const SItemPos & other) const + { + return *this==other + || ((INVENTORY == window_type || EQUIPMENT == window_type) + && (INVENTORY == other.window_type || EQUIPMENT == other.window_type) + && cell == other.cell); + } + + bool IsEquipPosition() const + { + return ((INVENTORY == window_type || EQUIPMENT == window_type) && cell >= INVENTORY_MAX_NUM && cell < INVENTORY_MAX_NUM + WEAR_MAX_NUM) + || IsDragonSoulEquipPosition(); + } + + bool IsDragonSoulEquipPosition() const + { + return (window_type == INVENTORY) && (DRAGON_SOUL_EQUIP_SLOT_START <= cell) && (DRAGON_SOUL_EQUIP_SLOT_END > cell); // @fixme184 + } + + bool IsBeltInventoryPosition() const // @fixme324 (INVENTORY n EQUIPMENT) + { + return (window_type == INVENTORY || window_type == EQUIPMENT) && (BELT_INVENTORY_SLOT_START <= cell) && (BELT_INVENTORY_SLOT_END > cell); + } + + bool IsDefaultInventoryPosition() const + { + return INVENTORY == window_type && cell < INVENTORY_MAX_NUM; + } + + bool operator==(const struct SItemPos& rhs) const + { + return (window_type == rhs.window_type) && (cell == rhs.cell); + } + bool operator<(const struct SItemPos& rhs) const + { + return (window_type < rhs.window_type) || ((window_type == rhs.window_type) && (cell < rhs.cell)); + } +} TItemPos; + +const TItemPos NPOS (RESERVED_WINDOW, WORD_MAX); + +typedef struct SItemPosEx +{ + TItemPos pos; + int id{ 0 }; +} TItemPosEx; + +typedef enum +{ + SHOP_COIN_TYPE_GOLD, // DEFAULT VALUE + SHOP_COIN_TYPE_SECONDARY_COIN, +} EShopCoinType; + +#pragma pack(pop) + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/noncopyable.h b/source-server/Srcs/Server/common/noncopyable.h new file mode 100644 index 000000000..ee69ff61e --- /dev/null +++ b/source-server/Srcs/Server/common/noncopyable.h @@ -0,0 +1,16 @@ +#ifndef INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE +#define INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE + +class noncopyable +{ + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable &); + noncopyable& operator = (const noncopyable &); +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/pool.h b/source-server/Srcs/Server/common/pool.h new file mode 100644 index 000000000..bfc5561b0 --- /dev/null +++ b/source-server/Srcs/Server/common/pool.h @@ -0,0 +1,186 @@ +#ifndef __INC_METIN_II_COMMON_POOL_H__ +#define __INC_METIN_II_COMMON_POOL_H__ + +#include +#include + +template +class CPoolNode : public T +{ + public: + CPoolNode() + { + m_pNext = NULL; + m_pPrev = NULL; + } + + virtual ~CPoolNode() + { + } + + public: + CPoolNode * m_pNext; + CPoolNode * m_pPrev; +}; + +template +class CDynamicPool +{ + public: + typedef CPoolNode TNode; + + public: + CDynamicPool() + { + Initialize(); + } + + virtual ~CDynamicPool() + { + assert(m_pFreeList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear"); + assert(m_pUsedList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear"); + Clear(); + } + + void Initialize() + { + m_nodes = NULL; + m_nodeCount = 0; + + m_pFreeList = NULL; + m_pUsedList = NULL; + } + + void SetName(const char* c_szName) + { + m_stName = c_szName; + } + + DWORD GetCapacity() + { + return m_nodeCount; + } + + T* Alloc() + { + TNode* pnewNode; + + if (m_pFreeList) + { + pnewNode = m_pFreeList; + m_pFreeList = m_pFreeList->m_pNext; + } + else + { + pnewNode = AllocNode(); + } + + if (!pnewNode) + return NULL; + + if (!m_pUsedList) + { + m_pUsedList = pnewNode; + m_pUsedList->m_pPrev = m_pUsedList->m_pNext = NULL; + } + else + { + m_pUsedList->m_pPrev = pnewNode; + pnewNode->m_pNext = m_pUsedList; + pnewNode->m_pPrev = NULL; + m_pUsedList = pnewNode; + } + //Tracef("%s Pool Alloc %p\n", m_stName.c_str(), pnewNode); + return (T*) pnewNode; + } + + void Free(T * pdata) + { + TNode* pfreeNode = (TNode*) pdata; + + if (pfreeNode == m_pUsedList) + { + if (NULL != (m_pUsedList = m_pUsedList->m_pNext)) + m_pUsedList->m_pPrev = NULL; + } + else + { + if (pfreeNode->m_pNext) + pfreeNode->m_pNext->m_pPrev = pfreeNode->m_pPrev; + + if (pfreeNode->m_pPrev) + pfreeNode->m_pPrev->m_pNext = pfreeNode->m_pNext; + } + + pfreeNode->m_pPrev = NULL; + pfreeNode->m_pNext = m_pFreeList; + m_pFreeList = pfreeNode; + //Tracef("%s Pool Free\n", m_stName.c_str()); + } + + void FreeAll() + { + TNode * pcurNode; + TNode * pnextNode; + + pcurNode = m_pUsedList; + + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + Free(pcurNode); + pcurNode = pnextNode; + } + } + + void Clear() + { + TNode* pcurNode; + TNode* pnextNode; + + [[maybe_unused]] DWORD count = 0; + + pcurNode = m_pFreeList; + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + delete pcurNode; + pcurNode = pnextNode; + ++count; + } + m_pFreeList = NULL; + + pcurNode = m_pUsedList; + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + delete pcurNode; + pcurNode = pnextNode; + ++count; + } + + m_pUsedList = NULL; + + assert(count==m_nodeCount && "CDynamicPool::Clear()"); + + m_nodeCount=0; + } + + protected: + TNode* AllocNode() + { + ++m_nodeCount; + return new TNode; + } + + protected: + TNode * m_nodes; + TNode * m_pFreeList; + TNode * m_pUsedList; + + DWORD m_nodeCount; + std::string m_stName; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/service.h b/source-server/Srcs/Server/common/service.h new file mode 100644 index 000000000..faefb3446 --- /dev/null +++ b/source-server/Srcs/Server/common/service.h @@ -0,0 +1,6 @@ +#ifndef __INC_SERVICE_H__ +#define __INC_SERVICE_H__ +#pragma once +#include "CommonDefines.h" +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/singleton.h b/source-server/Srcs/Server/common/singleton.h new file mode 100644 index 000000000..302da8ad6 --- /dev/null +++ b/source-server/Srcs/Server/common/singleton.h @@ -0,0 +1,45 @@ +#ifndef __INC_SINGLETON_H__ +#define __INC_SINGLETON_H__ + +#include + +template class singleton +{ + public: + static T * ms_singleton; + + singleton() + { + assert(!ms_singleton); + long offset = (long) (T*) 1 - (long) (singleton *) (T*) 1; + ms_singleton = (T*) ((long) this + offset); + } + + virtual ~singleton() + { + assert(ms_singleton); + ms_singleton = 0; + } + + static T & instance() + { + assert(ms_singleton); + return (*ms_singleton); + } + + static T & Instance() + { + assert(ms_singleton); + return (*ms_singleton); + } + + static T * instance_ptr() + { + return (ms_singleton); + } +}; + +template T * singleton ::ms_singleton = NULL; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/stl.h b/source-server/Srcs/Server/common/stl.h new file mode 100644 index 000000000..f5a287097 --- /dev/null +++ b/source-server/Srcs/Server/common/stl.h @@ -0,0 +1,237 @@ +#ifndef __INC_METIN_II_STL_H__ +#define __INC_METIN_II_STL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ + #ifndef __clang__ + #include + #else + #include + #endif +#endif + +#ifndef itertype +#define itertype(v) typeof((v).begin()) +#endif + +inline void stl_lowers(std::string& rstRet) +{ + for (size_t i = 0; i < rstRet.length(); ++i) + rstRet[i] = tolower(rstRet[i]); +} + +struct stringhash +{ + size_t operator () (const std::string & str) const + { + const unsigned char * s = (const unsigned char*) str.c_str(); + const unsigned char * end = s + str.size(); + size_t h = 0; + + while (s < end) + { + h *= 16777619; + h ^= *(s++); + } + + return h; + } +}; + +// code from tr1/functional_hash.h +template +struct hash; + +template +struct hash<_Tp*> +{ + std::size_t + operator()(_Tp* __p) const + { return reinterpret_cast(__p); } +}; + +namespace utils +{ + template struct IsContiguous { + static bool constexpr value = false; + }; + + template + struct IsContiguous> { + static bool constexpr value = true; + }; + + template <> + struct IsContiguous { + static bool constexpr value = true; + }; + + template + struct IsContiguous> { + static bool constexpr value = true; + }; + + template + constexpr bool IsContiguousV = IsContiguous::value; + + ///////////////////// + template + constexpr bool IsRawV = !std::is_pointer_v && std::is_trivially_copyable_v && !IsContiguousV; +}; + +namespace msl +{ + template< class F, class... Args > + constexpr decltype(auto) bind1st( F&& f, Args&&... args ) + { + return std::bind(f, args..., std::placeholders::_1); + } + + template< class F, class... Args > + constexpr decltype(auto) bind2nd( F&& f, Args&&... args ) + { + return std::bind(f, std::placeholders::_1, args...); + } + + template + void random_shuffle(T first, T last) + { + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(first, last, g); + } +}; + +namespace std +{ + template + void erase_if (container & a, typename container::iterator first, typename container::iterator past, Pred pred) + { + while (first != past) + if (pred(*first)) + a.erase(first++); + else + ++first; + } + + template + void wipe(container & a) + { + typename container::iterator first, past; + + first = a.begin(); + past = a.end(); + + while (first != past) + delete *(first++); + + a.clear(); + } + + template + void wipe_second(container & a) + { + typename container::iterator first, past; + + first = a.begin(); + past = a.end(); + + while (first != past) + { + delete first->second; + ++first; + } + + a.clear(); + } + + template T MINMAX(T min, T value, T max) + { + T tv; + + tv = (min > value ? min : value); + return (max < tv) ? max : tv; + } + + template + class void_mem_fun_t + { + public: + explicit void_mem_fun_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) + { + } + + void operator()(_Ty* p) const + { + ((p->*_Ptr)()); + } + + private: + void (_Ty::*_Ptr)(); + }; + + template inline + void_mem_fun_t<_Ty> void_mem_fun(void (_Ty::*_Pm)()) + { return (void_mem_fun_t<_Ty>(_Pm)); } + + template + class void_mem_fun_ref_t + { + public: + explicit void_mem_fun_ref_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) {} + void operator()(_Ty& x) const + { return ((x.*_Ptr)()); } + private: + void (_Ty::*_Ptr)(); + }; + + template inline + void_mem_fun_ref_t<_Ty> void_mem_fun_ref(void (_Ty::*_Pm)()) + { return (void_mem_fun_ref_t< _Ty>(_Pm)); } +}; + +inline std::string FormatNumberWithDots(long long number) { + std::string result; + bool isNegative = false; + + // Handle the case for negative numbers + if (number < 0) { + isNegative = true; + number = -number; // Make it positive for processing + } + + // Convert the number to a string + std::string numStr = std::to_string(number); + + // Insert dots from the right every three digits + int count = 0; + for (int i = numStr.size() - 1; i >= 0; --i) { + result.push_back(numStr[i]); + if (++count == 3 && i > 0) { + result.push_back('.'); + count = 0; + } + } + + // Reverse the result string to get the correct order + std::reverse(result.begin(), result.end()); + + // Add the negative sign if necessary + if (isNegative) { + result.insert(result.begin(), '-'); + } + + return result; +} + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/tables.h b/source-server/Srcs/Server/common/tables.h new file mode 100644 index 000000000..b58dcc02c --- /dev/null +++ b/source-server/Srcs/Server/common/tables.h @@ -0,0 +1,1362 @@ +#ifndef __INC_TABLES_H__ +#define __INC_TABLES_H__ + +#include "length.h" +#include "item_length.h" +#include "CommonDefines.h" + +typedef DWORD IDENT; + +enum +{ + HEADER_GD_LOGIN = 1, + HEADER_GD_LOGOUT = 2, + + HEADER_GD_PLAYER_LOAD = 3, + HEADER_GD_PLAYER_SAVE = 4, + HEADER_GD_PLAYER_CREATE = 5, + HEADER_GD_PLAYER_DELETE = 6, + + HEADER_GD_LOGIN_KEY = 7, + // 8 empty + HEADER_GD_BOOT = 9, + HEADER_GD_PLAYER_COUNT = 10, + HEADER_GD_QUEST_SAVE = 11, + HEADER_GD_SAFEBOX_LOAD = 12, + HEADER_GD_SAFEBOX_SAVE = 13, + HEADER_GD_SAFEBOX_CHANGE_SIZE = 14, + HEADER_GD_EMPIRE_SELECT = 15, + + HEADER_GD_SAFEBOX_CHANGE_PASSWORD = 16, + HEADER_GD_SAFEBOX_CHANGE_PASSWORD_SECOND = 17, // Not really a packet, used internal + HEADER_GD_DIRECT_ENTER = 18, + + HEADER_GD_GUILD_SKILL_UPDATE = 19, + HEADER_GD_GUILD_EXP_UPDATE = 20, + HEADER_GD_GUILD_ADD_MEMBER = 21, + HEADER_GD_GUILD_REMOVE_MEMBER = 22, + HEADER_GD_GUILD_CHANGE_GRADE = 23, + HEADER_GD_GUILD_CHANGE_MEMBER_DATA = 24, + HEADER_GD_GUILD_DISBAND = 25, + HEADER_GD_GUILD_WAR = 26, + HEADER_GD_GUILD_WAR_SCORE = 27, + HEADER_GD_GUILD_CREATE = 28, + + HEADER_GD_ITEM_SAVE = 30, + HEADER_GD_ITEM_DESTROY = 31, + + HEADER_GD_ADD_AFFECT = 32, + HEADER_GD_REMOVE_AFFECT = 33, + + HEADER_GD_ITEM_FLUSH = 35, + + HEADER_GD_PARTY_CREATE = 36, + HEADER_GD_PARTY_DELETE = 37, + HEADER_GD_PARTY_ADD = 38, + HEADER_GD_PARTY_REMOVE = 39, + HEADER_GD_PARTY_STATE_CHANGE = 40, + HEADER_GD_PARTY_HEAL_USE = 41, + + HEADER_GD_RELOAD_PROTO = 43, + + HEADER_GD_CHANGE_NAME = 44, + + HEADER_GD_GUILD_CHANGE_LADDER_POINT = 46, + HEADER_GD_GUILD_USE_SKILL = 47, + + HEADER_GD_REQUEST_EMPIRE_PRIV = 48, + HEADER_GD_REQUEST_GUILD_PRIV = 49, + + HEADER_GD_MONEY_LOG = 50, + + HEADER_GD_GUILD_DEPOSIT_MONEY = 51, + HEADER_GD_GUILD_WITHDRAW_MONEY = 52, + HEADER_GD_GUILD_WITHDRAW_MONEY_GIVE_REPLY = 53, + + HEADER_GD_REQUEST_CHARACTER_PRIV = 54, + + HEADER_GD_SET_EVENT_FLAG = 55, + + HEADER_GD_PARTY_SET_MEMBER_LEVEL = 56, + + HEADER_GD_GUILD_WAR_BET = 57, + + HEADER_GD_CREATE_OBJECT = 60, + HEADER_GD_DELETE_OBJECT = 61, + HEADER_GD_UPDATE_LAND = 62, + + HEADER_GD_MARRIAGE_ADD = 70, + HEADER_GD_MARRIAGE_UPDATE = 71, + HEADER_GD_MARRIAGE_REMOVE = 72, + + HEADER_GD_WEDDING_REQUEST = 73, + HEADER_GD_WEDDING_READY = 74, + HEADER_GD_WEDDING_END = 75, + + HEADER_GD_AUTH_LOGIN = 100, + HEADER_GD_LOGIN_BY_KEY = 101, + HEADER_GD_BILLING_EXPIRE = 104, + HEADER_GD_BILLING_CHECK = 106, + HEADER_GD_MALL_LOAD = 107, + + HEADER_GD_MYSHOP_PRICELIST_UPDATE = 108, + HEADER_GD_MYSHOP_PRICELIST_REQ = 109, + + HEADER_GD_BLOCK_CHAT = 110, + + HEADER_GD_RELOAD_ADMIN = 115, + HEADER_GD_BREAK_MARRIAGE = 116, + HEADER_GD_ELECT_MONARCH = 117, + HEADER_GD_CANDIDACY = 118, + HEADER_GD_ADD_MONARCH_MONEY = 119, + HEADER_GD_TAKE_MONARCH_MONEY = 120, + HEADER_GD_COME_TO_VOTE = 121, + HEADER_GD_RMCANDIDACY = 122, + HEADER_GD_SETMONARCH = 123, + HEADER_GD_RMMONARCH = 124, + HEADER_GD_DEC_MONARCH_MONEY = 125, + + HEADER_GD_CHANGE_MONARCH_LORD = 126, + + HEADER_GD_REQ_CHANGE_GUILD_MASTER = 129, + + HEADER_GD_REQ_SPARE_ITEM_ID_RANGE = 130, + + HEADER_GD_UPDATE_HORSE_NAME = 131, + HEADER_GD_REQ_HORSE_NAME = 132, + + HEADER_GD_DC = 133, + + HEADER_GD_VALID_LOGOUT = 134, + + HEADER_GD_REQUEST_CHARGE_CASH = 137, + + HEADER_GD_DELETE_AWARDID = 138, // delete gift notify icon + + HEADER_GD_UPDATE_CHANNELSTATUS = 139, + HEADER_GD_REQUEST_CHANNELSTATUS = 140, + + HEADER_GD_SETUP = 0xff, + + /////////////////////////////////////////////// + HEADER_DG_NOTICE = 1, + + HEADER_DG_LOGIN_SUCCESS = 30, + HEADER_DG_LOGIN_NOT_EXIST = 31, + HEADER_DG_LOGIN_WRONG_PASSWD = 33, + HEADER_DG_LOGIN_ALREADY = 34, + + HEADER_DG_PLAYER_LOAD_SUCCESS = 35, + HEADER_DG_PLAYER_LOAD_FAILED = 36, + HEADER_DG_PLAYER_CREATE_SUCCESS = 37, + HEADER_DG_PLAYER_CREATE_ALREADY = 38, + HEADER_DG_PLAYER_CREATE_FAILED = 39, + HEADER_DG_PLAYER_DELETE_SUCCESS = 40, + HEADER_DG_PLAYER_DELETE_FAILED = 41, + + HEADER_DG_ITEM_LOAD = 42, + + HEADER_DG_BOOT = 43, + HEADER_DG_QUEST_LOAD = 44, + + HEADER_DG_SAFEBOX_LOAD = 45, + HEADER_DG_SAFEBOX_CHANGE_SIZE = 46, + HEADER_DG_SAFEBOX_WRONG_PASSWORD = 47, + HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER = 48, + + HEADER_DG_EMPIRE_SELECT = 49, + + HEADER_DG_AFFECT_LOAD = 50, + HEADER_DG_MALL_LOAD = 51, + + HEADER_DG_DIRECT_ENTER = 55, + + HEADER_DG_GUILD_SKILL_UPDATE = 56, + HEADER_DG_GUILD_SKILL_RECHARGE = 57, + HEADER_DG_GUILD_EXP_UPDATE = 58, + + HEADER_DG_PARTY_CREATE = 59, + HEADER_DG_PARTY_DELETE = 60, + HEADER_DG_PARTY_ADD = 61, + HEADER_DG_PARTY_REMOVE = 62, + HEADER_DG_PARTY_STATE_CHANGE = 63, + HEADER_DG_PARTY_HEAL_USE = 64, + HEADER_DG_PARTY_SET_MEMBER_LEVEL = 65, + + HEADER_DG_TIME = 90, + HEADER_DG_ITEM_ID_RANGE = 91, + + HEADER_DG_GUILD_ADD_MEMBER = 92, + HEADER_DG_GUILD_REMOVE_MEMBER = 93, + HEADER_DG_GUILD_CHANGE_GRADE = 94, + HEADER_DG_GUILD_CHANGE_MEMBER_DATA = 95, + HEADER_DG_GUILD_DISBAND = 96, + HEADER_DG_GUILD_WAR = 97, + HEADER_DG_GUILD_WAR_SCORE = 98, + HEADER_DG_GUILD_TIME_UPDATE = 99, + HEADER_DG_GUILD_LOAD = 100, + HEADER_DG_GUILD_LADDER = 101, + HEADER_DG_GUILD_SKILL_USABLE_CHANGE = 102, + HEADER_DG_GUILD_MONEY_CHANGE = 103, + HEADER_DG_GUILD_WITHDRAW_MONEY_GIVE = 104, + + HEADER_DG_SET_EVENT_FLAG = 105, + + HEADER_DG_GUILD_WAR_RESERVE_ADD = 106, + HEADER_DG_GUILD_WAR_RESERVE_DEL = 107, + HEADER_DG_GUILD_WAR_BET = 108, + + HEADER_DG_RELOAD_PROTO = 120, + HEADER_DG_CHANGE_NAME = 121, + + HEADER_DG_AUTH_LOGIN = 122, + + HEADER_DG_CHANGE_EMPIRE_PRIV = 124, + HEADER_DG_CHANGE_GUILD_PRIV = 125, + + HEADER_DG_MONEY_LOG = 126, + + HEADER_DG_CHANGE_CHARACTER_PRIV = 127, + + HEADER_DG_BILLING_REPAIR = 128, + HEADER_DG_BILLING_EXPIRE = 129, + HEADER_DG_BILLING_LOGIN = 130, + HEADER_DG_BILLING_CHECK = 132, + + HEADER_DG_CREATE_OBJECT = 140, + HEADER_DG_DELETE_OBJECT = 141, + HEADER_DG_UPDATE_LAND = 142, + + HEADER_DG_MARRIAGE_ADD = 150, + HEADER_DG_MARRIAGE_UPDATE = 151, + HEADER_DG_MARRIAGE_REMOVE = 152, + + HEADER_DG_WEDDING_REQUEST = 153, + HEADER_DG_WEDDING_READY = 154, + HEADER_DG_WEDDING_START = 155, + HEADER_DG_WEDDING_END = 156, + + HEADER_DG_MYSHOP_PRICELIST_RES = 157, + HEADER_DG_RELOAD_ADMIN = 158, + HEADER_DG_BREAK_MARRIAGE = 159, + HEADER_DG_ELECT_MONARCH = 160, + HEADER_DG_CANDIDACY = 161, + HEADER_DG_ADD_MONARCH_MONEY = 162, + HEADER_DG_TAKE_MONARCH_MONEY = 163, + HEADER_DG_COME_TO_VOTE = 164, + HEADER_DG_RMCANDIDACY = 165, + HEADER_DG_SETMONARCH = 166, + HEADER_DG_RMMONARCH = 167, + HEADER_DG_DEC_MONARCH_MONEY = 168, + + HEADER_DG_CHANGE_MONARCH_LORD_ACK = 169, + HEADER_DG_UPDATE_MONARCH_INFO = 170, + + HEADER_DG_ACK_CHANGE_GUILD_MASTER = 173, + + HEADER_DG_ACK_SPARE_ITEM_ID_RANGE = 174, + + HEADER_DG_UPDATE_HORSE_NAME = 175, + HEADER_DG_ACK_HORSE_NAME = 176, + + HEADER_DG_NEED_LOGIN_LOG = 177, + + HEADER_DG_RESULT_CHARGE_CASH = 179, + HEADER_DG_ITEMAWARD_INFORMER = 180, //gift notify + HEADER_DG_RESPOND_CHANNELSTATUS = 181, + + HEADER_DG_MAP_LOCATIONS = 0xfe, + HEADER_DG_P2P = 0xff, +}; + +/* ---------------------------------------------- + * table + * ---------------------------------------------- + */ + +/* game Server -> DB Server */ +#pragma pack(1) +enum ERequestChargeType +{ + ERequestCharge_Cash = 0, + ERequestCharge_Mileage, +}; + +typedef struct SRequestChargeCash +{ + DWORD dwAID; // id(primary key) - Account Table + DWORD dwAmount; + ERequestChargeType eChargeType; +} TRequestChargeCash; + +typedef struct SSimplePlayer +{ + DWORD dwID; + char szName[CHARACTER_NAME_MAX_LEN + 1]; + BYTE byJob; + BYTE byLevel; + DWORD dwPlayMinutes; + BYTE byST, byHT, byDX, byIQ; + DWORD wMainPart; // @fixme502 + BYTE bChangeName; + DWORD wHairPart; // @fixme502 +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + DWORD wAccePart; +#endif + BYTE bDummy[4]; + long x, y; + long lAddr; + WORD wPort; + BYTE skill_group; +} TSimplePlayer; + +typedef struct SAccountTable +{ + DWORD id; + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + char social_id[SOCIAL_ID_MAX_LEN + 1]; + char status[ACCOUNT_STATUS_MAX_LEN + 1]; + BYTE bEmpire; + TSimplePlayer players[PLAYER_PER_ACCOUNT]; +} TAccountTable; + +typedef struct SPacketDGCreateSuccess +{ + BYTE bAccountCharacterIndex; + TSimplePlayer player; +} TPacketDGCreateSuccess; + +typedef struct TPlayerItemAttribute +{ + BYTE bType; + short sValue; +} TPlayerItemAttribute; + +typedef struct SPlayerItem +{ + DWORD id; + BYTE window; + WORD pos; + DWORD count; + + DWORD vnum; + long alSockets[ITEM_SOCKET_MAX_NUM]; + + TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM]; + + DWORD owner; +} TPlayerItem; + +typedef struct SQuickslot +{ + BYTE type; + BYTE pos; +} TQuickslot; + +typedef struct SPlayerSkill +{ + BYTE bMasterType; + BYTE bLevel; + time_t tNextRead; +} TPlayerSkill; + +struct THorseInfo +{ + BYTE bLevel; + BYTE bRiding; + short sStamina; + short sHealth; + DWORD dwHorseHealthDropTime; +}; + +typedef struct SPlayerTable +{ + DWORD id; + + char name[CHARACTER_NAME_MAX_LEN + 1]; + char ip[IP_ADDRESS_LENGTH + 1]; + + WORD job; + BYTE voice; + + BYTE level; + BYTE level_step; + short st, ht, dx, iq; + + DWORD exp; + INT gold; + + BYTE dir; + INT x, y, z; + INT lMapIndex; + + long lExitX, lExitY; + long lExitMapIndex; + + // @fixme400 + int hp; + int sp; + + short sRandomHP; + short sRandomSP; + + int playtime; + + short stat_point; + short skill_point; + short sub_skill_point; + short horse_skill_point; +#ifdef ENABLE_CHEQUE_SYSTEM + int cheque; +#endif + + TPlayerSkill skills[SKILL_MAX_NUM]; + + TQuickslot quickslot[QUICKSLOT_MAX_NUM]; + + BYTE part_base; + DWORD parts[PART_MAX_NUM]; // @fixme502 + + short stamina; + + BYTE skill_group; + long lAlignment; + + short stat_reset_count; + + THorseInfo horse; + + DWORD logoff_interval; + + int aiPremiumTimes[PREMIUM_MAX_NUM]; +} TPlayerTable; + +typedef struct SMobSkillLevel +{ + DWORD dwVnum; + BYTE bLevel; +} TMobSkillLevel; + +typedef struct SEntityTable +{ + DWORD dwVnum; +} TEntityTable; + +typedef struct SMobTable : public SEntityTable +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + char szLocaleName[CHARACTER_NAME_MAX_LEN + 1]; + + BYTE bType; // Monster, NPC + BYTE bRank; // PAWN, KNIGHT, KING + BYTE bBattleType; // MELEE, etc.. + BYTE bLevel; // Level + BYTE bSize; + + DWORD dwGoldMin; + DWORD dwGoldMax; + DWORD dwExp; + DWORD dwMaxHP; + BYTE bRegenCycle; + BYTE bRegenPercent; + WORD wDef; + + DWORD dwAIFlag; + DWORD dwRaceFlag; + DWORD dwImmuneFlag; + + BYTE bStr, bDex, bCon, bInt; + DWORD dwDamageRange[2]; + + short sAttackSpeed; + short sMovingSpeed; + BYTE bAggresiveHPPct; + WORD wAggressiveSight; + WORD wAttackRange; + + char cEnchants[MOB_ENCHANTS_MAX_NUM]; + char cResists[MOB_RESISTS_MAX_NUM]; + + DWORD dwResurrectionVnum; + DWORD dwDropItemVnum; + + BYTE bMountCapacity; + BYTE bOnClickType; + + BYTE bEmpire; + char szFolder[64 + 1]; + + float fDamMultiply; + + DWORD dwSummonVnum; + DWORD dwDrainSP; + DWORD dwMobColor; + DWORD dwPolymorphItemVnum; + + TMobSkillLevel Skills[MOB_SKILL_MAX_NUM]; + + BYTE bBerserkPoint; + BYTE bStoneSkinPoint; + BYTE bGodSpeedPoint; + BYTE bDeathBlowPoint; + BYTE bRevivePoint; +} TMobTable; + +typedef struct SSkillTable +{ + DWORD dwVnum; + char szName[32 + 1]; + BYTE bType; + BYTE bMaxLevel; + DWORD dwSplashRange; + + char szPointOn[64]; + char szPointPoly[100 + 1]; + char szSPCostPoly[100 + 1]; + char szDurationPoly[100 + 1]; + char szDurationSPCostPoly[100 + 1]; + char szCooldownPoly[100 + 1]; + char szMasterBonusPoly[100 + 1]; + //char szAttackGradePoly[100 + 1]; + char szGrandMasterAddSPCostPoly[100 + 1]; + DWORD dwFlag; + DWORD dwAffectFlag; + + // Data for secondary skill + char szPointOn2[64]; + char szPointPoly2[100 + 1]; + char szDurationPoly2[100 + 1]; + DWORD dwAffectFlag2; + + // Data for grand master point + char szPointOn3[64]; + char szPointPoly3[100 + 1]; + char szDurationPoly3[100 + 1]; + + BYTE bLevelStep; + BYTE bLevelLimit; + DWORD preSkillVnum; + BYTE preSkillLevel; + + long lMaxHit; + char szSplashAroundDamageAdjustPoly[100 + 1]; + + BYTE bSkillAttrType; + + DWORD dwTargetRange; +} TSkillTable; + +typedef struct SShopItemTable +{ + DWORD vnum; + BYTE count; + + TItemPos pos; + DWORD price; +#ifdef ENABLE_CHEQUE_SYSTEM + DWORD cheque; +#endif + BYTE display_pos; +} TShopItemTable; + +typedef struct SShopTable +{ + DWORD dwVnum; + DWORD dwNPCVnum; + + BYTE byItemCount; + TShopItemTable items[SHOP_HOST_ITEM_MAX_NUM]; +} TShopTable; + +#define QUEST_NAME_MAX_LEN 32 +#define QUEST_STATE_MAX_LEN 64 + +typedef struct SQuestTable +{ + DWORD dwPID; + char szName[QUEST_NAME_MAX_LEN + 1]; + char szState[QUEST_STATE_MAX_LEN + 1]; + long lValue; +} TQuestTable; + +typedef struct SItemLimit +{ + BYTE bType; + long lValue; +} TItemLimit; + +typedef struct SItemApply +{ + BYTE bType; + long lValue; +} TItemApply; + +typedef struct SItemTable : public SEntityTable +{ + DWORD dwVnumRange; + char szName[ITEM_NAME_MAX_LEN + 1]; + char szLocaleName[ITEM_NAME_MAX_LEN + 1]; + BYTE bType; + BYTE bSubType; + + BYTE bWeight; + BYTE bSize; + + DWORD dwAntiFlags; + DWORD dwFlags; + DWORD dwWearFlags; + DWORD dwImmuneFlag; + + DWORD dwGold; + DWORD dwShopBuyPrice; + + TItemLimit aLimits[ITEM_LIMIT_MAX_NUM]; + TItemApply aApplies[ITEM_APPLY_MAX_NUM]; + long alValues[ITEM_VALUES_MAX_NUM]; + long alSockets[ITEM_SOCKET_MAX_NUM]; + DWORD dwRefinedVnum; + WORD wRefineSet; + BYTE bAlterToMagicItemPct; + BYTE bSpecular; + BYTE bGainSocketPct; + + short int sAddonType; + + char cLimitRealTimeFirstUseIndex; + char cLimitTimerBasedOnWearIndex; +} TItemTable; + +struct TItemAttrTable +{ + TItemAttrTable() : + dwApplyIndex(0), + dwProb(0) + { + szApply[0] = 0; + memset(&lValues, 0, sizeof(lValues)); + memset(&bMaxLevelBySet, 0, sizeof(bMaxLevelBySet)); + } + + char szApply[APPLY_NAME_MAX_LEN + 1]; + DWORD dwApplyIndex; + DWORD dwProb; + long lValues[ITEM_ATTRIBUTE_MAX_LEVEL]; + BYTE bMaxLevelBySet[ATTRIBUTE_SET_MAX_NUM]; +}; + +typedef struct SConnectTable +{ + char login[LOGIN_MAX_LEN + 1]; + IDENT ident; +} TConnectTable; + +typedef struct SLoginPacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; +} TLoginPacket; + +typedef struct SPlayerLoadPacket +{ + DWORD account_id; + DWORD player_id; + BYTE account_index; +} TPlayerLoadPacket; + +typedef struct SPlayerCreatePacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + DWORD account_id; + BYTE account_index; + TPlayerTable player_table; +} TPlayerCreatePacket; + +typedef struct SPlayerDeletePacket +{ + char login[LOGIN_MAX_LEN + 1]; + DWORD player_id; + BYTE account_index; + //char name[CHARACTER_NAME_MAX_LEN + 1]; + char private_code[8]; +} TPlayerDeletePacket; + +typedef struct SLogoutPacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; +} TLogoutPacket; + +typedef struct SPlayerCountPacket +{ + DWORD dwCount; +} TPlayerCountPacket; + +#define SAFEBOX_MAX_NUM 135 +#define SAFEBOX_PASSWORD_MAX_LEN 6 + +typedef struct SSafeboxTable +{ + DWORD dwID; + BYTE bSize; + DWORD dwGold; + WORD wItemCount; +} TSafeboxTable; + +typedef struct SSafeboxChangeSizePacket +{ + DWORD dwID; + BYTE bSize; +} TSafeboxChangeSizePacket; + +typedef struct SSafeboxLoadPacket +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN + 1]; + char szPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; +} TSafeboxLoadPacket; + +typedef struct SSafeboxChangePasswordPacket +{ + DWORD dwID; + char szOldPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; + char szNewPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; +} TSafeboxChangePasswordPacket; + +typedef struct SSafeboxChangePasswordPacketAnswer +{ + BYTE flag; +} TSafeboxChangePasswordPacketAnswer; + +typedef struct SEmpireSelectPacket +{ + DWORD dwAccountID; + BYTE bEmpire; +} TEmpireSelectPacket; + +typedef struct SPacketGDSetup +{ + char szPublicIP[16]; // Public IP which listen to users + BYTE bChannel; + WORD wListenPort; + WORD wP2PPort; + long alMaps[MAP_ALLOW_LIMIT]; + DWORD dwLoginCount; + BYTE bAuthServer; +} TPacketGDSetup; + +typedef struct SPacketDGMapLocations +{ + BYTE bCount; +} TPacketDGMapLocations; + +typedef struct SMapLocation +{ + long alMaps[MAP_ALLOW_LIMIT]; + char szHost[MAX_HOST_LENGTH + 1]; + WORD wPort; +#ifdef ENABLE_MOVE_CHANNEL + BYTE channel; +#endif +} TMapLocation; + +typedef struct SPacketDGP2P +{ + char szHost[MAX_HOST_LENGTH + 1]; + WORD wPort; + BYTE bChannel; +} TPacketDGP2P; + +typedef struct SPacketGDDirectEnter +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + BYTE index; +} TPacketGDDirectEnter; + +typedef struct SPacketDGDirectEnter +{ + TAccountTable accountTable; + TPlayerTable playerTable; +} TPacketDGDirectEnter; + +typedef struct SPacketGuildSkillUpdate +{ + DWORD guild_id; + int amount; + BYTE skill_levels[12]; + BYTE skill_point; + BYTE save; +} TPacketGuildSkillUpdate; + +typedef struct SPacketGuildExpUpdate +{ + DWORD guild_id; + int amount; +} TPacketGuildExpUpdate; + +typedef struct SPacketGuildChangeMemberData +{ + DWORD guild_id; + DWORD pid; + DWORD offer; + BYTE level; + BYTE grade; +} TPacketGuildChangeMemberData; + +typedef struct SPacketDGLoginAlready +{ + char szLogin[LOGIN_MAX_LEN + 1]; +} TPacketDGLoginAlready; + +typedef struct TPacketAffectElement +{ + DWORD dwType; + BYTE bApplyOn; + long lApplyValue; + DWORD dwFlag; + long lDuration; + long lSPCost; +} TPacketAffectElement; + +typedef struct SPacketGDAddAffect +{ + DWORD dwPID; + TPacketAffectElement elem; +} TPacketGDAddAffect; + +typedef struct SPacketGDRemoveAffect +{ + DWORD dwPID; + DWORD dwType; + BYTE bApplyOn; +} TPacketGDRemoveAffect; + +typedef struct SPacketPartyCreate +{ + DWORD dwLeaderPID; +} TPacketPartyCreate; + +typedef struct SPacketPartyDelete +{ + DWORD dwLeaderPID; +} TPacketPartyDelete; + +typedef struct SPacketPartyAdd +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bState; +} TPacketPartyAdd; + +typedef struct SPacketPartyRemove +{ + DWORD dwLeaderPID; + DWORD dwPID; +} TPacketPartyRemove; + +typedef struct SPacketPartyStateChange +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bRole; + BYTE bFlag; +} TPacketPartyStateChange; + +typedef struct SPacketPartySetMemberLevel +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bLevel; +} TPacketPartySetMemberLevel; + +typedef struct SPacketGDBoot +{ + DWORD dwItemIDRange[2]; + char szIP[16]; +} TPacketGDBoot; + +typedef struct SPacketGuild +{ + DWORD dwGuild; + DWORD dwInfo; +} TPacketGuild; + +typedef struct SPacketGDGuildAddMember +{ + DWORD dwPID; + DWORD dwGuild; + BYTE bGrade; +} TPacketGDGuildAddMember; + +typedef struct SPacketDGGuildMember +{ + DWORD dwPID; + DWORD dwGuild; + BYTE bGrade; + BYTE isGeneral; + BYTE bJob; + BYTE bLevel; + DWORD dwOffer; + char szName[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketDGGuildMember; + +typedef struct SPacketGuildWar +{ + BYTE bType; + BYTE bWar; + DWORD dwGuildFrom; + DWORD dwGuildTo; + long lWarPrice; + long lInitialScore; +} TPacketGuildWar; + +typedef struct SPacketGuildWarScore +{ + DWORD dwGuildGainPoint; + DWORD dwGuildOpponent; + long lScore; + long lBetScore; +} TPacketGuildWarScore; + +typedef struct SRefineMaterial +{ + DWORD vnum; + int count; +} TRefineMaterial; + +typedef struct SRefineTable +{ + //DWORD src_vnum; + //DWORD result_vnum; + DWORD id; + BYTE material_count; + int cost; + int prob; + TRefineMaterial materials[REFINE_MATERIAL_MAX_NUM]; +} TRefineTable; + +typedef struct SBanwordTable +{ + char szWord[BANWORD_MAX_LEN + 1]; +} TBanwordTable; + +typedef struct SPacketGDChangeName +{ + DWORD pid; + char name[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketGDChangeName; + +typedef struct SPacketDGChangeName +{ + DWORD pid; + char name[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketDGChangeName; + +typedef struct SPacketGuildLadder +{ + DWORD dwGuild; + long lLadderPoint; + long lWin; + long lDraw; + long lLoss; +} TPacketGuildLadder; + +typedef struct SPacketGuildLadderPoint +{ + DWORD dwGuild; + long lChange; +} TPacketGuildLadderPoint; + +typedef struct SPacketGuildUseSkill +{ + DWORD dwGuild; + DWORD dwSkillVnum; + DWORD dwCooltime; +} TPacketGuildUseSkill; + +typedef struct SPacketGuildSkillUsableChange +{ + DWORD dwGuild; + DWORD dwSkillVnum; + BYTE bUsable; +} TPacketGuildSkillUsableChange; + +typedef struct SPacketGDLoginKey +{ + DWORD dwAccountID; + DWORD dwLoginKey; +} TPacketGDLoginKey; + +typedef struct SPacketGDAuthLogin +{ + DWORD dwID; + DWORD dwLoginKey; + char szLogin[LOGIN_MAX_LEN + 1]; + char szSocialID[SOCIAL_ID_MAX_LEN + 1]; + DWORD adwClientKey[4]; + BYTE bBillType; + DWORD dwBillID; + int iPremiumTimes[PREMIUM_MAX_NUM]; +} TPacketGDAuthLogin; + +typedef struct SPacketGDLoginByKey +{ + char szLogin[LOGIN_MAX_LEN + 1]; + DWORD dwLoginKey; + DWORD adwClientKey[4]; + char szIP[MAX_HOST_LENGTH + 1]; +} TPacketGDLoginByKey; + +typedef struct SPacketGiveGuildPriv +{ + BYTE type; + int value; + DWORD guild_id; + time_t duration_sec; +} TPacketGiveGuildPriv; +typedef struct SPacketGiveEmpirePriv +{ + BYTE type; + int value; + BYTE empire; + time_t duration_sec; +} TPacketGiveEmpirePriv; +typedef struct SPacketGiveCharacterPriv +{ + BYTE type; + int value; + DWORD pid; +} TPacketGiveCharacterPriv; +typedef struct SPacketRemoveGuildPriv +{ + BYTE type; + DWORD guild_id; +} TPacketRemoveGuildPriv; +typedef struct SPacketRemoveEmpirePriv +{ + BYTE type; + BYTE empire; +} TPacketRemoveEmpirePriv; + +typedef struct SPacketDGChangeCharacterPriv +{ + BYTE type; + int value; + DWORD pid; + BYTE bLog; +} TPacketDGChangeCharacterPriv; + +typedef struct SPacketDGChangeGuildPriv +{ + BYTE type; + int value; + DWORD guild_id; + BYTE bLog; + time_t end_time_sec; +} TPacketDGChangeGuildPriv; + +typedef struct SPacketDGChangeEmpirePriv +{ + BYTE type; + int value; + BYTE empire; + BYTE bLog; + time_t end_time_sec; +} TPacketDGChangeEmpirePriv; + +typedef struct SPacketMoneyLog +{ + BYTE type; + DWORD vnum; + INT gold; +} TPacketMoneyLog; + +typedef struct SPacketGDGuildMoney +{ + DWORD dwGuild; + INT iGold; +} TPacketGDGuildMoney; + +typedef struct SPacketDGGuildMoneyChange +{ + DWORD dwGuild; + INT iTotalGold; +} TPacketDGGuildMoneyChange; + +typedef struct SPacketDGGuildMoneyWithdraw +{ + DWORD dwGuild; + INT iChangeGold; +} TPacketDGGuildMoneyWithdraw; + +typedef struct SPacketGDGuildMoneyWithdrawGiveReply +{ + DWORD dwGuild; + INT iChangeGold; + BYTE bGiveSuccess; +} TPacketGDGuildMoneyWithdrawGiveReply; + +typedef struct SPacketSetEventFlag +{ + char szFlagName[EVENT_FLAG_NAME_MAX_LEN + 1]; + long lValue; +} TPacketSetEventFlag; + +typedef struct SPacketBillingLogin +{ + DWORD dwLoginKey; + BYTE bLogin; +} TPacketBillingLogin; + +typedef struct SPacketBillingRepair +{ + DWORD dwLoginKey; + char szLogin[LOGIN_MAX_LEN + 1]; + char szHost[MAX_HOST_LENGTH + 1]; +} TPacketBillingRepair; + +typedef struct SPacketBillingExpire +{ + char szLogin[LOGIN_MAX_LEN + 1]; + BYTE bBillType; + DWORD dwRemainSeconds; +} TPacketBillingExpire; + +typedef struct SPacketLoginOnSetup +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN + 1]; + char szSocialID[SOCIAL_ID_MAX_LEN + 1]; + char szHost[MAX_HOST_LENGTH + 1]; + DWORD dwLoginKey; + DWORD adwClientKey[4]; +} TPacketLoginOnSetup; + +typedef struct SPacketGDCreateObject +{ + DWORD dwVnum; + DWORD dwLandID; + INT lMapIndex; + INT x, y; + float xRot; + float yRot; + float zRot; +} TPacketGDCreateObject; + +typedef struct SGuildReserve +{ + DWORD dwID; + DWORD dwGuildFrom; + DWORD dwGuildTo; + DWORD dwTime; + BYTE bType; + long lWarPrice; + long lInitialScore; + bool bStarted; + DWORD dwBetFrom; + DWORD dwBetTo; + long lPowerFrom; + long lPowerTo; + long lHandicap; +} TGuildWarReserve; + +typedef struct SPacketGDGuildWarBet +{ + DWORD dwWarID; + char szLogin[LOGIN_MAX_LEN + 1]; + DWORD dwGold; + DWORD dwGuild; +} TPacketGDGuildWarBet; + +// Marriage + +typedef struct SPacketMarriageAdd +{ + DWORD dwPID1; + DWORD dwPID2; + time_t tMarryTime; + char szName1[CHARACTER_NAME_MAX_LEN + 1]; + char szName2[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketMarriageAdd; + +typedef struct SPacketMarriageUpdate +{ + DWORD dwPID1; + DWORD dwPID2; + INT iLovePoint; + BYTE byMarried; +} TPacketMarriageUpdate; + +typedef struct SPacketMarriageRemove +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketMarriageRemove; + +typedef struct SPacketWeddingRequest +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingRequest; + +typedef struct SPacketWeddingReady +{ + DWORD dwPID1; + DWORD dwPID2; + DWORD dwMapIndex; +} TPacketWeddingReady; + +typedef struct SPacketWeddingStart +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingStart; + +typedef struct SPacketWeddingEnd +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingEnd; + +typedef struct SPacketMyshopPricelistHeader +{ + DWORD dwOwnerID; + BYTE byCount; +} TPacketMyshopPricelistHeader; + +typedef struct SItemPriceInfo +{ + DWORD dwVnum; + DWORD dwPrice; +} TItemPriceInfo; + +typedef struct SItemPriceListTable +{ + DWORD dwOwnerID; + BYTE byCount; + + TItemPriceInfo aPriceInfo[SHOP_PRICELIST_MAX_NUM]; +} TItemPriceListTable; + +typedef struct SPacketBlockChat +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + long lDuration; +} TPacketBlockChat; + +//ADMIN_MANAGER +typedef struct TAdminInfo +{ + int m_ID; + char m_szAccount[32]; + char m_szName[32]; + char m_szContactIP[16]; + char m_szServerIP[16]; + int m_Authority; +} tAdminInfo; +//END_ADMIN_MANAGER + +//BOOT_LOCALIZATION +struct tLocale +{ + char szValue[32]; + char szKey[32]; +}; +//BOOT_LOCALIZATION + +//RELOAD_ADMIN +typedef struct SPacketReloadAdmin +{ + char szIP[16]; +} TPacketReloadAdmin; +//END_RELOAD_ADMIN + +typedef struct TMonarchInfo +{ + DWORD pid[4]; + int64_t money[4]; + char name[4][32]; + char date[4][32]; +} MonarchInfo; + +typedef struct TMonarchElectionInfo +{ + DWORD pid; + DWORD selectedpid; + char date[32]; +} MonarchElectionInfo; + +typedef struct tMonarchCandidacy +{ + DWORD pid; + char name[32]; + char date[32]; +} MonarchCandidacy; + +typedef struct tChangeMonarchLord +{ + BYTE bEmpire; + DWORD dwPID; +} TPacketChangeMonarchLord; + +typedef struct tChangeMonarchLordACK +{ + BYTE bEmpire; + DWORD dwPID; + char szName[32]; + char szDate[32]; +} TPacketChangeMonarchLordACK; + +typedef struct tChangeGuildMaster +{ + DWORD dwGuildID; + DWORD idFrom; + DWORD idTo; +} TPacketChangeGuildMaster; + +typedef struct tItemIDRange +{ + DWORD dwMin; + DWORD dwMax; + DWORD dwUsableItemIDMin; +} TItemIDRangeTable; + +typedef struct tUpdateHorseName +{ + DWORD dwPlayerID; + char szHorseName[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketUpdateHorseName; + +typedef struct tDC +{ + char login[LOGIN_MAX_LEN + 1]; +} TPacketDC; + +typedef struct tNeedLoginLogInfo +{ + DWORD dwPlayerID; +} TPacketNeedLoginLogInfo; + +typedef struct tItemAwardInformer +{ + char login[LOGIN_MAX_LEN + 1]; + char command[20]; + unsigned int vnum; +} TPacketItemAwardInfromer; + +typedef struct tDeleteAwardID +{ + DWORD dwID; +} TPacketDeleteAwardID; + +typedef struct SChannelStatus +{ + WORD nPort; // backward change for client @fixme024 + BYTE bStatus; +} TChannelStatus; + +#pragma pack() +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/common/utils.h b/source-server/Srcs/Server/common/utils.h new file mode 100644 index 000000000..2ded3351d --- /dev/null +++ b/source-server/Srcs/Server/common/utils.h @@ -0,0 +1,122 @@ +#ifndef _INCLUDE_HEADER_COMMON_UTILS_ +#define _INCLUDE_HEADER_COMMON_UTILS_ + +#include + +/*----- atoi function -----*/ +inline bool str_to_number (bool& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (strtol(in, NULL, 10) != 0); + return true; +} + +inline bool str_to_number (char& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (char) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned char& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned char) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (short& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (short) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned short& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned short) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (int& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (int) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned int& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned int) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned long) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (long long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long long) strtoull(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned long long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned long long) strtoull(in, NULL, 10); + return true; +} + +inline bool str_to_number (float& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (float) strtof(in, NULL); + return true; +} + +inline bool str_to_number (double& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (double) strtod(in, NULL); + return true; +} + +#ifdef __FreeBSD__ +inline bool str_to_number (long double& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long double) strtold(in, NULL); + return true; +} +#endif + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Cache.cpp b/source-server/Srcs/Server/db/src/Cache.cpp new file mode 100644 index 000000000..309e1297b --- /dev/null +++ b/source-server/Srcs/Server/db/src/Cache.cpp @@ -0,0 +1,204 @@ +#include "stdafx.h" +#include "Cache.h" + +#include "QID.h" +#include "ClientManager.h" +#include "Main.h" +#include + +extern CPacketInfo g_item_info; +extern int g_iPlayerCacheFlushSeconds; +extern int g_iItemCacheFlushSeconds; +extern int g_test_server; +// MYSHOP_PRICE_LIST +extern int g_iItemPriceListTableCacheFlushSeconds; +// END_OF_MYSHOP_PRICE_LIST +// +extern int g_item_count; + +CItemCache::CItemCache() +{ + m_expireTime = MIN(1800, g_iItemCacheFlushSeconds); +} + +CItemCache::~CItemCache() +{ +} + +void CItemCache::Delete() +{ + if (m_data.vnum == 0) + return; + + if (g_test_server) + sys_log(0, "ItemCache::Delete : DELETE %u", m_data.id); + + m_data.vnum = 0; + m_bNeedQuery = true; + m_lastUpdateTime = time(0); + OnFlush(); +} + +void CItemCache::OnFlush() +{ + if (m_data.vnum == 0) + { + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM item%s WHERE id=%u", GetTablePostfix(), m_data.id); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, 0, NULL); + + if (g_test_server) + sys_log(0, "ItemCache::Flush : DELETE %u %s", m_data.id, szQuery); + } + else + { + TPlayerItem *p = &m_data; + const auto setQuery = fmt::format(FMT_COMPILE("id={}, owner_id={}, `window`={}, pos={}, count={}, vnum={}, socket0={}, socket1={}, socket2={}, " + "attrtype0={}, attrvalue0={}, " + "attrtype1={}, attrvalue1={}, " + "attrtype2={}, attrvalue2={}, " + "attrtype3={}, attrvalue3={}, " + "attrtype4={}, attrvalue4={}, " + "attrtype5={}, attrvalue5={}, " + "attrtype6={}, attrvalue6={} ") + , p->id, + p->owner, + p->window, + p->pos, + p->count, + p->vnum, + p->alSockets[0], + p->alSockets[1], + p->alSockets[2], + p->aAttr[0].bType, p->aAttr[0].sValue, + p->aAttr[1].bType, p->aAttr[1].sValue, + p->aAttr[2].bType, p->aAttr[2].sValue, + p->aAttr[3].bType, p->aAttr[3].sValue, + p->aAttr[4].bType, p->aAttr[4].sValue, + p->aAttr[5].bType, p->aAttr[5].sValue, + p->aAttr[6].bType, p->aAttr[6].sValue + ); // @fixme205 + + const auto itemQuery = fmt::format(FMT_COMPILE("INSERT INTO item{} SET {} ON DUPLICATE KEY UPDATE {}"), + GetTablePostfix(), setQuery, setQuery); + + if (g_test_server) + sys_log(0, "ItemCache::Flush :REPLACE (%s)", itemQuery.c_str()); + + CDBManager::instance().ReturnQuery(itemQuery.c_str(), QID_ITEM_SAVE, 0, NULL); + + ++g_item_count; + } + + m_bNeedQuery = false; +} + +// +// CPlayerTableCache +// +CPlayerTableCache::CPlayerTableCache() +{ + m_expireTime = MIN(1800, g_iPlayerCacheFlushSeconds); +} + +CPlayerTableCache::~CPlayerTableCache() +{ +} + +void CPlayerTableCache::OnFlush() +{ + if (g_test_server) + sys_log(0, "PlayerTableCache::Flush : %s", m_data.name); + + char szQuery[QUERY_MAX_LEN]; + CreatePlayerSaveQuery(szQuery, sizeof(szQuery), &m_data); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_SAVE, 0, NULL); +} + +// MYSHOP_PRICE_LIST +// +// CItemPriceListTableCache class implementation +// + +const int CItemPriceListTableCache::s_nMinFlushSec = 1800; + +CItemPriceListTableCache::CItemPriceListTableCache() +{ + m_expireTime = MIN(s_nMinFlushSec, g_iItemPriceListTableCacheFlushSeconds); +} + +void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList) +{ + std::vector tmpvec; + + for (uint idx = 0; idx < m_data.byCount; ++idx) + { + const TItemPriceInfo* pos = pUpdateList->aPriceInfo; + for (; pos != pUpdateList->aPriceInfo + pUpdateList->byCount && m_data.aPriceInfo[idx].dwVnum != pos->dwVnum; ++pos) + ; + + if (pos == pUpdateList->aPriceInfo + pUpdateList->byCount) + tmpvec.emplace_back(m_data.aPriceInfo[idx]); + } + + if (pUpdateList->byCount > SHOP_PRICELIST_MAX_NUM) + { + sys_err("Count overflow!"); + return; + } + + m_data.byCount = pUpdateList->byCount; + + thecore_memcpy(m_data.aPriceInfo, pUpdateList->aPriceInfo, sizeof(TItemPriceInfo) * pUpdateList->byCount); + + int nDeletedNum; + + if (pUpdateList->byCount < SHOP_PRICELIST_MAX_NUM) + { + size_t sizeAddOldDataSize = SHOP_PRICELIST_MAX_NUM - pUpdateList->byCount; + + if (tmpvec.size() < sizeAddOldDataSize) + sizeAddOldDataSize = tmpvec.size(); + if (tmpvec.size() != 0) + { + thecore_memcpy(m_data.aPriceInfo + pUpdateList->byCount, &tmpvec[0], sizeof(TItemPriceInfo) * sizeAddOldDataSize); + m_data.byCount += sizeAddOldDataSize; + } + nDeletedNum = tmpvec.size() - sizeAddOldDataSize; + } + else + nDeletedNum = tmpvec.size(); + + m_bNeedQuery = true; + + sys_log(0, + "ItemPriceListTableCache::UpdateList : OwnerID[%u] Update [%u] Items, Delete [%u] Items, Total [%u] Items", + m_data.dwOwnerID, pUpdateList->byCount, nDeletedNum, m_data.byCount); +} + +void CItemPriceListTableCache::OnFlush() +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM myshop_pricelist%s WHERE owner_id = %u", GetTablePostfix(), m_data.dwOwnerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_DESTROY, 0, NULL); + + for (int idx = 0; idx < m_data.byCount; ++idx) + { + snprintf(szQuery, sizeof(szQuery), + "REPLACE myshop_pricelist%s(owner_id, item_vnum, price) VALUES(%u, %u, %u)", // @fixme204 (INSERT INTO -> REPLACE) + GetTablePostfix(), m_data.dwOwnerID, m_data.aPriceInfo[idx].dwVnum, m_data.aPriceInfo[idx].dwPrice); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_SAVE, 0, NULL); + } + + sys_log(0, "ItemPriceListTableCache::Flush : OwnerID[%u] Update [%u]Items", m_data.dwOwnerID, m_data.byCount); + + m_bNeedQuery = false; +} + +CItemPriceListTableCache::~CItemPriceListTableCache() +{ +} + +// END_OF_MYSHOP_PRICE_LIST +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Cache.h b/source-server/Srcs/Server/db/src/Cache.h new file mode 100644 index 000000000..06ea3bc82 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Cache.h @@ -0,0 +1,49 @@ +// vim:ts=8 sw=4 +#ifndef __INC_DB_CACHE_H__ +#define __INC_DB_CACHE_H__ + +#include "../../common/cache.h" + +class CItemCache : public cache +{ + public: + CItemCache(); + virtual ~CItemCache(); + + void Delete(); + virtual void OnFlush(); +}; + +class CPlayerTableCache : public cache +{ + public: + CPlayerTableCache(); + virtual ~CPlayerTableCache(); + + virtual void OnFlush(); + + DWORD GetLastUpdateTime() { return m_lastUpdateTime; } +}; + +// MYSHOP_PRICE_LIST + +class CItemPriceListTableCache : public cache< TItemPriceListTable > +{ + public: + + /// Constructor + + CItemPriceListTableCache(void); + virtual ~CItemPriceListTableCache(); + + void UpdateList(const TItemPriceListTable* pUpdateList); + + virtual void OnFlush(void); + + private: + + static const int s_nMinFlushSec; ///< Minimum cache expire time +}; +// END_OF_MYSHOP_PRICE_LIST +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManager.cpp b/source-server/Srcs/Server/db/src/ClientManager.cpp new file mode 100644 index 000000000..9df048e8f --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManager.cpp @@ -0,0 +1,4238 @@ +#include "stdafx.h" +#include "../../common/CommonDefines.h" + +#include "../../common/billing.h" +#include "../../common/building.h" +#include "../../common/VnumHelper.h" +#include "../../libgame/include/grid.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" +#include "GuildManager.h" +#include "PrivManager.h" +#include "MoneyLog.h" +#include "ItemAwardManager.h" +#include "Marriage.h" +#include "Monarch.h" +#include "ItemIDRangeManager.h" +#include "Cache.h" +#include +#include +extern int g_iPlayerCacheFlushSeconds; +extern int g_iItemCacheFlushSeconds; +extern int g_test_server; +extern int g_log; +extern std::string g_stLocale; +extern std::string g_stLocaleNameColumn; +bool CreateItemTableFromRes(MYSQL_RES * res, std::vector * pVec, DWORD dwPID); + +DWORD g_dwUsageMax = 0; +DWORD g_dwUsageAvg = 0; + +CPacketInfo g_query_info; +CPacketInfo g_item_info; + +int g_item_count = 0; +int g_query_count[2]; +#ifdef ENABLE_PROTO_FROM_DB +bool g_bMirror2DB = false; +#endif + +CClientManager::CClientManager() : + m_pkAuthPeer(NULL), + m_iPlayerIDStart(0), + m_iPlayerDeleteLevelLimit(0), + m_iPlayerDeleteLevelLimitLower(0), + m_bChinaEventServer(false), + m_iShopTableSize(0), + m_pShopTable(NULL), + m_iRefineTableSize(0), + m_pRefineTable(NULL), + m_bShutdowned(FALSE), + m_iCacheFlushCount(0), + m_iCacheFlushCountLimit(200) +{ + m_itemRange.dwMin = 0; + m_itemRange.dwMax = 0; + m_itemRange.dwUsableItemIDMin = 0; + + memset(g_query_count, 0, sizeof(g_query_count)); +#ifdef ENABLE_PROTO_FROM_DB + bIsProtoReadFromDB = false; +#endif +} + +CClientManager::~CClientManager() +{ + Destroy(); +} + +void CClientManager::SetPlayerIDStart(int iIDStart) +{ + m_iPlayerIDStart = iIDStart; +} + +void CClientManager::GetPeerP2PHostNames(std::string& peerHostNames) +{ + std::ostringstream oss(std::ostringstream::out); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * peer = *it; + oss << peer->GetHost() << " " << peer->GetP2PPort() << " channel : " << (int)(peer->GetChannel()) << "\n"; + } + + peerHostNames += oss.str(); +} + +void CClientManager::Destroy() +{ + m_mChannelStatus.clear(); + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end(); ++i) + (*i)->Destroy(); + + m_peerList.clear(); + + if (m_fdAccept > 0) + { + socket_close(m_fdAccept); + m_fdAccept = -1; + } +} + +#define ENABLE_DEFAULT_PRIV +#ifdef ENABLE_DEFAULT_PRIV +static bool bCleanOldPriv = true; +static bool __InitializeDefaultPriv() +{ + if (bCleanOldPriv) + { + auto pCleanStuff(CDBManager::instance().DirectQuery("DELETE FROM priv_settings WHERE value <= 0 OR duration <= NOW();", SQL_COMMON)); + printf("DEFAULT_PRIV_EMPIRE: removed %u expired priv settings.\n", pCleanStuff->Get()->uiAffectedRows); + } + auto pMsg(CDBManager::instance().DirectQuery("SELECT priv_type, id, type, value, UNIX_TIMESTAMP(duration) FROM priv_settings", SQL_COMMON)); + if (pMsg->Get()->uiNumRows == 0) + return false; + MYSQL_ROW row = NULL; + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + if (!strcmp(row[0], "EMPIRE")) + { + // init + BYTE empire = 0; + BYTE type = 1; + int value = 0; + time_t duration_sec = 0; + // set + str_to_number(empire, row[1]); + str_to_number(type, row[2]); + str_to_number(value, row[3]); + str_to_number(duration_sec, row[4]); + // recalibrate time + time_t now_time_sec = CClientManager::instance().GetCurrentTime(); + if (now_time_sec>duration_sec) + duration_sec = 0; + else + duration_sec -= now_time_sec; + // send priv + printf("DEFAULT_PRIV_EMPIRE: set empire(%u), type(%u), value(%d), duration(%u)\n", empire, type, value, duration_sec); + CPrivManager::instance().AddEmpirePriv(empire, type, value, duration_sec); + } + else if (!strcmp(row[0], "GUILD")) + { + // init + DWORD guild_id = 0; + BYTE type = 1; + int value = 0; + time_t duration_sec = 0; + // set + str_to_number(guild_id, row[1]); + str_to_number(type, row[2]); + str_to_number(value, row[3]); + str_to_number(duration_sec, row[4]); + // recalibrate time + time_t now_time_sec = CClientManager::instance().GetCurrentTime(); + if (now_time_sec>duration_sec) + duration_sec = 0; + else + duration_sec -= now_time_sec; + // send priv + if (guild_id) + { + printf("DEFAULT_PRIV_GUILD: set guild_id(%u), type(%u), value(%d), duration(%u)\n", guild_id, type, value, duration_sec); + CPrivManager::instance().AddGuildPriv(guild_id, type, value, duration_sec); + } + } + else if (!strcmp(row[0], "PLAYER")) + { + // init + DWORD pid = 0; + BYTE type = 1; + int value = 0; + // set + str_to_number(pid, row[1]); + str_to_number(type, row[2]); + str_to_number(value, row[3]); + // send priv + if (pid) + { + printf("DEFAULT_PRIV_PLAYER: set pid(%u), type(%u), value(%d)\n", pid, type, value); + CPrivManager::instance().AddCharPriv(pid, type, value); + } + } + } + return true; +} + +static bool __UpdateDefaultPriv(const char* priv_type, DWORD id, BYTE type, int value, time_t duration_sec) +{ + char szQuery[1024]; + snprintf(szQuery, 1024, + "REPLACE INTO priv_settings SET priv_type='%s', id=%u, type=%u, value=%d, duration=DATE_ADD(NOW(), INTERVAL %u SECOND);", + priv_type, id, type, value, duration_sec + ); + auto pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_COMMON)); + return pMsg->Get()->uiAffectedRows; +} +#endif + +bool CClientManager::Initialize() +{ + int tmpValue; + + //BOOT_LOCALIZATION + if (!InitializeLocalization()) + { + fprintf(stderr, "Failed Localization Infomation so exit\n"); + return false; + } +#ifdef ENABLE_DEFAULT_PRIV + if (!__InitializeDefaultPriv()) + { + // fprintf(stderr, "Failed Default Priv Setting so exit\n"); + // return false; + } +#endif + //END_BOOT_LOCALIZATION + //ITEM_UNIQUE_ID + + if (!InitializeNowItemID()) + { + fprintf(stderr, " Item range Initialize Failed. Exit DBCache Server\n"); + return false; + } + //END_ITEM_UNIQUE_ID + +#ifdef ENABLE_PROTO_FROM_DB + int iTemp; + if (CConfig::instance().GetValue("PROTO_FROM_DB", &iTemp)) + { + bIsProtoReadFromDB = !!iTemp; + fprintf(stdout, "PROTO_FROM_DB: %s\n", (bIsProtoReadFromDB)?"Enabled":"Disabled"); + } + if (!bIsProtoReadFromDB && CConfig::instance().GetValue("MIRROR2DB", &iTemp)) + { + g_bMirror2DB = !!iTemp; + fprintf(stdout, "MIRROR2DB: %s\n", (g_bMirror2DB)?"Enabled":"Disabled"); + } +#endif + if (!InitializeTables()) + { + sys_err("Table Initialize FAILED"); + return false; + } + + CGuildManager::instance().BootReserveWar(); + + if (!CConfig::instance().GetValue("BIND_PORT", &tmpValue)) + tmpValue = 5300; + + char szBindIP[128]; + + if (!CConfig::instance().GetValue("BIND_IP", szBindIP, 128)) + strlcpy(szBindIP, "0", sizeof(szBindIP)); + + m_fdAccept = socket_tcp_bind(szBindIP, tmpValue); + + if (m_fdAccept < 0) + { + perror("socket"); + return false; + } + + sys_log(0, "ACCEPT_HANDLE: %u", m_fdAccept); + fdwatch_add_fd(m_fdWatcher, m_fdAccept, NULL, FDW_READ, false); + + if (!CConfig::instance().GetValue("BACKUP_LIMIT_SEC", &tmpValue)) + tmpValue = 600; + + m_looping = true; + + if (!CConfig::instance().GetValue("PLAYER_DELETE_LEVEL_LIMIT", &m_iPlayerDeleteLevelLimit)) + { + sys_err("conf.txt: Cannot find PLAYER_DELETE_LEVEL_LIMIT, use default level %d", PLAYER_MAX_LEVEL_CONST + 1); + m_iPlayerDeleteLevelLimit = PLAYER_MAX_LEVEL_CONST + 1; + } + + if (!CConfig::instance().GetValue("PLAYER_DELETE_LEVEL_LIMIT_LOWER", &m_iPlayerDeleteLevelLimitLower)) + { + m_iPlayerDeleteLevelLimitLower = 0; + } + + sys_log(0, "PLAYER_DELETE_LEVEL_LIMIT set to %d", m_iPlayerDeleteLevelLimit); + sys_log(0, "PLAYER_DELETE_LEVEL_LIMIT_LOWER set to %d", m_iPlayerDeleteLevelLimitLower); + + m_bChinaEventServer = false; + + int iChinaEventServer = 0; + + if (CConfig::instance().GetValue("CHINA_EVENT_SERVER", &iChinaEventServer)) + m_bChinaEventServer = (iChinaEventServer); + + sys_log(0, "CHINA_EVENT_SERVER %s", CClientManager::instance().IsChinaEventServer()?"true":"false"); + + LoadEventFlag(); + + if (g_stLocale == "big5" || g_stLocale == "sjis") + CDBManager::instance().QueryLocaleSet(); + + return true; +} + +void CClientManager::MainLoop() +{ + SQLMsg * tmp; + + sys_log(0, "ClientManager pointer is %p", this); + + while (!m_bShutdowned) + { + while ((tmp = CDBManager::instance().PopResult())) + { + AnalyzeQueryResult(tmp); + delete tmp; + } + + if (!Process()) + break; + + log_rotate(); + } + + sys_log(0, "MainLoop exited, Starting cache flushing"); + + signal_timer_disable(); + + itertype(m_map_playerCache) it = m_map_playerCache.begin(); + + while (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = (it++)->second; + + c->Flush(); + delete c; + } + m_map_playerCache.clear(); + + itertype(m_map_itemCache) it2 = m_map_itemCache.begin(); + + while (it2 != m_map_itemCache.end()) + { + CItemCache * c = (it2++)->second; + + c->Flush(); + delete c; + } + m_map_itemCache.clear(); + + // MYSHOP_PRICE_LIST + + for (itertype(m_mapItemPriceListCache) itPriceList = m_mapItemPriceListCache.begin(); itPriceList != m_mapItemPriceListCache.end(); ++itPriceList) + { + CItemPriceListTableCache* pCache = itPriceList->second; + pCache->Flush(); + delete pCache; + } + + m_mapItemPriceListCache.clear(); + // END_OF_MYSHOP_PRICE_LIST +} + +void CClientManager::Quit() +{ + m_bShutdowned = TRUE; +} + +void CClientManager::QUERY_BOOT(CPeer* peer, TPacketGDBoot * p) +{ + const BYTE bPacketVersion = 6; + + std::vector vAdmin; + std::vector vHost; + + __GetHostInfo(vHost); + __GetAdminInfo(p->szIP, vAdmin); + + sys_log(0, "QUERY_BOOT : AdminInfo (Request ServerIp %s) ", p->szIP); + + DWORD dwPacketSize = + sizeof(DWORD) + + sizeof(BYTE) + + sizeof(WORD) + sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TShopTable) * m_iShopTableSize + + sizeof(WORD) + sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TRefineTable) * m_iRefineTableSize + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemAttrTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemRareTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TLand) * m_vec_kLandTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TObjectProto) * m_vec_kObjectProto.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TObject) * m_map_pkObjectTable.size() + + sizeof(time_t) + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemIDRangeTable)*2 + + //ADMIN_MANAGER + sizeof(WORD) + sizeof(WORD) + 16 * vHost.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(tAdminInfo) * vAdmin.size() + + //END_ADMIN_MANAGER + sizeof(WORD) + sizeof(WORD) + sizeof(TMonarchInfo) + + sizeof(WORD) + sizeof(WORD) + sizeof(MonarchCandidacy)* CMonarch::instance().MonarchCandidacySize() + + sizeof(WORD); + + peer->EncodeHeader(HEADER_DG_BOOT, 0, dwPacketSize); + peer->Encode(dwPacketSize); + peer->Encode(bPacketVersion); + + sys_log(0, "BOOT: PACKET: %d", dwPacketSize); + sys_log(0, "BOOT: VERSION: %d", bPacketVersion); + + sys_log(0, "sizeof(TMobTable) = %d", sizeof(TMobTable)); + sys_log(0, "sizeof(TItemTable) = %d", sizeof(TItemTable)); + sys_log(0, "sizeof(TShopTable) = %d", sizeof(TShopTable)); + sys_log(0, "sizeof(TSkillTable) = %d", sizeof(TSkillTable)); + sys_log(0, "sizeof(TRefineTable) = %d", sizeof(TRefineTable)); + sys_log(0, "sizeof(TItemAttrTable) = %d", sizeof(TItemAttrTable)); + sys_log(0, "sizeof(TItemRareTable) = %d", sizeof(TItemAttrTable)); + sys_log(0, "sizeof(TBanwordTable) = %d", sizeof(TBanwordTable)); + sys_log(0, "sizeof(TLand) = %d", sizeof(building::TLand)); + sys_log(0, "sizeof(TObjectProto) = %d", sizeof(building::TObjectProto)); + sys_log(0, "sizeof(TObject) = %d", sizeof(building::TObject)); + //ADMIN_MANAGER + sys_log(0, "sizeof(tAdminInfo) = %d * %d ", sizeof(tAdminInfo) * vAdmin.size()); + //END_ADMIN_MANAGER + sys_log(0, "sizeof(TMonarchInfo) = %d * %d", sizeof(TMonarchInfo)); + + peer->EncodeWORD(sizeof(TMobTable)); + peer->EncodeWORD(m_vec_mobTable.size()); + peer->Encode(m_vec_mobTable); + + peer->EncodeWORD(sizeof(TItemTable)); + peer->EncodeWORD(m_vec_itemTable.size()); + peer->Encode(m_vec_itemTable); + + peer->EncodeWORD(sizeof(TShopTable)); + peer->EncodeWORD(m_iShopTableSize); + peer->Encode(m_pShopTable, sizeof(TShopTable) * m_iShopTableSize); + + peer->EncodeWORD(sizeof(TSkillTable)); + peer->EncodeWORD(m_vec_skillTable.size()); + peer->Encode(m_vec_skillTable); + + peer->EncodeWORD(sizeof(TRefineTable)); + peer->EncodeWORD(m_iRefineTableSize); + peer->Encode(m_pRefineTable, sizeof(TRefineTable) * m_iRefineTableSize); + + peer->EncodeWORD(sizeof(TItemAttrTable)); + peer->EncodeWORD(m_vec_itemAttrTable.size()); + peer->Encode(m_vec_itemAttrTable); + + peer->EncodeWORD(sizeof(TItemAttrTable)); + peer->EncodeWORD(m_vec_itemRareTable.size()); + peer->Encode(m_vec_itemRareTable); + + peer->EncodeWORD(sizeof(TBanwordTable)); + peer->EncodeWORD(m_vec_banwordTable.size()); + peer->Encode(m_vec_banwordTable); + + peer->EncodeWORD(sizeof(building::TLand)); + peer->EncodeWORD(m_vec_kLandTable.size()); + peer->Encode(m_vec_kLandTable); + + peer->EncodeWORD(sizeof(building::TObjectProto)); + peer->EncodeWORD(m_vec_kObjectProto.size()); + peer->Encode(m_vec_kObjectProto); + + peer->EncodeWORD(sizeof(building::TObject)); + peer->EncodeWORD(m_map_pkObjectTable.size()); + + itertype(m_map_pkObjectTable) it = m_map_pkObjectTable.begin(); + + while (it != m_map_pkObjectTable.end()) + peer->Encode((it++)->second, sizeof(building::TObject)); + + time_t now = time(0); + peer->Encode(now); + + TItemIDRangeTable itemRange = CItemIDRangeManager::instance().GetRange(); + TItemIDRangeTable itemRangeSpare = CItemIDRangeManager::instance().GetRange(); + + peer->EncodeWORD(sizeof(TItemIDRangeTable)); + peer->EncodeWORD(1); + peer->Encode(itemRange); + peer->Encode(itemRangeSpare); + + peer->SetItemIDRange(itemRange); + peer->SetSpareItemIDRange(itemRangeSpare); + + //ADMIN_MANAGER + peer->EncodeWORD(16); + peer->EncodeWORD(vHost.size()); + + for (size_t n = 0; n < vHost.size(); ++n) + { + peer->Encode(vHost[n].c_str(), 16); + sys_log(0, "GMHosts %s", vHost[n].c_str()); + } + + peer->EncodeWORD(sizeof(tAdminInfo)); + peer->EncodeWORD(vAdmin.size()); + + for (size_t n = 0; n < vAdmin.size(); ++n) + { + peer->Encode(vAdmin[n]); + sys_log(0, "Admin name %s ConntactIP %s", vAdmin[n].m_szName, vAdmin[n].m_szContactIP); + } + //END_ADMIN_MANAGER + + //MONARCH + peer->EncodeWORD(sizeof(TMonarchInfo)); + peer->EncodeWORD(1); + peer->Encode(CMonarch::instance().GetMonarch(), sizeof(TMonarchInfo)); + + CMonarch::VEC_MONARCHCANDIDACY & rVecMonarchCandidacy = CMonarch::instance().GetVecMonarchCandidacy(); + + size_t num_monarch_candidacy = CMonarch::instance().MonarchCandidacySize(); + peer->EncodeWORD(sizeof(MonarchCandidacy)); + peer->EncodeWORD(num_monarch_candidacy); + if (num_monarch_candidacy) + peer->Encode(rVecMonarchCandidacy); + //END_MONARCE + + if (g_test_server) + sys_log(0, "MONARCHCandidacy Size %d", CMonarch::instance().MonarchCandidacySize()); + + peer->EncodeWORD(0xffff); +} + +void CClientManager::SendPartyOnSetup(CPeer* pkPeer) +{ + TPartyMap & pm = m_map_pkChannelParty[pkPeer->GetChannel()]; + + for (itertype(pm) it_party = pm.begin(); it_party != pm.end(); ++it_party) + { + sys_log(0, "PARTY SendPartyOnSetup Party [%u]", it_party->first); + pkPeer->EncodeHeader(HEADER_DG_PARTY_CREATE, 0, sizeof(TPacketPartyCreate)); + pkPeer->Encode(it_party->first); + + for (itertype(it_party->second) it_member = it_party->second.begin(); it_member != it_party->second.end(); ++it_member) + { + sys_log(0, "PARTY SendPartyOnSetup Party [%u] Member [%u]", it_party->first, it_member->first); + pkPeer->EncodeHeader(HEADER_DG_PARTY_ADD, 0, sizeof(TPacketPartyAdd)); + pkPeer->Encode(it_party->first); + pkPeer->Encode(it_member->first); + pkPeer->Encode(it_member->second.bRole); + + pkPeer->EncodeHeader(HEADER_DG_PARTY_SET_MEMBER_LEVEL, 0, sizeof(TPacketPartySetMemberLevel)); + pkPeer->Encode(it_party->first); + pkPeer->Encode(it_member->first); + pkPeer->Encode(it_member->second.bLevel); + } + } +} + +void CClientManager::QUERY_PLAYER_COUNT(CPeer * pkPeer, TPlayerCountPacket * pPacket) +{ + pkPeer->SetUserCount(pPacket->dwCount); +} + +void CClientManager::QUERY_QUEST_SAVE(CPeer * pkPeer, TQuestTable * pTable, DWORD dwLen) +{ + if (0 != (dwLen % sizeof(TQuestTable))) + { + sys_err("invalid packet size %d, sizeof(TQuestTable) == %d", dwLen, sizeof(TQuestTable)); + return; + } + + int iSize = dwLen / sizeof(TQuestTable); + + char szQuery[1024]; + + for (int i = 0; i < iSize; ++i, ++pTable) + { + if (pTable->lValue == 0) + { + snprintf(szQuery, sizeof(szQuery), + "DELETE FROM quest%s WHERE dwPID=%d AND szName='%s' AND szState='%s'", + GetTablePostfix(), pTable->dwPID, pTable->szName, pTable->szState); + } + else + { + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(%d, '%s', '%s', %ld)", + GetTablePostfix(), pTable->dwPID, pTable->szName, pTable->szState, pTable->lValue); + } + + CDBManager::instance().ReturnQuery(szQuery, QID_QUEST_SAVE, pkPeer->GetHandle(), NULL); + } +} + +void CClientManager::QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafeboxLoadPacket * packet, bool bMall) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + strlcpy(pi->safebox_password, packet->szPassword, sizeof(pi->safebox_password)); + pi->account_id = packet->dwID; + pi->account_index = 0; + pi->ip[0] = bMall ? 1 : 0; + strlcpy(pi->login, packet->szLogin, sizeof(pi->login)); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), + "SELECT account_id, size, password FROM safebox%s WHERE account_id=%u", + GetTablePostfix(), packet->dwID); + + if (g_log) + sys_log(0, "HEADER_GD_SAFEBOX_LOAD (handle: %d account.id %u is_mall %d)", dwHandle, packet->dwID, bMall ? 1 : 0); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = pi->dwHandle; + + if (pi->account_index == 0) + { + char szSafeboxPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; + strlcpy(szSafeboxPassword, pi->safebox_password, sizeof(szSafeboxPassword)); + + TSafeboxTable * pSafebox = new TSafeboxTable; + memset(pSafebox, 0, sizeof(TSafeboxTable)); + + SQLResult * res = msg->Get(); + + if (res->uiNumRows == 0) + { + if (strcmp("000000", szSafeboxPassword)) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_WRONG_PASSWORD, dwHandle, 0); + delete pi; + return; + } + } + else + { + MYSQL_ROW row = mysql_fetch_row(res->pSQLResult); + + if (((!row[2] || !*row[2]) && strcmp("000000", szSafeboxPassword)) || + ((row[2] && *row[2]) && strcmp(row[2], szSafeboxPassword))) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_WRONG_PASSWORD, dwHandle, 0); + delete pi; + return; + } + + if (!row[0]) + pSafebox->dwID = 0; + else + str_to_number(pSafebox->dwID, row[0]); + + if (!row[1]) + pSafebox->bSize = 0; + else + str_to_number(pSafebox->bSize, row[1]); + /* + if (!row[3]) + pSafebox->dwGold = 0; + else + pSafebox->dwGold = atoi(row[3]); + */ + if (pi->ip[0] == 1) + { + pSafebox->bSize = 1; + sys_log(0, "MALL id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); + } + else + sys_log(0, "SAFEBOX id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); + } + + if (0 == pSafebox->dwID) + pSafebox->dwID = pi->account_id; + + pi->pSafebox = pSafebox; + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "SELECT id, `window`+0, pos, count, vnum, socket0, socket1, socket2, " + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6 " + "FROM item%s WHERE owner_id=%d AND `window`='%s'", + GetTablePostfix(), pi->account_id, pi->ip[0] == 0 ? "SAFEBOX" : "MALL"); + + pi->account_index = 1; + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); + } + else + { + if (!pi->pSafebox) + { + sys_err("null safebox pointer!"); + delete pi; + return; + } + + if (!msg->Get()->pSQLResult) + { + sys_err("null safebox result"); + delete pi; + return; + } + + static std::vector s_items; + CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id); + + std::set * pSet = ItemAwardManager::instance().GetByLogin(pi->login); + + if (pSet && !m_vec_itemTable.empty()) + { + CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9); + bool bEscape = false; + + for (DWORD i = 0; i < s_items.size(); ++i) + { + TPlayerItem & r = s_items[i]; + + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum); + + if (it == m_map_itemTableByVnum.end()) + { + bEscape = true; + sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login); + break; + } + + grid.Put(r.pos, 1, it->second->bSize); + } + + if (!bEscape) + { + std::vector > vec_dwFinishedAwardID; + + typeof(pSet->begin()) it = pSet->begin(); + + char szQuery[512]; + + while (it != pSet->end()) + { + TItemAward * pItemAward = *(it++); + const DWORD& dwItemVnum = pItemAward->dwVnum; + + if (pItemAward->bTaken) + continue; + + if (pi->ip[0] == 0 && pItemAward->bMall) + continue; + + if (pi->ip[0] == 1 && !pItemAward->bMall) + continue; + + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum); + + if (it == m_map_itemTableByVnum.end()) + { + sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login); + continue; + } + + TItemTable * pItemTable = it->second; + + int iPos; + + if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1) + break; + + TPlayerItem item; + memset(&item, 0, sizeof(TPlayerItem)); + + DWORD dwSocket2 = 0; + #ifdef ENABLE_EXTEND_ITEM_AWARD + dwSocket2 = pItemAward->dwSocket2; + #endif + + if (pItemTable->bType == ITEM_UNIQUE) + { + #ifdef ENABLE_EXTEND_ITEM_AWARD + // 12.04.2019 - Correction for unique items based on the real time. + const auto lValue0 = pItemTable->alValues[ITEM_SOCKET_REMAIN_SEC]; + const auto lValue2 = pItemTable->alValues[ITEM_SOCKET_UNIQUE_REMAIN_TIME]; + const auto tNow = CClientManager::instance().GetCurrentTime(); + dwSocket2 = (lValue2 == 0) ? static_cast(lValue0) : static_cast(tNow + lValue0); + #else + if (pItemAward->dwSocket2 != 0) + dwSocket2 = pItemAward->dwSocket2; + else + dwSocket2 = pItemTable->alValues[0]; + #endif + } + else if ((dwItemVnum == 50300 || dwItemVnum == 70037) && pItemAward->dwSocket0 == 0) + { + #ifndef ENABLE_EXTEND_ITEM_AWARD + DWORD dwSkillIdx; + DWORD dwSkillVnum; + + do + { + dwSkillIdx = number(0, m_vec_skillTable.size()-1); + dwSkillVnum = m_vec_skillTable[dwSkillIdx].dwVnum; + break; + } while (1); + + pItemAward->dwSocket0 = dwSkillVnum; + #endif + } + else + { + switch (dwItemVnum) + { + case 72723: case 72724: case 72725: case 72726: + case 72727: case 72728: case 72729: case 72730: + + case 76004: case 76005: case 76021: case 76022: + case 79012: case 79013: + if (pItemAward->dwSocket2 == 0) + { + dwSocket2 = pItemTable->alValues[0]; + } + else + { + dwSocket2 = pItemAward->dwSocket2; + } + break; + } + } + + if (GetItemID () > m_itemRange.dwMax) + { + sys_err("UNIQUE ID OVERFLOW!!"); + break; + } + + { + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find (dwItemVnum); + if (it == m_map_itemTableByVnum.end()) + { + sys_err ("Invalid item(vnum : %d). It is not in m_map_itemTableByVnum.", dwItemVnum); + continue; + } + TItemTable* item_table = it->second; + if (item_table == NULL) + { + sys_err ("Invalid item_table (vnum : %d). It's value is NULL in m_map_itemTableByVnum.", dwItemVnum); + continue; + } + if (0 == pItemAward->dwSocket0) + { + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; i++) + { + if (LIMIT_REAL_TIME == item_table->aLimits[i].bType) + { + if (0 == item_table->aLimits[i].lValue) + pItemAward->dwSocket0 = time(0) + 60 * 60 * 24 * 7; + else + pItemAward->dwSocket0 = time(0) + item_table->aLimits[i].lValue; + + break; + } + else if (LIMIT_REAL_TIME_START_FIRST_USE == item_table->aLimits[i].bType || LIMIT_TIMER_BASED_ON_WEAR == item_table->aLimits[i].bType) + { + if (0 == item_table->aLimits[i].lValue) + pItemAward->dwSocket0 = 60 * 60 * 24 * 7; + else + pItemAward->dwSocket0 = item_table->aLimits[i].lValue; + + break; + } + } + } + + #ifdef ENABLE_EXTEND_ITEM_AWARD + ItemAwardManager::instance().CheckItemCount(*pItemAward, *pItemTable); + ItemAwardManager::instance().CheckItemSocket(*pItemAward, *pItemTable); + ItemAwardManager::instance().CheckItemBlend(*pItemAward, *pItemTable); + ItemAwardManager::instance().CheckItemAddonType(*pItemAward, *pItemTable); + ItemAwardManager::instance().CheckItemSkillBook(*pItemAward, m_vec_skillTable); + #ifdef USE_ITEM_AWARD_CHECK_ATTRIBUTES + ItemAwardManager::instance().CheckItemAttributes(*pItemAward, *pItemTable, m_vec_itemAttrTable, m_vec_itemRareTable); + #endif + + // START_OF_AUTO_QUERY + char szColumns[QUERY_MAX_LEN], szValues[QUERY_MAX_LEN]; + + auto iLen = snprintf(szColumns, sizeof(szColumns), "id, owner_id, `window`, pos, vnum, count"); + auto iValueLen = snprintf(szValues, sizeof(szValues), "%u, %u, '%s', %d, %u, %u", GainItemID(), pi->account_id, (pi->ip[0] == 0) ? "SAFEBOX" : "MALL", iPos, pItemAward->dwVnum, pItemAward->dwCount); + + iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen, ", socket0, socket1, socket2"); + iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen, ", %u, %u, %u", pItemAward->dwSocket0, pItemAward->dwSocket1, dwSocket2); + + for (auto i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + { + iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen, ", attrtype%d, attrvalue%d", i, i); + iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen, ", %d, %d", pItemAward->aAttr[i].bType, pItemAward->aAttr[i].sValue); + } + // END_OF_AUTO_QUERY + + snprintf(szQuery, sizeof(szQuery), "INSERT INTO item%s (%s) VALUES(%s)", GetTablePostfix(), szColumns, szValues); + #else + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO item%s (id, owner_id, `window`, pos, vnum, count, socket0, socket1, socket2) " + "VALUES(%u, %u, '%s', %d, %u, %u, %u, %u, %u)", + GetTablePostfix(), + GainItemID(), + pi->account_id, + pi->ip[0] == 0 ? "SAFEBOX" : "MALL", + iPos, + pItemAward->dwVnum, pItemAward->dwCount, pItemAward->dwSocket0, pItemAward->dwSocket1, dwSocket2); + #endif + } + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult * pRes = pmsg->Get(); + sys_log(0, "SAFEBOX Query : [%s]", szQuery); + + if (pRes->uiAffectedRows == 0 || pRes->uiInsertID == 0 || pRes->uiAffectedRows == (uint32_t)-1) + break; + + item.id = pmsg->Get()->uiInsertID; + item.window = pi->ip[0] == 0 ? SAFEBOX : MALL, + item.pos = iPos; + item.count = pItemAward->dwCount; + item.vnum = pItemAward->dwVnum; + item.alSockets[0] = pItemAward->dwSocket0; + item.alSockets[1] = pItemAward->dwSocket1; + item.alSockets[2] = dwSocket2; + #ifdef ENABLE_EXTEND_ITEM_AWARD + memcpy(&item.aAttr, pItemAward->aAttr, sizeof(item.aAttr)); + #endif + s_items.emplace_back(item); + + vec_dwFinishedAwardID.emplace_back(pItemAward->dwID, item.id); + grid.Put(iPos, 1, it->second->bSize); + } + + for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i) + ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second); + } + } + + pi->pSafebox->wItemCount = s_items.size(); + + pkPeer->EncodeHeader(pi->ip[0] == 0 ? HEADER_DG_SAFEBOX_LOAD : HEADER_DG_MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size()); + + pkPeer->Encode(*pi->pSafebox); + + if (!s_items.empty()) + pkPeer->Encode(s_items); + + delete pi; + } +} + +void CClientManager::QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + pi->account_index = p->bSize; + + char szQuery[QUERY_MAX_LEN]; + + if (p->bSize == 1) + snprintf(szQuery, sizeof(szQuery), "INSERT INTO safebox%s (account_id, size) VALUES(%u, %u)", GetTablePostfix(), p->dwID, p->bSize); + else + snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET size=%u WHERE account_id=%u", GetTablePostfix(), p->bSize, p->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_SIZE, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + BYTE bSize = p->account_index; + + delete p; + + if (msg->Get()->uiNumRows > 0) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_SIZE, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bSize); + } +} + +void CClientManager::QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + strlcpy(pi->safebox_password, p->szNewPassword, sizeof(pi->safebox_password)); + strlcpy(pi->login, p->szOldPassword, sizeof(pi->login)); + pi->account_id = p->dwID; + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT password FROM safebox%s WHERE account_id=%u", GetTablePostfix(), p->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + + if (msg->Get()->uiNumRows > 0) + { + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + if ((row[0] && *row[0] && !strcasecmp(row[0], p->login)) || ((!row[0] || !*row[0]) && !strcmp("000000", p->login))) + { + char szQuery[QUERY_MAX_LEN]; + char escape_pwd[64]; + CDBManager::instance().EscapeString(escape_pwd, p->safebox_password, strlen(p->safebox_password)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET password='%s' WHERE account_id=%u", GetTablePostfix(), escape_pwd, p->account_id); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD_SECOND, pkPeer->GetHandle(), p); + return; + } + } + + delete p; + + // Wrong old password + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(0); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + delete p; + + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(1); +} + +// MYSHOP_PRICE_LIST +void CClientManager::RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg) +{ + TItemPricelistReqInfo* pReqInfo = (TItemPricelistReqInfo*)static_cast(pMsg->pvUserData)->pvData; + + TItemPriceListTable table; + table.dwOwnerID = pReqInfo->second; + table.byCount = 0; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + str_to_number(table.aPriceInfo[table.byCount].dwVnum, row[0]); + str_to_number(table.aPriceInfo[table.byCount].dwPrice, row[1]); + table.byCount++; + } + + PutItemPriceListCache(&table); + + TPacketMyshopPricelistHeader header; + + header.dwOwnerID = pReqInfo->second; + header.byCount = table.byCount; + + size_t sizePriceListSize = sizeof(TItemPriceInfo) * header.byCount; + + peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, pReqInfo->first, sizeof(header) + sizePriceListSize); + peer->Encode(header); + peer->Encode(table.aPriceInfo, sizePriceListSize); + + sys_log(0, "Load MyShopPricelist handle[%d] pid[%d] count[%d]", pReqInfo->first, pReqInfo->second, header.byCount); + + delete pReqInfo; +} + +void CClientManager::RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg) +{ + TItemPriceListTable* pUpdateTable = (TItemPriceListTable*)static_cast(pMsg->pvUserData)->pvData; + + TItemPriceListTable table; + table.dwOwnerID = pUpdateTable->dwOwnerID; + table.byCount = 0; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + str_to_number(table.aPriceInfo[table.byCount].dwVnum, row[0]); + str_to_number(table.aPriceInfo[table.byCount].dwPrice, row[1]); + table.byCount++; + } + + PutItemPriceListCache(&table); + + // Update cache + GetItemPriceListCache(pUpdateTable->dwOwnerID)->UpdateList(pUpdateTable); + + delete pUpdateTable; +} +// END_OF_MYSHOP_PRICE_LIST + +void CClientManager::QUERY_SAFEBOX_SAVE(CPeer * pkPeer, TSafeboxTable * pTable) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "UPDATE safebox%s SET gold='%u' WHERE account_id=%u", + GetTablePostfix(), pTable->dwGold, pTable->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_SAVE, pkPeer->GetHandle(), NULL); +} + +void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE id=%u", GetTablePostfix(), p->bEmpire, p->dwAccountID); + CDBManager::instance().DirectQuery(szQuery); + + sys_log(0, "EmpireSelect: %s", szQuery); + { +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, pid3, pid4, pid5 FROM player_index%s WHERE id=%u", GetTablePostfix(), p->dwAccountID); +#else + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, pid3, pid4 FROM player_index%s WHERE id=%u", GetTablePostfix(), p->dwAccountID); +#endif + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult * pRes = pmsg->Get(); + + if (pRes->uiNumRows) + { + sys_log(0, "EMPIRE %lu", pRes->uiNumRows); + + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + DWORD pids[PLAYER_PER_ACCOUNT]{}; // @fixme310 + + UINT g_start_map[4] = + { + 0, // reserved + 1, + 21, + 41 + }; + + // FIXME share with game + DWORD g_start_position[4][2]= + { + { 0, 0 }, + { 469300, 964200 }, + { 55700, 157900 }, + { 969600, 278400 } + }; + + for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i) // @fixme310 + { + str_to_number(pids[i], row[i]); + sys_log(0, "EMPIRE PIDS[%d]", pids[i]); + + if (pids[i]) + { + sys_log(0, "EMPIRE move to pid[%d] to village of %u, map_index %d", + pids[i], p->bEmpire, g_start_map[p->bEmpire]); + + snprintf(szQuery, sizeof(szQuery), "UPDATE player%s SET map_index=%u,x=%u,y=%u WHERE id=%u", + GetTablePostfix(), + g_start_map[p->bEmpire], + g_start_position[p->bEmpire][0], + g_start_position[p->bEmpire][1], + pids[i]); + + auto pmsg2(CDBManager::instance().DirectQuery(szQuery)); + } + } + } + } + + pkPeer->EncodeHeader(HEADER_DG_EMPIRE_SELECT, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(p->bEmpire); +} + +void CClientManager::QUERY_SETUP(CPeer * peer, DWORD dwHandle, const char * c_pData) +{ + TPacketGDSetup * p = (TPacketGDSetup *) c_pData; + c_pData += sizeof(TPacketGDSetup); + + if (p->bAuthServer) + { + sys_log(0, "AUTH_PEER ptr %p", peer); + + m_pkAuthPeer = peer; + SendAllLoginToBilling(); + return; + } + + peer->SetPublicIP(p->szPublicIP); + peer->SetChannel(p->bChannel); + peer->SetListenPort(p->wListenPort); + peer->SetP2PPort(p->wP2PPort); + peer->SetMaps(p->alMaps); + + TMapLocation kMapLocations{}; + #ifdef ENABLE_MOVE_CHANNEL + kMapLocations.channel = peer->GetChannel(); + #endif + strlcpy(kMapLocations.szHost, peer->GetPublicIP(), sizeof(kMapLocations.szHost)); + kMapLocations.wPort = peer->GetListenPort(); + thecore_memcpy(kMapLocations.alMaps, peer->GetMaps(), sizeof(kMapLocations.alMaps)); + + BYTE bMapCount = 1; + std::vector vec_kMapLocations; + + #ifndef ENABLE_MOVE_CHANNEL + if (peer->GetChannel() == 1) + { + for (auto & tmp : m_peerList) + { + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == GUILD_WARP_WAR_CHANNEL || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2{}; + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.emplace_back(kMapLocation2); + + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + tmp->EncodeBYTE(bMapCount); + tmp->Encode(kMapLocations); + } + } + } + else if (peer->GetChannel() == GUILD_WARP_WAR_CHANNEL) + { + for (auto & tmp : m_peerList) + { + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == 1 || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2{}; + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.emplace_back(kMapLocation2); + } + + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + tmp->EncodeBYTE(bMapCount); + tmp->Encode(kMapLocations); + } + } + else + { + for (auto & tmp : m_peerList) + { + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == GUILD_WARP_WAR_CHANNEL || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2; + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.emplace_back(kMapLocation2); + } + + if (tmp->GetChannel() == peer->GetChannel()) + { + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + tmp->EncodeBYTE(bMapCount); + tmp->Encode(kMapLocations); + } + } + } + #else + for (auto & tmp : m_peerList) { + if (tmp == peer) { + continue; + } + + if (!tmp->GetChannel()) + continue; + + TMapLocation kMapLocation2{}; + kMapLocation2.channel = tmp->GetChannel(); + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.emplace_back(kMapLocation2); + + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + tmp->EncodeBYTE(bMapCount); + tmp->Encode(kMapLocations); + } + #endif + + vec_kMapLocations.emplace_back(kMapLocations); + peer->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation) * vec_kMapLocations.size()); + bMapCount = vec_kMapLocations.size(); + peer->EncodeBYTE(bMapCount); + peer->Encode(vec_kMapLocations); + + sys_log(0, "SETUP: channel %u listen %u p2p %u count %u", peer->GetChannel(), p->wListenPort, p->wP2PPort, bMapCount); + + TPacketDGP2P p2pSetupPacket; + p2pSetupPacket.wPort = peer->GetP2PPort(); + p2pSetupPacket.bChannel = peer->GetChannel(); + strlcpy(p2pSetupPacket.szHost, peer->GetPublicIP(), sizeof(p2pSetupPacket.szHost)); + + for (auto & tmp : m_peerList) + { + if (tmp == peer) + continue; + + if (0 == tmp->GetChannel()) + continue; + + tmp->EncodeHeader(HEADER_DG_P2P, 0, sizeof(TPacketDGP2P)); + tmp->Encode(p2pSetupPacket); + } + + TPacketLoginOnSetup * pck = (TPacketLoginOnSetup *) c_pData;; + std::vector vec_repair; + + for (DWORD c = 0; c < p->dwLoginCount; ++c, ++pck) + { + CLoginData * pkLD = new CLoginData; + + pkLD->SetKey(pck->dwLoginKey); + pkLD->SetClientKey(pck->adwClientKey); + pkLD->SetIP(pck->szHost); + + TAccountTable & r = pkLD->GetAccountRef(); + + r.id = pck->dwID; + trim_and_lower(pck->szLogin, r.login, sizeof(r.login)); + strlcpy(r.social_id, pck->szSocialID, sizeof(r.social_id)); + strlcpy(r.passwd, "TEMP", sizeof(r.passwd)); + + InsertLoginData(pkLD); + + if (InsertLogonAccount(pck->szLogin, peer->GetHandle(), pck->szHost)) + { + sys_log(0, "SETUP: login %u %s login_key %u host %s", pck->dwID, pck->szLogin, pck->dwLoginKey, pck->szHost); + pkLD->SetPlay(true); + + if (m_pkAuthPeer) + { + TPacketBillingRepair pck_repair; + pck_repair.dwLoginKey = pkLD->GetKey(); + strlcpy(pck_repair.szLogin, pck->szLogin, sizeof(pck_repair.szLogin)); + strlcpy(pck_repair.szHost, pck->szHost, sizeof(pck_repair.szHost)); + vec_repair.emplace_back(pck_repair); + } + } + else + sys_log(0, "SETUP: login_fail %u %s login_key %u", pck->dwID, pck->szLogin, pck->dwLoginKey); + } + + if (m_pkAuthPeer && !vec_repair.empty()) + { + sys_log(0, "REPAIR size %d", vec_repair.size()); + + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_REPAIR, 0, sizeof(DWORD) + sizeof(TPacketBillingRepair) * vec_repair.size()); + m_pkAuthPeer->EncodeDWORD(vec_repair.size()); + m_pkAuthPeer->Encode(vec_repair); + } + + SendPartyOnSetup(peer); + CGuildManager::instance().OnSetup(peer); + CPrivManager::instance().SendPrivOnSetup(peer); + SendEventFlagsOnSetup(peer); + marriage::CManager::instance().OnSetup(peer); +} + +void CClientManager::QUERY_ITEM_FLUSH(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwID = *(DWORD *) c_pData; + + if (g_log) + sys_log(0, "HEADER_GD_ITEM_FLUSH: %u", dwID); + + CItemCache * c = GetItemCache(dwID); + + if (c) + c->Flush(); +} + +void CClientManager::QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData) +{ + TPlayerItem * p = (TPlayerItem *) c_pData; + + if (p->window == SAFEBOX || p->window == MALL) + { + CItemCache * c = GetItemCache(p->id); + + if (c) + { + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_test_server) + sys_log(0, "ITEM_CACHE: safebox owner %u id %u", c->Get()->owner, c->Get()->id); + + it->second->erase(c); + } + + m_map_itemCache.erase(p->id); + + delete c; + } + + const auto setQuery = fmt::format(FMT_COMPILE("id={}, owner_id={}, `window`={}, pos={}, count={}, vnum={}, socket0={}, socket1={}, socket2={}, " + "attrtype0={}, attrvalue0={}, " + "attrtype1={}, attrvalue1={}, " + "attrtype2={}, attrvalue2={}, " + "attrtype3={}, attrvalue3={}, " + "attrtype4={}, attrvalue4={}, " + "attrtype5={}, attrvalue5={}, " + "attrtype6={}, attrvalue6={} ") + , p->id, + p->owner, + p->window, + p->pos, + p->count, + p->vnum, + p->alSockets[0], + p->alSockets[1], + p->alSockets[2], + p->aAttr[0].bType, p->aAttr[0].sValue, + p->aAttr[1].bType, p->aAttr[1].sValue, + p->aAttr[2].bType, p->aAttr[2].sValue, + p->aAttr[3].bType, p->aAttr[3].sValue, + p->aAttr[4].bType, p->aAttr[4].sValue, + p->aAttr[5].bType, p->aAttr[5].sValue, + p->aAttr[6].bType, p->aAttr[6].sValue + ); // @fixme205 + + const auto itemQuery = fmt::format(FMT_COMPILE("INSERT INTO item{} SET {} ON DUPLICATE KEY UPDATE {}"), + GetTablePostfix(), setQuery, setQuery); + + if (g_test_server) + sys_log(0, "QUERY_ITEM_SAVE :REPLACE (%s)", itemQuery.c_str()); + CDBManager::instance().ReturnQuery(itemQuery.c_str(), QID_ITEM_SAVE, pkPeer->GetHandle(), NULL); + } + else + { + if (g_test_server) + sys_log(0, "QUERY_ITEM_SAVE => PutItemCache() owner %d id %d vnum %d ", p->owner, p->id, p->vnum); + + PutItemCache(p); + } +} + +CClientManager::TItemCacheSet * CClientManager::GetItemCacheSet(DWORD pid) +{ + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + return NULL; + + return it->second; +} + +void CClientManager::CreateItemCacheSet(DWORD pid) +{ + if (m_map_pkItemCacheSetPtr.find(pid) != m_map_pkItemCacheSetPtr.end()) + return; + + TItemCacheSet * pSet = new TItemCacheSet; + m_map_pkItemCacheSetPtr.emplace(pid, pSet); + + if (g_log) + sys_log(0, "ITEM_CACHE: new cache %u", pid); +} + +void CClientManager::FlushItemCacheSet(DWORD pid) +{ + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + { + sys_log(0, "FLUSH_ITEMCACHESET : No ItemCacheSet pid(%d)", pid); + return; + } + + TItemCacheSet * pSet = it->second; + TItemCacheSet::iterator it_set = pSet->begin(); + + while (it_set != pSet->end()) + { + CItemCache * c = *it_set++; + c->Flush(); + + m_map_itemCache.erase(c->Get()->id); + delete c; + } + + pSet->clear(); + delete pSet; + + m_map_pkItemCacheSetPtr.erase(it); + + if (g_log) + sys_log(0, "FLUSH_ITEMCACHESET : Deleted pid(%d)", pid); +} + +CItemCache * CClientManager::GetItemCache(DWORD id) +{ + TItemCacheMap::iterator it = m_map_itemCache.find(id); + + if (it == m_map_itemCache.end()) + return NULL; + + return it->second; +} + +void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery) +{ + CItemCache * c; + + c = GetItemCache(pNew->id); + + if (!c) + { + if (g_log) + sys_log(0, "ITEM_CACHE: PutItemCache ==> New CItemCache id%d vnum%d new owner%d", pNew->id, pNew->vnum, pNew->owner); + + c = new CItemCache; + m_map_itemCache.emplace(pNew->id, c); + } + + else + { + if (g_log) + sys_log(0, "ITEM_CACHE: PutItemCache ==> Have Cache"); + + if (pNew->owner != c->Get()->owner) + { + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_log) + sys_log(0, "ITEM_CACHE: delete owner %u id %u new owner %u", c->Get()->owner, c->Get()->id, pNew->owner); + it->second->erase(c); + } + } + } + + c->Put(pNew, bSkipQuery); + + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_log) + sys_log(0, "ITEM_CACHE: save %u id %u", c->Get()->owner, c->Get()->id); + else + sys_log(1, "ITEM_CACHE: save %u id %u", c->Get()->owner, c->Get()->id); + it->second->emplace(c); + } + else + { + if (g_log) + sys_log(0, "ITEM_CACHE: direct save %u id %u", c->Get()->owner, c->Get()->id); + else + sys_log(1, "ITEM_CACHE: direct save %u id %u", c->Get()->owner, c->Get()->id); + + c->OnFlush(); + } +} + +bool CClientManager::DeleteItemCache(DWORD dwID) +{ + CItemCache * c = GetItemCache(dwID); + + if (!c) + return false; + + c->Delete(); + return true; +} + +// MYSHOP_PRICE_LIST +CItemPriceListTableCache* CClientManager::GetItemPriceListCache(DWORD dwID) +{ + TItemPriceListCacheMap::iterator it = m_mapItemPriceListCache.find(dwID); + + if (it == m_mapItemPriceListCache.end()) + return NULL; + + return it->second; +} + +void CClientManager::PutItemPriceListCache(const TItemPriceListTable* pItemPriceList) +{ + CItemPriceListTableCache* pCache = GetItemPriceListCache(pItemPriceList->dwOwnerID); + + if (!pCache) + { + pCache = new CItemPriceListTableCache; + m_mapItemPriceListCache.emplace(pItemPriceList->dwOwnerID, pCache); + } + + pCache->Put(const_cast(pItemPriceList), true); +} + +void CClientManager::UpdatePlayerCache() +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.begin(); + + while (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = (it++)->second; + + if (c->CheckTimeout()) + { + if (g_log) + sys_log(0, "UPDATE : UpdatePlayerCache() ==> FlushPlayerCache %d %s ", c->Get(false)->id, c->Get(false)->name); + + c->Flush(); + + UpdateItemCacheSet(c->Get()->id); + } + else if (c->CheckFlushTimeout()) + c->Flush(); + } +} +// END_OF_MYSHOP_PRICE_LIST + +void CClientManager::SetCacheFlushCountLimit(int iLimit) +{ + m_iCacheFlushCountLimit = MAX(10, iLimit); + sys_log(0, "CACHE_FLUSH_LIMIT_PER_SECOND: %d", m_iCacheFlushCountLimit); +} + +void CClientManager::UpdateItemCache() +{ + if (m_iCacheFlushCount >= m_iCacheFlushCountLimit) + return; + + TItemCacheMap::iterator it = m_map_itemCache.begin(); + + while (it != m_map_itemCache.end()) + { + CItemCache * c = (it++)->second; + + if (c->CheckFlushTimeout()) + { + if (g_test_server) + sys_log(0, "UpdateItemCache ==> Flush() vnum %d id owner %d", c->Get()->vnum, c->Get()->id, c->Get()->owner); + + c->Flush(); + + if (++m_iCacheFlushCount >= m_iCacheFlushCountLimit) + break; + } + } +} + +void CClientManager::UpdateItemPriceListCache() +{ + TItemPriceListCacheMap::iterator it = m_mapItemPriceListCache.begin(); + + while (it != m_mapItemPriceListCache.end()) + { + CItemPriceListTableCache* pCache = it->second; + + if (pCache->CheckFlushTimeout()) + { + pCache->Flush(); + m_mapItemPriceListCache.erase(it++); + } + else + ++it; + } +} + +void CClientManager::QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwID = *(DWORD *) c_pData; + c_pData += sizeof(DWORD); + + DWORD dwPID = *(DWORD *) c_pData; + + if (!DeleteItemCache(dwID)) + { + char szQuery[64]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM item%s WHERE id=%u", GetTablePostfix(), dwID); + + if (g_log) + sys_log(0, "HEADER_GD_ITEM_DESTROY: PID %u ID %u", dwPID, dwID); + + if (dwPID == 0) + CDBManager::instance().AsyncQuery(szQuery); + else + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, pkPeer->GetHandle(), NULL); + } +} + +void CClientManager::QUERY_FLUSH_CACHE(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwPID = *(DWORD *) c_pData; + + CPlayerTableCache * pkCache = GetPlayerCache(dwPID); + + if (!pkCache) + return; + + sys_log(0, "FLUSH_CACHE: %u", dwPID); + + pkCache->Flush(); + FlushItemCacheSet(dwPID); + + m_map_playerCache.erase(dwPID); + delete pkCache; +} + +void CClientManager::QUERY_RELOAD_PROTO() +{ + if (!InitializeTables()) + { + sys_err("QUERY_RELOAD_PROTO: cannot load tables"); + return; + } + + for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i) + { + CPeer * tmp = *i; + + if (!tmp->GetChannel()) + continue; + + tmp->EncodeHeader(HEADER_DG_RELOAD_PROTO, 0, + sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() + + sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() + + sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() + + sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size()); + + tmp->EncodeWORD(m_vec_skillTable.size()); + tmp->Encode(m_vec_skillTable); + + tmp->EncodeWORD(m_vec_banwordTable.size()); + tmp->Encode(m_vec_banwordTable); + + tmp->EncodeWORD(m_vec_itemTable.size()); + tmp->Encode(m_vec_itemTable); + + tmp->EncodeWORD(m_vec_mobTable.size()); + tmp->Encode(m_vec_mobTable); + } +} + +// ADD_GUILD_PRIV_TIME + +void CClientManager::AddGuildPriv(TPacketGiveGuildPriv* p) +{ + CPrivManager::instance().AddGuildPriv(p->guild_id, p->type, p->value, p->duration_sec); +#ifdef ENABLE_DEFAULT_PRIV + __UpdateDefaultPriv("GUILD", p->guild_id, p->type, p->value, p->duration_sec); +#endif +} + +void CClientManager::AddEmpirePriv(TPacketGiveEmpirePriv* p) +{ + CPrivManager::instance().AddEmpirePriv(p->empire, p->type, p->value, p->duration_sec); +#ifdef ENABLE_DEFAULT_PRIV + __UpdateDefaultPriv("EMPIRE", p->empire, p->type, p->value, p->duration_sec); +#endif +} +// END_OF_ADD_GUILD_PRIV_TIME + +void CClientManager::AddCharacterPriv(TPacketGiveCharacterPriv* p) +{ + CPrivManager::instance().AddCharPriv(p->pid, p->type, p->value); +#ifdef ENABLE_DEFAULT_PRIV + __UpdateDefaultPriv("PLAYER", p->pid, p->type, p->value, 0); +#endif +} + +void CClientManager::MoneyLog(TPacketMoneyLog* p) +{ + CMoneyLog::instance().AddLog(p->type, p->vnum, p->gold); +} + +CLoginData * CClientManager::GetLoginData(DWORD dwKey) +{ + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(dwKey); + + if (it == m_map_pkLoginData.end()) + return NULL; + + return it->second; +} + +CLoginData * CClientManager::GetLoginDataByLogin(const char * c_pszLogin) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + TLoginDataByLogin::iterator it = m_map_pkLoginDataByLogin.find(szLogin); + + if (it == m_map_pkLoginDataByLogin.end()) + return NULL; + + return it->second; +} + +CLoginData * CClientManager::GetLoginDataByAID(DWORD dwAID) +{ + TLoginDataByAID::iterator it = m_map_pkLoginDataByAID.find(dwAID); + + if (it == m_map_pkLoginDataByAID.end()) + return NULL; + + return it->second; +} + +void CClientManager::InsertLoginData(CLoginData * pkLD) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(pkLD->GetAccountRef().login, szLogin, sizeof(szLogin)); + + m_map_pkLoginData.emplace(pkLD->GetKey(), pkLD); + m_map_pkLoginDataByLogin.emplace(szLogin, pkLD); + m_map_pkLoginDataByAID.emplace(pkLD->GetAccountRef().id, pkLD); +} + +void CClientManager::DeleteLoginData(CLoginData * pkLD) +{ + m_map_pkLoginData.erase(pkLD->GetKey()); + m_map_pkLoginDataByLogin.erase(pkLD->GetAccountRef().login); + m_map_pkLoginDataByAID.erase(pkLD->GetAccountRef().id); + + if (m_map_kLogonAccount.find(pkLD->GetAccountRef().login) == m_map_kLogonAccount.end()) + delete pkLD; + else + pkLD->SetDeleted(true); +} + +void CClientManager::QUERY_AUTH_LOGIN(CPeer * pkPeer, DWORD dwHandle, TPacketGDAuthLogin * p) +{ + if (g_test_server) + sys_log(0, "QUERY_AUTH_LOGIN %d %d %s", p->dwID, p->dwLoginKey, p->szLogin); + CLoginData * pkLD = GetLoginDataByLogin(p->szLogin); + + if (pkLD) + { + DeleteLoginData(pkLD); + } + + BYTE bResult; + + if (GetLoginData(p->dwLoginKey)) + { + sys_err("LoginData already exist key %u login %s", p->dwLoginKey, p->szLogin); + bResult = 0; + + pkPeer->EncodeHeader(HEADER_DG_AUTH_LOGIN, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bResult); + } + else + { + CLoginData * pkLD = new CLoginData; + + pkLD->SetKey(p->dwLoginKey); + pkLD->SetClientKey(p->adwClientKey); + pkLD->SetBillType(p->bBillType); + pkLD->SetBillID(p->dwBillID); + pkLD->SetPremium(p->iPremiumTimes); + + TAccountTable & r = pkLD->GetAccountRef(); + + r.id = p->dwID; + trim_and_lower(p->szLogin, r.login, sizeof(r.login)); + strlcpy(r.social_id, p->szSocialID, sizeof(r.social_id)); + strlcpy(r.passwd, "TEMP", sizeof(r.passwd)); + + sys_log(0, "AUTH_LOGIN id(%u) login(%s) social_id(%s) login_key(%u), client_key(%u %u %u %u)", + p->dwID, p->szLogin, p->szSocialID, p->dwLoginKey, + p->adwClientKey[0], p->adwClientKey[1], p->adwClientKey[2], p->adwClientKey[3]); + + bResult = 1; + + InsertLoginData(pkLD); + + pkPeer->EncodeHeader(HEADER_DG_AUTH_LOGIN, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bResult); + } +} + +void CClientManager::BillingExpire(TPacketBillingExpire * p) +{ + char key[LOGIN_MAX_LEN + 1]; + trim_and_lower(p->szLogin, key, sizeof(key)); + + switch (p->bBillType) + { + case BILLING_IP_TIME: + case BILLING_IP_DAY: + { + DWORD dwIPID = 0; + str_to_number(dwIPID, p->szLogin); + + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = (it++)->second; + + if (pkLD->GetBillID() == dwIPID) + { + CPeer * pkPeer = GetPeer(pkLD->GetConnectedPeerHandle()); + + if (pkPeer) + { + strlcpy(p->szLogin, pkLD->GetAccountRef().login, sizeof(p->szLogin)); + pkPeer->EncodeHeader(HEADER_DG_BILLING_EXPIRE, 0, sizeof(TPacketBillingExpire)); + pkPeer->Encode(p, sizeof(TPacketBillingExpire)); + } + } + } + } + break; + + case BILLING_TIME: + case BILLING_DAY: + { + TLogonAccountMap::iterator it = m_map_kLogonAccount.find(key); + + if (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = it->second; + + CPeer * pkPeer = GetPeer(pkLD->GetConnectedPeerHandle()); + + if (pkPeer) + { + pkPeer->EncodeHeader(HEADER_DG_BILLING_EXPIRE, 0, sizeof(TPacketBillingExpire)); + pkPeer->Encode(p, sizeof(TPacketBillingExpire)); + } + } + } + break; + } +} + +void CClientManager::BillingCheck(const char * data) +{ + if (!m_pkAuthPeer) + return; + + time_t curTime = GetCurrentTime(); + + DWORD dwCount = *(DWORD *) data; + data += sizeof(DWORD); + + std::vector vec; + + sys_log(0, "BillingCheck: size %u", dwCount); + + for (DWORD i = 0; i < dwCount; ++i) + { + DWORD dwKey = *(DWORD *) data; + data += sizeof(DWORD); + + sys_log(0, "BillingCheck: %u", dwKey); + + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(dwKey); + + if (it == m_map_pkLoginData.end()) + { + sys_log(0, "BillingCheck: key not exist: %u", dwKey); + vec.emplace_back(dwKey); + } + else + { + CLoginData * pkLD = it->second; + + if (!pkLD->IsPlay() && curTime - pkLD->GetLastPlayTime() > 180) + { + sys_log(0, "BillingCheck: not login: %u", dwKey); + vec.emplace_back(dwKey); + } + } + } + + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_CHECK, 0, sizeof(DWORD) + sizeof(DWORD) * vec.size()); + m_pkAuthPeer->EncodeDWORD(vec.size()); + + if (!vec.empty()) + m_pkAuthPeer->Encode(vec); +} + +void CClientManager::GuildDepositMoney(TPacketGDGuildMoney* p) +{ + CGuildManager::instance().DepositMoney(p->dwGuild, p->iGold); +} + +void CClientManager::GuildWithdrawMoney(CPeer* peer, TPacketGDGuildMoney* p) +{ + CGuildManager::instance().WithdrawMoney(peer, p->dwGuild, p->iGold); +} + +void CClientManager::GuildWithdrawMoneyGiveReply(TPacketGDGuildMoneyWithdrawGiveReply* p) +{ + CGuildManager::instance().WithdrawMoneyReply(p->dwGuild, p->bGiveSuccess, p->iChangeGold); +} + +void CClientManager::GuildWarBet(TPacketGDGuildWarBet * p) +{ + CGuildManager::instance().Bet(p->dwWarID, p->szLogin, p->dwGold, p->dwGuild); +} + +void CClientManager::SendAllLoginToBilling() +{ + if (!m_pkAuthPeer) + return; + + std::vector vec; + TPacketBillingRepair p; + + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = (it++)->second; + + p.dwLoginKey = pkLD->GetKey(); + strlcpy(p.szLogin, pkLD->GetAccountRef().login, sizeof(p.szLogin)); + strlcpy(p.szHost, pkLD->GetIP(), sizeof(p.szHost)); + sys_log(0, "SendAllLoginToBilling %s %s", pkLD->GetAccountRef().login, pkLD->GetIP()); + vec.emplace_back(p); + } + + if (!vec.empty()) + { + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_REPAIR, 0, sizeof(DWORD) + sizeof(TPacketBillingRepair) * vec.size()); + m_pkAuthPeer->EncodeDWORD(vec.size()); + m_pkAuthPeer->Encode(vec); + } +} + +void CClientManager::SendLoginToBilling(CLoginData * pkLD, bool bLogin) +{ + if (!m_pkAuthPeer) + return; + + TPacketBillingLogin p; + + p.dwLoginKey = pkLD->GetKey(); + p.bLogin = bLogin ? 1 : 0; + + DWORD dwCount = 1; + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_LOGIN, 0, sizeof(DWORD) + sizeof(TPacketBillingLogin)); + m_pkAuthPeer->EncodeDWORD(dwCount); + m_pkAuthPeer->Encode(p); +} + +void CClientManager::CreateObject(TPacketGDCreateObject * p) +{ + using namespace building; + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO object%s (land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot) VALUES(%u, %u, %d, %d, %d, %f, %f, %f)", + GetTablePostfix(), p->dwLandID, p->dwVnum, p->lMapIndex, p->x, p->y, p->xRot, p->yRot, p->zRot); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + if (pmsg->Get()->uiInsertID == 0) + { + sys_err("cannot insert object"); + return; + } + + TObject * pkObj = new TObject; + + memset(pkObj, 0, sizeof(TObject)); + + pkObj->dwID = pmsg->Get()->uiInsertID; + pkObj->dwVnum = p->dwVnum; + pkObj->dwLandID = p->dwLandID; + pkObj->lMapIndex = p->lMapIndex; + pkObj->x = p->x; + pkObj->y = p->y; + pkObj->xRot = p->xRot; + pkObj->yRot = p->yRot; + pkObj->zRot = p->zRot; + pkObj->lLife = 0; + + ForwardPacket(HEADER_DG_CREATE_OBJECT, pkObj, sizeof(TObject)); + + m_map_pkObjectTable.emplace(pkObj->dwID, pkObj); +} + +void CClientManager::DeleteObject(DWORD dwID) +{ + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM object%s WHERE id=%u", GetTablePostfix(), dwID); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_err("no object by id %u", dwID); + return; + } + + itertype(m_map_pkObjectTable) it = m_map_pkObjectTable.find(dwID); + + if (it != m_map_pkObjectTable.end()) + { + delete it->second; + m_map_pkObjectTable.erase(it); + } + + ForwardPacket(HEADER_DG_DELETE_OBJECT, &dwID, sizeof(DWORD)); +} + +void CClientManager::UpdateLand(DWORD * pdw) +{ + DWORD dwID = pdw[0]; + DWORD dwGuild = pdw[1]; + + building::TLand * p = &m_vec_kLandTable[0]; + + DWORD i; + + for (i = 0; i < m_vec_kLandTable.size(); ++i, ++p) + { + if (p->dwID == dwID) + { + char buf[256]; + snprintf(buf, sizeof(buf), "UPDATE land%s SET guild_id=%u WHERE id=%u", GetTablePostfix(), dwGuild, dwID); + CDBManager::instance().AsyncQuery(buf); + + p->dwGuildID = dwGuild; + break; + } + } + + if (i < m_vec_kLandTable.size()) + ForwardPacket(HEADER_DG_UPDATE_LAND, p, sizeof(building::TLand)); +} + +// BLOCK_CHAT +void CClientManager::BlockChat(TPacketBlockChat* p) +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s'", GetTablePostfix(), p->szName); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult * pRes = pmsg->Get(); + + if (pRes->uiNumRows) + { + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + DWORD pid = strtoul(row[0], NULL, 10); + + TPacketGDAddAffect pa; + pa.dwPID = pid; + pa.elem.dwType = 223; + pa.elem.bApplyOn = 0; + pa.elem.lApplyValue = 0; + pa.elem.dwFlag = 0; + pa.elem.lDuration = p->lDuration; + pa.elem.lSPCost = 0; + QUERY_ADD_AFFECT(NULL, &pa); + } + else + { + // cannot find user with that name + } +} +// END_OF_BLOCK_CHAT + +void CClientManager::MarriageAdd(TPacketMarriageAdd * p) +{ + sys_log(0, "MarriageAdd %u %u %s %s", p->dwPID1, p->dwPID2, p->szName1, p->szName2); + marriage::CManager::instance().Add(p->dwPID1, p->dwPID2, p->szName1, p->szName2); +} + +void CClientManager::MarriageUpdate(TPacketMarriageUpdate * p) +{ + sys_log(0, "MarriageUpdate PID:%u %u LP:%d ST:%d", p->dwPID1, p->dwPID2, p->iLovePoint, p->byMarried); + marriage::CManager::instance().Update(p->dwPID1, p->dwPID2, p->iLovePoint, p->byMarried); +} + +void CClientManager::MarriageRemove(TPacketMarriageRemove * p) +{ + sys_log(0, "MarriageRemove %u %u", p->dwPID1, p->dwPID2); + marriage::CManager::instance().Remove(p->dwPID1, p->dwPID2); +} + +void CClientManager::WeddingRequest(TPacketWeddingRequest * p) +{ + sys_log(0, "WeddingRequest %u %u", p->dwPID1, p->dwPID2); + ForwardPacket(HEADER_DG_WEDDING_REQUEST, p, sizeof(TPacketWeddingRequest)); + //marriage::CManager::instance().RegisterWedding(p->dwPID1, p->szName1, p->dwPID2, p->szName2); +} + +void CClientManager::WeddingReady(TPacketWeddingReady * p) +{ + sys_log(0, "WeddingReady %u %u", p->dwPID1, p->dwPID2); + ForwardPacket(HEADER_DG_WEDDING_READY, p, sizeof(TPacketWeddingReady)); + marriage::CManager::instance().ReadyWedding(p->dwMapIndex, p->dwPID1, p->dwPID2); +} + +void CClientManager::WeddingEnd(TPacketWeddingEnd * p) +{ + sys_log(0, "WeddingEnd %u %u", p->dwPID1, p->dwPID2); + marriage::CManager::instance().EndWedding(p->dwPID1, p->dwPID2); +} + +// + +// +void CClientManager::MyshopPricelistUpdate(const TItemPriceListTable* pPacket) // @fixme403 (TPacketMyshopPricelistHeader to TItemPriceListTable) +{ + if (pPacket->byCount > SHOP_PRICELIST_MAX_NUM) + { + sys_err("count overflow!"); + return; + } + + CItemPriceListTableCache* pCache = GetItemPriceListCache(pPacket->dwOwnerID); + + if (pCache) + { + TItemPriceListTable table; + + table.dwOwnerID = pPacket->dwOwnerID; + table.byCount = pPacket->byCount; + + thecore_memcpy(table.aPriceInfo, pPacket->aPriceInfo, sizeof(TItemPriceInfo) * pPacket->byCount); + + pCache->UpdateList(&table); + } + else + { + TItemPriceListTable* pUpdateTable = new TItemPriceListTable; + + pUpdateTable->dwOwnerID = pPacket->dwOwnerID; + pUpdateTable->byCount = pPacket->byCount; + + thecore_memcpy(pUpdateTable->aPriceInfo, pPacket->aPriceInfo, sizeof(TItemPriceInfo) * pPacket->byCount); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT item_vnum, price FROM myshop_pricelist%s WHERE owner_id=%u", GetTablePostfix(), pPacket->dwOwnerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_LOAD_FOR_UPDATE, 0, pUpdateTable); + } +} + +// MYSHOP_PRICE_LIST + +// +void CClientManager::MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID) +{ + if (CItemPriceListTableCache* pCache = GetItemPriceListCache(dwPlayerID)) + { + sys_log(0, "Cache MyShopPricelist handle[%d] pid[%d]", dwHandle, dwPlayerID); + + TItemPriceListTable* pTable = pCache->Get(false); + + TPacketMyshopPricelistHeader header = + { + pTable->dwOwnerID, + pTable->byCount + }; + + size_t sizePriceListSize = sizeof(TItemPriceInfo) * pTable->byCount; + + peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, dwHandle, sizeof(header) + sizePriceListSize); + peer->Encode(header); + peer->Encode(pTable->aPriceInfo, sizePriceListSize); + + } + else + { + sys_log(0, "Query MyShopPricelist handle[%d] pid[%d]", dwHandle, dwPlayerID); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT item_vnum, price FROM myshop_pricelist%s WHERE owner_id=%u", GetTablePostfix(), dwPlayerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_LOAD, peer->GetHandle(), new TItemPricelistReqInfo(dwHandle, dwPlayerID)); + } +} +// END_OF_MYSHOP_PRICE_LIST + +void CPacketInfo::Add(int header) +{ + itertype(m_map_info) it = m_map_info.find(header); + + if (it == m_map_info.end()) + m_map_info.emplace(header, 1); + else + ++it->second; +} + +void CPacketInfo::Reset() +{ + m_map_info.clear(); +} + +void CClientManager::ProcessPackets(CPeer * peer) +{ + BYTE header; + DWORD dwHandle; + DWORD dwLength; + const char * data = NULL; + int i = 0; + int iCount = 0; + + while (peer->PeekPacket(i, header, dwHandle, dwLength, &data)) + { + // DISABLE_DB_HEADER_LOG + // sys_log(0, "header %d %p size %d", header, this, dwLength); + // END_OF_DISABLE_DB_HEADER_LOG + m_bLastHeader = header; + ++iCount; + + if (g_test_server) + { + if (header != HEADER_GD_PLAYER_COUNT) + sys_log(0, " ProcessPacket Header [%d] Handle[%d] Length[%d] iCount[%d]", header, dwHandle, dwLength, iCount); + } + + switch (header) + { + case HEADER_GD_BOOT: + QUERY_BOOT(peer, (TPacketGDBoot *) data); + break; + + case HEADER_GD_LOGIN_BY_KEY: + QUERY_LOGIN_BY_KEY(peer, dwHandle, (TPacketGDLoginByKey *) data); + break; + + case HEADER_GD_LOGOUT: + QUERY_LOGOUT(peer, dwHandle, data); + break; + + case HEADER_GD_PLAYER_LOAD: + sys_log(1, "HEADER_GD_PLAYER_LOAD (handle: %d length: %d)", dwHandle, dwLength); + QUERY_PLAYER_LOAD(peer, dwHandle, (TPlayerLoadPacket *) data); + break; + + case HEADER_GD_PLAYER_SAVE: + sys_log(1, "HEADER_GD_PLAYER_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_PLAYER_SAVE(peer, dwHandle, (TPlayerTable *) data); + break; + + case HEADER_GD_PLAYER_CREATE: + sys_log(0, "HEADER_GD_PLAYER_CREATE (handle: %d length: %d)", dwHandle, dwLength); + __QUERY_PLAYER_CREATE(peer, dwHandle, (TPlayerCreatePacket *) data); + sys_log(0, "END"); + break; + + case HEADER_GD_PLAYER_DELETE: + sys_log(1, "HEADER_GD_PLAYER_DELETE (handle: %d length: %d)", dwHandle, dwLength); + __QUERY_PLAYER_DELETE(peer, dwHandle, (TPlayerDeletePacket *) data); + break; + + case HEADER_GD_PLAYER_COUNT: + QUERY_PLAYER_COUNT(peer, (TPlayerCountPacket *) data); + break; + + case HEADER_GD_QUEST_SAVE: + sys_log(1, "HEADER_GD_QUEST_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_QUEST_SAVE(peer, (TQuestTable *) data, dwLength); + break; + + case HEADER_GD_SAFEBOX_LOAD: + QUERY_SAFEBOX_LOAD(peer, dwHandle, (TSafeboxLoadPacket *) data, 0); + break; + + case HEADER_GD_SAFEBOX_SAVE: + sys_log(1, "HEADER_GD_SAFEBOX_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_SAFEBOX_SAVE(peer, (TSafeboxTable *) data); + break; + + case HEADER_GD_SAFEBOX_CHANGE_SIZE: + QUERY_SAFEBOX_CHANGE_SIZE(peer, dwHandle, (TSafeboxChangeSizePacket *) data); + break; + + case HEADER_GD_SAFEBOX_CHANGE_PASSWORD: + QUERY_SAFEBOX_CHANGE_PASSWORD(peer, dwHandle, (TSafeboxChangePasswordPacket *) data); + break; + + case HEADER_GD_MALL_LOAD: + QUERY_SAFEBOX_LOAD(peer, dwHandle, (TSafeboxLoadPacket *) data, 1); + break; + + case HEADER_GD_EMPIRE_SELECT: + QUERY_EMPIRE_SELECT(peer, dwHandle, (TEmpireSelectPacket *) data); + break; + + case HEADER_GD_SETUP: + QUERY_SETUP(peer, dwHandle, data); + break; + + case HEADER_GD_GUILD_CREATE: + GuildCreate(peer, *(DWORD *) data); + break; + + case HEADER_GD_GUILD_SKILL_UPDATE: + GuildSkillUpdate(peer, (TPacketGuildSkillUpdate *) data); + break; + + case HEADER_GD_GUILD_EXP_UPDATE: + GuildExpUpdate(peer, (TPacketGuildExpUpdate *) data); + break; + + case HEADER_GD_GUILD_ADD_MEMBER: + GuildAddMember(peer, (TPacketGDGuildAddMember*) data); + break; + + case HEADER_GD_GUILD_REMOVE_MEMBER: + GuildRemoveMember(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_CHANGE_GRADE: + GuildChangeGrade(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_CHANGE_MEMBER_DATA: + GuildChangeMemberData(peer, (TPacketGuildChangeMemberData*) data); + break; + + case HEADER_GD_GUILD_DISBAND: + GuildDisband(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_WAR: + GuildWar(peer, (TPacketGuildWar*) data); + break; + + case HEADER_GD_GUILD_WAR_SCORE: + GuildWarScore(peer, (TPacketGuildWarScore*) data); + break; + + case HEADER_GD_GUILD_CHANGE_LADDER_POINT: + GuildChangeLadderPoint((TPacketGuildLadderPoint*) data); + break; + + case HEADER_GD_GUILD_USE_SKILL: + GuildUseSkill((TPacketGuildUseSkill*) data); + break; + + case HEADER_GD_ITEM_SAVE: + QUERY_ITEM_SAVE(peer, data); + break; + + case HEADER_GD_ITEM_DESTROY: + QUERY_ITEM_DESTROY(peer, data); + break; + + case HEADER_GD_ITEM_FLUSH: + QUERY_ITEM_FLUSH(peer, data); + break; + + case HEADER_GD_ADD_AFFECT: + sys_log(1, "HEADER_GD_ADD_AFFECT"); + QUERY_ADD_AFFECT(peer, (TPacketGDAddAffect *) data); + break; + + case HEADER_GD_REMOVE_AFFECT: + sys_log(1, "HEADER_GD_REMOVE_AFFECT"); + QUERY_REMOVE_AFFECT(peer, (TPacketGDRemoveAffect *) data); + break; + + case HEADER_GD_PARTY_CREATE: + QUERY_PARTY_CREATE(peer, (TPacketPartyCreate*) data); + break; + + case HEADER_GD_PARTY_DELETE: + QUERY_PARTY_DELETE(peer, (TPacketPartyDelete*) data); + break; + + case HEADER_GD_PARTY_ADD: + QUERY_PARTY_ADD(peer, (TPacketPartyAdd*) data); + break; + + case HEADER_GD_PARTY_REMOVE: + QUERY_PARTY_REMOVE(peer, (TPacketPartyRemove*) data); + break; + + case HEADER_GD_PARTY_STATE_CHANGE: + QUERY_PARTY_STATE_CHANGE(peer, (TPacketPartyStateChange*) data); + break; + + case HEADER_GD_PARTY_SET_MEMBER_LEVEL: + QUERY_PARTY_SET_MEMBER_LEVEL(peer, (TPacketPartySetMemberLevel*) data); + break; + + case HEADER_GD_RELOAD_PROTO: + QUERY_RELOAD_PROTO(); + break; + + case HEADER_GD_CHANGE_NAME: + QUERY_CHANGE_NAME(peer, dwHandle, (TPacketGDChangeName *) data); + break; + + case HEADER_GD_AUTH_LOGIN: + QUERY_AUTH_LOGIN(peer, dwHandle, (TPacketGDAuthLogin *) data); + break; + + case HEADER_GD_REQUEST_GUILD_PRIV: + AddGuildPriv((TPacketGiveGuildPriv*)data); + break; + + case HEADER_GD_REQUEST_EMPIRE_PRIV: + AddEmpirePriv((TPacketGiveEmpirePriv*)data); + break; + + case HEADER_GD_REQUEST_CHARACTER_PRIV: + AddCharacterPriv((TPacketGiveCharacterPriv*) data); + break; + + case HEADER_GD_MONEY_LOG: + MoneyLog((TPacketMoneyLog*)data); + break; + + case HEADER_GD_GUILD_DEPOSIT_MONEY: + GuildDepositMoney((TPacketGDGuildMoney*)data); + break; + + case HEADER_GD_GUILD_WITHDRAW_MONEY: + GuildWithdrawMoney(peer, (TPacketGDGuildMoney*)data); + break; + + case HEADER_GD_GUILD_WITHDRAW_MONEY_GIVE_REPLY: + GuildWithdrawMoneyGiveReply((TPacketGDGuildMoneyWithdrawGiveReply*)data); + break; + + case HEADER_GD_GUILD_WAR_BET: + GuildWarBet((TPacketGDGuildWarBet *) data); + break; + + case HEADER_GD_SET_EVENT_FLAG: + SetEventFlag((TPacketSetEventFlag*) data); + break; + + case HEADER_GD_BILLING_EXPIRE: + BillingExpire((TPacketBillingExpire *) data); + break; + + case HEADER_GD_BILLING_CHECK: + BillingCheck(data); + break; + + case HEADER_GD_CREATE_OBJECT: + CreateObject((TPacketGDCreateObject *) data); + break; + + case HEADER_GD_DELETE_OBJECT: + DeleteObject(*(DWORD *) data); + break; + + case HEADER_GD_UPDATE_LAND: + UpdateLand((DWORD *) data); + break; + + case HEADER_GD_MARRIAGE_ADD: + MarriageAdd((TPacketMarriageAdd *) data); + break; + + case HEADER_GD_MARRIAGE_UPDATE: + MarriageUpdate((TPacketMarriageUpdate *) data); + break; + + case HEADER_GD_MARRIAGE_REMOVE: + MarriageRemove((TPacketMarriageRemove *) data); + break; + + case HEADER_GD_WEDDING_REQUEST: + WeddingRequest((TPacketWeddingRequest *) data); + break; + + case HEADER_GD_WEDDING_READY: + WeddingReady((TPacketWeddingReady *) data); + break; + + case HEADER_GD_WEDDING_END: + WeddingEnd((TPacketWeddingEnd *) data); + break; + + // BLOCK_CHAT + case HEADER_GD_BLOCK_CHAT: + BlockChat((TPacketBlockChat *) data); + break; + // END_OF_BLOCK_CHAT + + // MYSHOP_PRICE_LIST + case HEADER_GD_MYSHOP_PRICELIST_UPDATE: + MyshopPricelistUpdate((TItemPriceListTable*)data); // @fixme403 (TPacketMyshopPricelistHeader to TItemPriceListTable) + break; + + case HEADER_GD_MYSHOP_PRICELIST_REQ: + MyshopPricelistRequest(peer, dwHandle, *(DWORD*)data); + break; + // END_OF_MYSHOP_PRICE_LIST + + //RELOAD_ADMIN + case HEADER_GD_RELOAD_ADMIN: + ReloadAdmin(peer, (TPacketReloadAdmin*)data); + break; + //END_RELOAD_ADMIN + + case HEADER_GD_BREAK_MARRIAGE: + BreakMarriage(peer, data); + break; + + //MOANRCH + case HEADER_GD_ELECT_MONARCH: + Election(peer, dwHandle, data); + break; + + case HEADER_GD_CANDIDACY: + Candidacy(peer, dwHandle, data); + break; + + case HEADER_GD_ADD_MONARCH_MONEY: + AddMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_DEC_MONARCH_MONEY: + DecMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_TAKE_MONARCH_MONEY: + TakeMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_COME_TO_VOTE: + ComeToVote(peer, dwHandle, data); + break; + + case HEADER_GD_RMCANDIDACY: + RMCandidacy(peer, dwHandle, data); + break; + + case HEADER_GD_SETMONARCH: + SetMonarch(peer, dwHandle, data); + break; + + case HEADER_GD_RMMONARCH: + RMMonarch(peer, dwHandle, data); + break; + //END_MONARCH + + case HEADER_GD_CHANGE_MONARCH_LORD : + ChangeMonarchLord(peer, dwHandle, (TPacketChangeMonarchLord*)data); + break; + + case HEADER_GD_REQ_SPARE_ITEM_ID_RANGE : + SendSpareItemIDRange(peer); + break; + + case HEADER_GD_REQ_CHANGE_GUILD_MASTER : + GuildChangeMaster((TPacketChangeGuildMaster*) data); + break; + + case HEADER_GD_UPDATE_HORSE_NAME : + UpdateHorseName((TPacketUpdateHorseName*) data, peer); + break; + + case HEADER_GD_REQ_HORSE_NAME : + AckHorseName(*(DWORD*)data, peer); + break; + + case HEADER_GD_DC: + DeleteLoginKey((TPacketDC*) data); + break; + + case HEADER_GD_VALID_LOGOUT: + ResetLastPlayerID((TPacketNeedLoginLogInfo*)data); + break; + + case HEADER_GD_REQUEST_CHARGE_CASH: + ChargeCash((TRequestChargeCash*)data); + break; + + //delete gift notify icon + + case HEADER_GD_DELETE_AWARDID: + DeleteAwardId((TPacketDeleteAwardID*) data); + break; + + case HEADER_GD_UPDATE_CHANNELSTATUS: + UpdateChannelStatus((SChannelStatus*) data); + break; + case HEADER_GD_REQUEST_CHANNELSTATUS: + RequestChannelStatus(peer, dwHandle); + break; + default: + sys_err("Unknown header (header: %d handle: %d length: %d)", header, dwHandle, dwLength); + break; + } + } + + peer->RecvEnd(i); +} + +void CClientManager::AddPeer(socket_t fd) +{ + CPeer * pPeer = new CPeer; + + if (pPeer->Accept(fd)) + m_peerList.push_front(pPeer); + else + delete pPeer; +} + +void CClientManager::RemovePeer(CPeer * pPeer) +{ + if (m_pkAuthPeer == pPeer) + { + m_pkAuthPeer = NULL; + } + else + { + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = it->second; + + if (pkLD->GetConnectedPeerHandle() == pPeer->GetHandle()) + { + if (pkLD->IsPlay()) + { + pkLD->SetPlay(false); + SendLoginToBilling(pkLD, false); + } + + if (pkLD->IsDeleted()) + { + sys_log(0, "DELETING LoginData"); + delete pkLD; + } + + m_map_kLogonAccount.erase(it++); + } + else + ++it; + } + } + + m_peerList.remove(pPeer); + delete pPeer; +} + +CPeer * CClientManager::GetPeer(IDENT ident) +{ + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end();++i) + { + CPeer * tmp = *i; + + if (tmp->GetHandle() == ident) + return tmp; + } + + return NULL; +} + +CPeer * CClientManager::GetAnyPeer() +{ + if (m_peerList.empty()) + return NULL; + + return m_peerList.front(); +} + +// +int CClientManager::AnalyzeQueryResult(SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + CPeer * peer = GetPeer(qi->dwIdent); + +#ifdef _TEST + if (qi->iType != QID_ITEM_AWARD_LOAD) + sys_log(0, "AnalyzeQueryResult %d", qi->iType); +#endif + switch (qi->iType) + { + case QID_ITEM_AWARD_LOAD: + ItemAwardManager::instance().Load(msg); + delete qi; + return true; + + case QID_GUILD_RANKING: + CGuildManager::instance().ResultRanking(msg->Get()->pSQLResult); + break; + + // MYSHOP_PRICE_LIST + case QID_ITEMPRICE_LOAD_FOR_UPDATE: + RESULT_PRICELIST_LOAD_FOR_UPDATE(msg); + break; + // END_OF_MYSHOP_PRICE_LIST + } + + if (!peer) + { + //sys_err("CClientManager::AnalyzeQueryResult: peer not exist anymore. (ident: %d)", qi->dwIdent); + delete qi; + return true; + } + + switch (qi->iType) + { + case QID_PLAYER: + case QID_ITEM: + case QID_QUEST: + case QID_AFFECT: + RESULT_COMPOSITE_PLAYER(peer, msg, qi->iType); + break; + + case QID_LOGIN: + RESULT_LOGIN(peer, msg); + break; + + case QID_SAFEBOX_LOAD: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_LOAD"); + RESULT_SAFEBOX_LOAD(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_SIZE: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_SIZE"); + RESULT_SAFEBOX_CHANGE_SIZE(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_PASSWORD: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_PASSWORD %p", msg); + RESULT_SAFEBOX_CHANGE_PASSWORD(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_PASSWORD_SECOND: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_PASSWORD %p", msg); + RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(peer, msg); + break; + + case QID_SAFEBOX_SAVE: + case QID_ITEM_SAVE: + case QID_ITEM_DESTROY: + case QID_QUEST_SAVE: + case QID_PLAYER_SAVE: + case QID_ITEM_AWARD_TAKEN: + break; + + // PLAYER_INDEX_CREATE_BUG_FIX + case QID_PLAYER_INDEX_CREATE: + RESULT_PLAYER_INDEX_CREATE(peer, msg); + break; + // END_PLAYER_INDEX_CREATE_BUG_FIX + + case QID_PLAYER_DELETE: + __RESULT_PLAYER_DELETE(peer, msg); + break; + + case QID_LOGIN_BY_KEY: + RESULT_LOGIN_BY_KEY(peer, msg); + break; + + // MYSHOP_PRICE_LIST + case QID_ITEMPRICE_LOAD: + RESULT_PRICELIST_LOAD(peer, msg); + break; + // END_OF_MYSHOP_PRICE_LIST + + default: + sys_log(0, "CClientManager::AnalyzeQueryResult unknown query result type: %d, str: %s", qi->iType, msg->stQuery.c_str()); + break; + } + + delete qi; + return true; +} + +void UsageLog() +{ + FILE* fp = NULL; + + time_t ct; + char *time_s; + struct tm lt; + + int avg = g_dwUsageAvg / 3600; + + fp = fopen("usage.txt", "a+"); + + if (!fp) + return; + + ct = time(0); + lt = *localtime(&ct); + time_s = asctime(<); + + time_s[strlen(time_s) - 1] = '\0'; + + fprintf(fp, "| %4d %-15.15s | %5d | %5u |", lt.tm_year + 1900, time_s + 4, avg, g_dwUsageMax); + + fprintf(fp, "\n"); + fclose(fp); + + g_dwUsageMax = g_dwUsageAvg = 0; +} + +#define ENABLE_ITEMAWARD_REFRESH +int CClientManager::Process() +{ + int pulses; + + if (!(pulses = thecore_idle())) + return 0; + + while (pulses--) + { + ++thecore_heart->pulse; + + if (!(thecore_heart->pulse % thecore_heart->passes_per_sec)) + { + if (g_test_server) + { + if (!(thecore_heart->pulse % thecore_heart->passes_per_sec * 10)) + + { + pt_log("[%9d] return %d/%d/%d/%d async %d/%d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountReturnCopiedQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncCopiedQuery(SQL_PLAYER)); + + if ((thecore_heart->pulse % 50) == 0) + sys_log(0, "[%9d] return %d/%d/%d async %d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER)); + } + } + else + { + pt_log("[%9d] return %d/%d/%d/%d async %d/%d/%d%/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountReturnCopiedQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncCopiedQuery(SQL_PLAYER)); + + if ((thecore_heart->pulse % 50) == 0) + sys_log(0, "[%9d] return %d/%d/%d async %d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER)); + } + + CDBManager::instance().ResetCounter(); + + DWORD dwCount = CClientManager::instance().GetUserCount(); + + g_dwUsageAvg += dwCount; + g_dwUsageMax = MAX(g_dwUsageMax, dwCount); + + memset(&thecore_profiler[0], 0, sizeof(thecore_profiler)); + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) + UsageLog(); + + m_iCacheFlushCount = 0; + + UpdatePlayerCache(); + + UpdateItemCache(); + + UpdateLogoutPlayer(); + + // MYSHOP_PRICE_LIST + UpdateItemPriceListCache(); + // END_OF_MYSHOP_PRICE_LIST + + CGuildManager::instance().Update(); + CPrivManager::instance().Update(); + marriage::CManager::instance().Update(); + } +#ifdef ENABLE_ITEMAWARD_REFRESH + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 5))) + { + ItemAwardManager::instance().RequestLoad(); + } +#endif + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 10))) + { + pt_log("QUERY: MAIN[%d] ASYNC[%d]", g_query_count[0], g_query_count[1]); + g_query_count[0] = 0; + g_query_count[1] = 0; + ///////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////// + pt_log("ITEM:%d\n", g_item_count); + g_item_count = 0; + ///////////////////////////////////////////////////////////////// + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 60))) + { + CClientManager::instance().SendTime(); + + std::string st; + CClientManager::instance().GetPeerP2PHostNames(st); + sys_log(0, "Current Peer host names...\n%s", st.c_str()); + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) + { + CMoneyLog::instance().Save(); + } + } + + int num_events = fdwatch(m_fdWatcher, 0); + int idx; + CPeer * peer; + + for (idx = 0; idx < num_events; ++idx) + { + peer = (CPeer *) fdwatch_get_client_data(m_fdWatcher, idx); + + if (!peer) + { + if (fdwatch_check_event(m_fdWatcher, m_fdAccept, idx) == FDW_READ) + { + AddPeer(m_fdAccept); + fdwatch_clear_event(m_fdWatcher, m_fdAccept, idx); + } + else + { + sys_log(0, "FDWATCH: peer null in event: ident %d", fdwatch_get_ident(m_fdWatcher, idx)); // @warme012 + } + + continue; + } + + switch (fdwatch_check_event(m_fdWatcher, peer->GetFd(), idx)) + { + case FDW_READ: + if (peer->Recv() < 0) + { + sys_err("Recv failed"); + RemovePeer(peer); + } + else + { + if (peer == m_pkAuthPeer) + if (g_log) + sys_log(0, "AUTH_PEER_READ: size %d", peer->GetRecvLength()); + + ProcessPackets(peer); + } + break; + + case FDW_WRITE: + if (peer == m_pkAuthPeer) + if (g_log) + sys_log(0, "AUTH_PEER_WRITE: size %d", peer->GetSendLength()); + + if (peer->Send() < 0) + { + sys_err("Send failed"); + RemovePeer(peer); + } + + break; + + case FDW_EOF: + RemovePeer(peer); + break; + + default: + sys_err("fdwatch_check_fd returned unknown result"); + RemovePeer(peer); + break; + } + } + +#ifdef __WIN32__ + if (_kbhit()) { + int c = _getch(); + switch (c) { + case 0x1b: // Esc + return 0; // shutdown + break; + default: + break; + } + } +#endif + + return 1; +} + +DWORD CClientManager::GetUserCount() +{ + return m_map_kLogonAccount.size(); +} + +void CClientManager::SendAllGuildSkillRechargePacket() +{ + ForwardPacket(HEADER_DG_GUILD_SKILL_RECHARGE, NULL, 0); +} + +void CClientManager::SendTime() +{ + time_t now = GetCurrentTime(); + ForwardPacket(HEADER_DG_TIME, &now, sizeof(time_t)); +} + +void CClientManager::ForwardPacket(BYTE header, const void* data, int size, BYTE bChannel, CPeer* except) +{ + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * peer = *it; + + if (peer == except) + continue; + + if (!peer->GetChannel()) + continue; + + if (bChannel && peer->GetChannel() != bChannel) + continue; + + peer->EncodeHeader(header, 0, size); + + if (size > 0 && data) + peer->Encode(data, size); + } +} + +void CClientManager::SendNotice(const char * c_pszFormat, ...) +{ + char szBuf[255+1]; + va_list args; + + va_start(args, c_pszFormat); + int len = vsnprintf(szBuf, sizeof(szBuf), c_pszFormat, args); + va_end(args); + szBuf[len] = '\0'; + + ForwardPacket(HEADER_DG_NOTICE, szBuf, len + 1); +} + +time_t CClientManager::GetCurrentTime() +{ + return time(0); +} + +// ITEM_UNIQUE_ID +bool CClientManager::InitializeNowItemID() +{ + DWORD dwMin, dwMax; + + if (!CConfig::instance().GetTwoValue("ITEM_ID_RANGE", &dwMin, &dwMax)) + { + sys_err("conf.txt: Cannot find ITEM_ID_RANGE [start_item_id] [end_item_id]"); + return false; + } + + sys_log(0, "ItemRange From File %u ~ %u ", dwMin, dwMax); + + if (CItemIDRangeManager::instance().BuildRange(dwMin, dwMax, m_itemRange) == false) + { + sys_err("Can not build ITEM_ID_RANGE"); + return false; + } + + sys_log(0, " Init Success Start %u End %u Now %u\n", m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin); + + return true; +} + +DWORD CClientManager::GainItemID() +{ + return m_itemRange.dwUsableItemIDMin++; +} + +DWORD CClientManager::GetItemID() +{ + return m_itemRange.dwUsableItemIDMin; +} +// ITEM_UNIQUE_ID_END +//BOOT_LOCALIZATION + +bool CClientManager::InitializeLocalization() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "SELECT mValue, mKey FROM locale"); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + if (pMsg->Get()->uiNumRows == 0) + { + sys_err("InitializeLocalization() ==> DirectQuery failed(%s)", szQuery); + return false; + } + + sys_log(0, "InitializeLocalization() - LoadLocaleTable(count:%d)", pMsg->Get()->uiNumRows); + + m_vec_Locale.clear(); + + MYSQL_ROW row = NULL; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != nullptr) + { + int col = 0; + tLocale locale; + + strlcpy(locale.szValue, row[col++], sizeof(locale.szValue)); + strlcpy(locale.szKey, row[col++], sizeof(locale.szKey)); + + //DB_NAME_COLUMN Setting + if (strcmp(locale.szKey, "LOCALE") == 0) + { + if (strcmp(locale.szValue, "cibn") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "gb2312"); + + g_stLocale = "gb2312"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "ymir") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "japan") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "sjis"); + + g_stLocale = "sjis"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "english") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = ""; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "germany") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "france") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "italy") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "spain") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "uk") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "turkey") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin5"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "poland") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "portugal") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "hongkong") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "big5"); + + g_stLocale = "big5"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "newcibn") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "gb2312"); + + g_stLocale = "gb2312"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "korea") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "canada") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "brazil") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "greek") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "greek"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "russia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "denmark") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "bulgaria") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "croatia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "mexico") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "arabia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "cp1256"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "czech") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "hungary") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "romania") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "netherlands") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "singapore") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "vietnam") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "thailand") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "usa") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "we_korea") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "taiwan") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "big5"); + g_stLocale = "big5"; + g_stLocaleNameColumn = "locale_name"; + } + else + { + sys_err("locale[LOCALE] = UNKNOWN(%s)", locale.szValue); + exit(0); + } + // @warme007 + // sys_log(0,"before call SetLocale: %s",g_stLocale.c_str()); + // CDBManager::instance().SetLocale(g_stLocale.c_str()); + // sys_log(0,"Called SetLocale"); + } + else if (strcmp(locale.szKey, "DB_NAME_COLUMN") == 0) + { + sys_log(0, "locale[DB_NAME_COLUMN] = %s", locale.szValue); + g_stLocaleNameColumn = locale.szValue; + } + else + { + sys_log(0, "locale[UNKNOWN_KEY(%s)] = %s", locale.szKey, locale.szValue); + } + m_vec_Locale.emplace_back(locale); + } + + return true; +} +//END_BOOT_LOCALIZATION +//ADMIN_MANAGER + +bool CClientManager::__GetAdminInfo(const char *szIP, std::vector & rAdminVec) +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "SELECT mID,mAccount,mName,mContactIP,mServerIP,mAuthority FROM gmlist WHERE mServerIP='ALL' or mServerIP='%s'", + szIP ? szIP : "ALL"); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + + if (pMsg->Get()->uiNumRows == 0) + { + // sys_err("__GetAdminInfo() ==> DirectQuery failed(%s)", szQuery); // @warme013 + return false; + } + + MYSQL_ROW row; + rAdminVec.reserve(pMsg->Get()->uiNumRows); + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + int idx = 0; + tAdminInfo Info; + + str_to_number(Info.m_ID, row[idx++]); + trim_and_lower(row[idx++], Info.m_szAccount, sizeof(Info.m_szAccount)); + strlcpy(Info.m_szName, row[idx++], sizeof(Info.m_szName)); + strlcpy(Info.m_szContactIP, row[idx++], sizeof(Info.m_szContactIP)); + strlcpy(Info.m_szServerIP, row[idx++], sizeof(Info.m_szServerIP)); + std::string stAuth = row[idx++]; + + if (!stAuth.compare("IMPLEMENTOR")) + Info.m_Authority = GM_IMPLEMENTOR; + else if (!stAuth.compare("GOD")) + Info.m_Authority = GM_GOD; + else if (!stAuth.compare("HIGH_WIZARD")) + Info.m_Authority = GM_HIGH_WIZARD; + else if (!stAuth.compare("LOW_WIZARD")) + Info.m_Authority = GM_LOW_WIZARD; + else if (!stAuth.compare("WIZARD")) + Info.m_Authority = GM_WIZARD; + else + continue; + + rAdminVec.emplace_back(Info); + + sys_log(0, "GM: PID %u Login %s Character %s ContactIP %s ServerIP %s Authority %d[%s]", + Info.m_ID, Info.m_szAccount, Info.m_szName, Info.m_szContactIP, Info.m_szServerIP, Info.m_Authority, stAuth.c_str()); + } + + return true; +} + +bool CClientManager::__GetHostInfo(std::vector & rIPVec) +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "SELECT mIP FROM gmhost"); + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + + if (pMsg->Get()->uiNumRows == 0) + { + // sys_err("__GetHostInfo() ==> DirectQuery failed(%s)", szQuery); // @warme013 + return false; + } + + rIPVec.reserve(pMsg->Get()->uiNumRows); + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + if (row[0] && *row[0]) + { + rIPVec.emplace_back(row[0]); + sys_log(0, "GMHOST: %s", row[0]); + } + } + + return true; +} +//END_ADMIN_MANAGER + +void CClientManager::ReloadAdmin(CPeer*, TPacketReloadAdmin* p) +{ + std::vector vAdmin; + std::vector vHost; + + __GetHostInfo(vHost); + __GetAdminInfo(p->szIP, vAdmin); + + DWORD dwPacketSize = sizeof(WORD) + sizeof (WORD) + sizeof(tAdminInfo) * vAdmin.size() + + sizeof(WORD) + sizeof(WORD) + 16 * vHost.size(); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * peer = *it; + + if (!peer->GetChannel()) + continue; + + peer->EncodeHeader(HEADER_DG_RELOAD_ADMIN, 0, dwPacketSize); + + peer->EncodeWORD(16); + peer->EncodeWORD(vHost.size()); + + for (size_t n = 0; n < vHost.size(); ++n) + peer->Encode(vHost[n].c_str(), 16); + + peer->EncodeWORD(sizeof(tAdminInfo)); + peer->EncodeWORD(vAdmin.size()); + + for (size_t n = 0; n < vAdmin.size(); ++n) + peer->Encode(vAdmin[n]); + } + + sys_log(0, "ReloadAdmin End %s", p->szIP); +} + +//BREAK_MARRIAGE +void CClientManager::BreakMarriage(CPeer * peer, const char * data) +{ + DWORD pid1, pid2; + + pid1 = *(int *) data; + data += sizeof(int); + + pid2 = *(int *) data; + data += sizeof(int); + + sys_log(0, "Breaking off a marriage engagement! pid %d and pid %d", pid1, pid2); + marriage::CManager::instance().Remove(pid1, pid2); +} +//END_BREAK_MARIIAGE + +void CClientManager::UpdateItemCacheSet(DWORD pid) +{ + itertype(m_map_pkItemCacheSetPtr) it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + { + if (g_test_server) + sys_log(0, "UPDATE_ITEMCACHESET : UpdateItemCacheSet ==> No ItemCacheSet pid(%d)", pid); + return; + } + + TItemCacheSet * pSet = it->second; + TItemCacheSet::iterator it_set = pSet->begin(); + + while (it_set != pSet->end()) + { + CItemCache * c = *it_set++; + c->Flush(); + } + + if (g_log) + sys_log(0, "UPDATE_ITEMCACHESET : UpdateItemCachsSet pid(%d)", pid); +} + +void CClientManager::Election(CPeer * peer, DWORD dwHandle, const char* data) +{ + DWORD idx; + DWORD selectingpid; + + idx = *(DWORD *) data; + data += sizeof(DWORD); + + selectingpid = *(DWORD *) data; + data += sizeof(DWORD); + + int Success = 0; + + if (!(Success = CMonarch::instance().VoteMonarch(selectingpid, idx))) + { + if (g_test_server) + sys_log(0, "[MONARCH_VOTE] Failed %d %d", idx, selectingpid); + peer->EncodeHeader(HEADER_DG_ELECT_MONARCH, dwHandle, sizeof(int)); + peer->Encode(Success); + return; + } + else + { + if (g_test_server) + sys_log(0, "[MONARCH_VOTE] Success %d %d", idx, selectingpid); + peer->EncodeHeader(HEADER_DG_ELECT_MONARCH, dwHandle, sizeof(int)); + peer->Encode(Success); + return; + } +} +void CClientManager::Candidacy(CPeer * peer, DWORD dwHandle, const char* data) +{ + DWORD pid; + + pid = *(DWORD *) data; + data += sizeof(DWORD); + + if (!CMonarch::instance().AddCandidacy(pid, data)) + { + if (g_test_server) + sys_log(0, "[MONARCH_CANDIDACY] Failed %d %s", pid, data); + + peer->EncodeHeader(HEADER_DG_CANDIDACY, dwHandle, sizeof(int) + 32); + peer->Encode(0, sizeof(int)); + peer->Encode(data, 32); + return; + } + else + { + if (g_test_server) + sys_log(0, "[MONARCH_CANDIDACY] Success %d %s", pid, data); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (0 && p->GetChannel() != 0) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_CANDIDACY, dwHandle, sizeof(int) + 32); + p->Encode(pid); + p->Encode(data, 32); + } + else + { + p->EncodeHeader(HEADER_DG_CANDIDACY, 0, sizeof(int) + 32); + p->Encode(pid); + p->Encode(data, 32); + } + } + } +} + +void CClientManager::AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Add money Empire(%d) Money(%d)", Empire, Money); + + CMonarch::instance().AddMoney(Empire, Money); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_ADD_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + p->Encode(Empire); + p->Encode(Money); + } + else + { + p->EncodeHeader(HEADER_DG_ADD_MONARCH_MONEY, 0, sizeof(int) + sizeof(int)); + p->Encode(Empire); + p->Encode(Money); + } + + } +} +void CClientManager::DecMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Dec money Empire(%d) Money(%d)", Empire, Money); + + CMonarch::instance().DecMoney(Empire, Money); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_DEC_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + p->Encode(Empire); + p->Encode(Money); + } + else + { + p->EncodeHeader(HEADER_DG_DEC_MONARCH_MONEY, 0, sizeof(int) + sizeof(int)); + p->Encode(Empire); + p->Encode(Money); + } + } +} + +void CClientManager::TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + DWORD pid = *(DWORD *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Take money Empire(%d) Money(%d)", Empire, Money); + + if (CMonarch::instance().TakeMoney(Empire, pid, Money) == true) + { + peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + peer->Encode(Empire); + peer->Encode(Money); + } + else + { + Money = 0; + peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + peer->Encode(Empire); + peer->Encode(Money); + } +} + +void CClientManager::ComeToVote(CPeer * peer, DWORD dwHandle, const char * data) +{ + CMonarch::instance().ElectMonarch(); +} + +void CClientManager::RMCandidacy(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + sys_log(0, "[MONARCH_GM] Remove candidacy name(%s)", szName); + + int iRet = CMonarch::instance().DelCandidacy(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + else + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } +} + +void CClientManager::SetMonarch(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + + if (g_test_server) + sys_log(0, "[MONARCH_GM] Set Monarch name(%s)", szName); + + int iRet = CMonarch::instance().SetMonarch(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + else + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } +} + +void CClientManager::RMMonarch(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + + if (g_test_server) + sys_log(0, "[MONARCH_GM] Remove Monarch name(%s)", szName); + + CMonarch::instance().DelMonarch(szName); + + int iRet = CMonarch::instance().DelMonarch(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMMONARCH, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + else + { + p->EncodeHeader(HEADER_DG_RMMONARCH, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(iRet); + p->Encode(szName); + } +} + +void CClientManager::ChangeMonarchLord(CPeer * peer, DWORD dwHandle, TPacketChangeMonarchLord* info) +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "SELECT a.name, NOW() FROM player%s AS a, player_index%s AS b WHERE (a.account_id=b.id AND a.id=%u AND b.empire=%u) AND " +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + "(b.pid1=%u OR b.pid2=%u OR b.pid3=%u OR b.pid4=%u OR b.pid5=%u)", +#else + "(b.pid1=%u OR b.pid2=%u OR b.pid3=%u OR b.pid4=%u)", +#endif + GetTablePostfix(), GetTablePostfix(), info->dwPID, info->bEmpire, +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + info->dwPID, info->dwPID, info->dwPID, info->dwPID, info->dwPID); +#else + info->dwPID, info->dwPID, info->dwPID, info->dwPID); +#endif + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + if (pMsg->Get()->uiNumRows != 0) + { + TPacketChangeMonarchLordACK ack; + ack.bEmpire = info->bEmpire; + ack.dwPID = info->dwPID; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + strlcpy(ack.szName, row[0], sizeof(ack.szName)); + strlcpy(ack.szDate, row[1], sizeof(ack.szDate)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch SET pid=%u, windate=NOW() WHERE empire=%d", ack.dwPID, ack.bEmpire); + auto pMsg2 = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + if (pMsg2->Get()->uiAffectedRows > 0) + { + CMonarch::instance().LoadMonarch(); + + TMonarchInfo* newInfo = CMonarch::instance().GetMonarch(); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); it++) + { + CPeer* client = *it; + + client->EncodeHeader(HEADER_DG_CHANGE_MONARCH_LORD_ACK, 0, sizeof(TPacketChangeMonarchLordACK)); + client->Encode(ack); + + client->EncodeHeader(HEADER_DG_UPDATE_MONARCH_INFO, 0, sizeof(TMonarchInfo)); + client->Encode(newInfo, sizeof(TMonarchInfo)); + } + } + } +} + +void CClientManager::SendSpareItemIDRange(CPeer* peer) +{ + peer->SendSpareItemIDRange(); +} + +// + +// +void CClientManager::DeleteLoginKey(TPacketDC *data) +{ + char login[LOGIN_MAX_LEN+1] = {0}; + trim_and_lower(data->login, login, sizeof(login)); + + CLoginData *pkLD = GetLoginDataByLogin(login); + + if (pkLD) + { + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(pkLD->GetKey()); + + if (it != m_map_pkLoginData.end()) + m_map_pkLoginData.erase(it); + } +} + +// delete gift notify icon +void CClientManager::DeleteAwardId(TPacketDeleteAwardID *data) +{ + //sys_log(0,"data from game server arrived %d",data->dwID); + std::map::iterator it; + it = ItemAwardManager::Instance().GetMapAward().find(data->dwID); + if ( it != ItemAwardManager::Instance().GetMapAward().end() ) + { + std::set & kSet = ItemAwardManager::Instance().GetMapkSetAwardByLogin()[it->second->szLogin]; + if(kSet.erase(it->second)) + sys_log(0,"erase ItemAward id: %d from cache", data->dwID); + ItemAwardManager::Instance().GetMapAward().erase(data->dwID); + } + else + { + sys_log(0,"DELETE_AWARDID : could not find the id: %d", data->dwID); + } +} + +void CClientManager::UpdateChannelStatus(TChannelStatus* pData) +{ + TChannelStatusMap::iterator it = m_mChannelStatus.find(pData->nPort); + if (it != m_mChannelStatus.end()) { + it->second = pData->bStatus; + } + else { + m_mChannelStatus.emplace(pData->nPort, pData->bStatus); + } +} + +void CClientManager::RequestChannelStatus(CPeer* peer, DWORD dwHandle) +{ + const int nSize = m_mChannelStatus.size(); + peer->EncodeHeader(HEADER_DG_RESPOND_CHANNELSTATUS, dwHandle, sizeof(TChannelStatus)*nSize+sizeof(int)); + peer->Encode(nSize); + for (TChannelStatusMap::iterator it = m_mChannelStatus.begin(); it != m_mChannelStatus.end(); it++) { + peer->Encode(it->first); + peer->Encode(it->second); + } +} + +void CClientManager::ResetLastPlayerID(const TPacketNeedLoginLogInfo* data) +{ + CLoginData* pkLD = GetLoginDataByAID( data->dwPlayerID ); + + if (NULL != pkLD) + { + pkLD->SetLastPlayerID( 0 ); + } +} + +void CClientManager::ChargeCash(const TRequestChargeCash* packet) +{ + char szQuery[512]; + + if (ERequestCharge_Cash == packet->eChargeType) + sprintf(szQuery, "update account set cash = cash + %d where id = %d limit 1", packet->dwAmount, packet->dwAID); + else if(ERequestCharge_Mileage == packet->eChargeType) + sprintf(szQuery, "update account set mileage = mileage + %d where id = %d limit 1", packet->dwAmount, packet->dwAID); + else + { + sys_err ("Invalid request charge type (type : %d, amount : %d, aid : %d)", packet->eChargeType, packet->dwAmount, packet->dwAID); + return; + } + + sys_err ("Request Charge (type : %d, amount : %d, aid : %d)", packet->eChargeType, packet->dwAmount, packet->dwAID); + + CDBManager::Instance().AsyncQuery(szQuery, SQL_ACCOUNT); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManager.h b/source-server/Srcs/Server/db/src/ClientManager.h new file mode 100644 index 000000000..d36e9945a --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManager.h @@ -0,0 +1,513 @@ +#ifndef __INC_CLIENTMANAGER_H__ +#define __INC_CLIENTMANAGER_H__ + +#include +#include + +#include "../../common/stl.h" +#include "../../common/building.h" + +#include "Peer.h" +#include "DBManager.h" +#include "LoginData.h" + +#define ENABLE_PROTO_FROM_DB + +class CPlayerTableCache; +class CItemCache; +class CItemPriceListTableCache; + +class CPacketInfo +{ + public: + void Add(int header); + void Reset(); + + std::map m_map_info; +}; + +size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab); + +class CClientManager : public CNetBase, public singleton +{ + public: + typedef std::list TPeerList; + typedef boost::unordered_map TPlayerTableCacheMap; + typedef boost::unordered_map TItemCacheMap; + typedef boost::unordered_set > TItemCacheSet; + typedef boost::unordered_map TItemCacheSetPtrMap; + typedef boost::unordered_map TItemPriceListCacheMap; + typedef boost::unordered_map TChannelStatusMap; + + // MYSHOP_PRICE_LIST + + typedef std::pair< DWORD, DWORD > TItemPricelistReqInfo; + // END_OF_MYSHOP_PRICE_LIST + + class ClientHandleInfo + { + public: + DWORD dwHandle; + DWORD account_id; + DWORD player_id; + BYTE account_index; + char login[LOGIN_MAX_LEN + 1]; + char safebox_password[SAFEBOX_PASSWORD_MAX_LEN + 1]; + char ip[MAX_HOST_LENGTH + 1]; + + TAccountTable * pAccountTable; + TSafeboxTable * pSafebox; + + ClientHandleInfo(DWORD argHandle, DWORD dwPID = 0) + { + dwHandle = argHandle; + pSafebox = NULL; + pAccountTable = NULL; + player_id = dwPID; + }; + + ClientHandleInfo(DWORD argHandle, DWORD dwPID, DWORD accountId) + { + dwHandle = argHandle; + pSafebox = NULL; + pAccountTable = NULL; + player_id = dwPID; + account_id = accountId; + }; + + ~ClientHandleInfo() + { + if (pSafebox) + { + delete pSafebox; + pSafebox = NULL; + } + } + }; + + public: + CClientManager(); + ~CClientManager(); + + bool Initialize(); + time_t GetCurrentTime(); + + void MainLoop(); + void Quit(); + + void GetPeerP2PHostNames(std::string& peerHostNames); + void SetTablePostfix(const char* c_pszTablePostfix); + void SetPlayerIDStart(int iIDStart); + int GetPlayerIDStart() { return m_iPlayerIDStart; } + + int GetPlayerDeleteLevelLimit() { return m_iPlayerDeleteLevelLimit; } + + void SetChinaEventServer(bool flag) { m_bChinaEventServer = flag; } + bool IsChinaEventServer() { return m_bChinaEventServer; } + + DWORD GetUserCount(); + + void SendAllGuildSkillRechargePacket(); + void SendTime(); + + CPlayerTableCache * GetPlayerCache(DWORD id); + void PutPlayerCache(TPlayerTable * pNew); + + void CreateItemCacheSet(DWORD dwID); + TItemCacheSet * GetItemCacheSet(DWORD dwID); + void FlushItemCacheSet(DWORD dwID); + + CItemCache * GetItemCache(DWORD id); + void PutItemCache(TPlayerItem * pNew, bool bSkipQuery = false); + bool DeleteItemCache(DWORD id); + + void UpdatePlayerCache(); + void UpdateItemCache(); + + // MYSHOP_PRICE_LIST + + CItemPriceListTableCache* GetItemPriceListCache(DWORD dwID); + + void PutItemPriceListCache(const TItemPriceListTable* pItemPriceList); + + void UpdateItemPriceListCache(void); + // END_OF_MYSHOP_PRICE_LIST + + void SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable); + + void SetCacheFlushCountLimit(int iLimit); + + template + Func for_each_peer(Func f); + + CPeer * GetAnyPeer(); + + void ForwardPacket(BYTE header, const void* data, int size, BYTE bChannel = 0, CPeer * except = NULL); + + void SendNotice(const char * c_pszFormat, ...); + + // @fixme203 directly GetCommand instead of strcpy + char* GetCommand(char* str, char* command); + void ItemAward(CPeer * peer, char* login); + + protected: + void Destroy(); + + private: + bool InitializeTables(); + bool InitializeShopTable(); + bool InitializeMobTable(); + bool InitializeItemTable(); + bool InitializeQuestItemTable(); + bool InitializeSkillTable(); + bool InitializeRefineTable(); + bool InitializeBanwordTable(); + bool InitializeItemAttrTable(); + bool InitializeItemRareTable(); + bool InitializeLandTable(); + bool InitializeObjectProto(); + bool InitializeObjectTable(); + bool InitializeMonarch(); + + bool MirrorMobTableIntoDB(); + bool MirrorItemTableIntoDB(); + + void AddPeer(socket_t fd); + void RemovePeer(CPeer * pPeer); + CPeer * GetPeer(IDENT ident); + + int AnalyzeQueryResult(SQLMsg * msg); + int AnalyzeErrorMsg(CPeer * peer, SQLMsg * msg); + + int Process(); + + void ProcessPackets(CPeer * peer); + + CLoginData * GetLoginData(DWORD dwKey); + CLoginData * GetLoginDataByLogin(const char * c_pszLogin); + CLoginData * GetLoginDataByAID(DWORD dwAID); + + void InsertLoginData(CLoginData * pkLD); + void DeleteLoginData(CLoginData * pkLD); + + bool InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP); + bool DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle); + bool FindLogonAccount(const char * c_pszLogin); + + void GuildCreate(CPeer * peer, DWORD dwGuildID); + void GuildSkillUpdate(CPeer * peer, TPacketGuildSkillUpdate* p); + void GuildExpUpdate(CPeer * peer, TPacketGuildExpUpdate* p); + void GuildAddMember(CPeer * peer, TPacketGDGuildAddMember* p); + void GuildChangeGrade(CPeer * peer, TPacketGuild* p); + void GuildRemoveMember(CPeer * peer, TPacketGuild* p); + void GuildChangeMemberData(CPeer * peer, TPacketGuildChangeMemberData* p); + void GuildDisband(CPeer * peer, TPacketGuild * p); + void GuildWar(CPeer * peer, TPacketGuildWar * p); + void GuildWarScore(CPeer * peer, TPacketGuildWarScore * p); + void GuildChangeLadderPoint(TPacketGuildLadderPoint* p); + void GuildUseSkill(TPacketGuildUseSkill* p); + void GuildDepositMoney(TPacketGDGuildMoney* p); + void GuildWithdrawMoney(CPeer* peer, TPacketGDGuildMoney* p); + void GuildWithdrawMoneyGiveReply(TPacketGDGuildMoneyWithdrawGiveReply* p); + void GuildWarBet(TPacketGDGuildWarBet * p); + void GuildChangeMaster(TPacketChangeGuildMaster* p); + + void SetGuildWarEndTime(DWORD guild_id1, DWORD guild_id2, time_t tEndTime); + + void QUERY_BOOT(CPeer * peer, TPacketGDBoot * p); + + void QUERY_LOGIN(CPeer * peer, DWORD dwHandle, SLoginPacket* data); + void QUERY_LOGOUT(CPeer * peer, DWORD dwHandle, const char *); + + void RESULT_LOGIN(CPeer * peer, SQLMsg *msg); + + void QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket*); + void RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID); + void RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo); + void RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID); + void RESULT_QUEST_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID); + // @fixme402 (RESULT_AFFECT_LOAD +dwRealPID) + void RESULT_AFFECT_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwRealPID); + + // PLAYER_INDEX_CREATE_BUG_FIX + void RESULT_PLAYER_INDEX_CREATE(CPeer *pkPeer, SQLMsg *msg); + // END_PLAYER_INDEX_CREATE_BUG_FIX + + // MYSHOP_PRICE_LIST + + void RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg); + + void RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg); + // END_OF_MYSHOP_PRICE_LIST + + void QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable*); + + void __QUERY_PLAYER_CREATE(CPeer * peer, DWORD dwHandle, TPlayerCreatePacket *); + void __QUERY_PLAYER_DELETE(CPeer * peer, DWORD dwHandle, TPlayerDeletePacket *); + void __RESULT_PLAYER_DELETE(CPeer * peer, SQLMsg* msg); + + void QUERY_PLAYER_COUNT(CPeer * pkPeer, TPlayerCountPacket *); + + void QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData); + void QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData); + void QUERY_ITEM_FLUSH(CPeer * pkPeer, const char * c_pData); + + void QUERY_QUEST_SAVE(CPeer * pkPeer, TQuestTable *, DWORD dwLen); + void QUERY_ADD_AFFECT(CPeer * pkPeer, TPacketGDAddAffect * p); + void QUERY_REMOVE_AFFECT(CPeer * pkPeer, TPacketGDRemoveAffect * p); + + void QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafeboxLoadPacket *, bool bMall); + void QUERY_SAFEBOX_SAVE(CPeer * pkPeer, TSafeboxTable * pTable); + void QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p); + void QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p); + + void RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg); + + void QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p); + void QUERY_SETUP(CPeer * pkPeer, DWORD dwHandle, const char * c_pData); + + void SendPartyOnSetup(CPeer * peer); + + void QUERY_FLUSH_CACHE(CPeer * pkPeer, const char * c_pData); + + void QUERY_PARTY_CREATE(CPeer * peer, TPacketPartyCreate* p); + void QUERY_PARTY_DELETE(CPeer * peer, TPacketPartyDelete* p); + void QUERY_PARTY_ADD(CPeer * peer, TPacketPartyAdd* p); + void QUERY_PARTY_REMOVE(CPeer * peer, TPacketPartyRemove* p); + void QUERY_PARTY_STATE_CHANGE(CPeer * peer, TPacketPartyStateChange* p); + void QUERY_PARTY_SET_MEMBER_LEVEL(CPeer * peer, TPacketPartySetMemberLevel* p); + + void QUERY_RELOAD_PROTO(); + + void QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p); + void GetPlayerFromRes(TPlayerTable * player_table, MYSQL_RES* res); + + void QUERY_LOGIN_KEY(CPeer * pkPeer, TPacketGDLoginKey * p); + + void AddGuildPriv(TPacketGiveGuildPriv* p); + void AddEmpirePriv(TPacketGiveEmpirePriv* p); + void AddCharacterPriv(TPacketGiveCharacterPriv* p); + + void MoneyLog(TPacketMoneyLog* p); + + void QUERY_AUTH_LOGIN(CPeer * pkPeer, DWORD dwHandle, TPacketGDAuthLogin * p); + + void QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p); + void RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg); + + void ChargeCash(const TRequestChargeCash * p); + + void LoadEventFlag(); + void SetEventFlag(TPacketSetEventFlag* p); + void SendEventFlagsOnSetup(CPeer* peer); + + void BillingExpire(TPacketBillingExpire * p); + void BillingCheck(const char * data); + + void SendAllLoginToBilling(); + void SendLoginToBilling(CLoginData * pkLD, bool bLogin); + + void MarriageAdd(TPacketMarriageAdd * p); + void MarriageUpdate(TPacketMarriageUpdate * p); + void MarriageRemove(TPacketMarriageRemove * p); + + void WeddingRequest(TPacketWeddingRequest * p); + void WeddingReady(TPacketWeddingReady * p); + void WeddingEnd(TPacketWeddingEnd * p); + + // MYSHOP_PRICE_LIST + + void MyshopPricelistUpdate(const TItemPriceListTable* pPacket); // @fixme403 (TPacketMyshopPricelistHeader to TItemPriceListTable) + + void MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID); + // END_OF_MYSHOP_PRICE_LIST + + // Building + void CreateObject(TPacketGDCreateObject * p); + void DeleteObject(DWORD dwID); + void UpdateLand(DWORD * pdw); + + // BLOCK_CHAT + void BlockChat(TPacketBlockChat * p); + // END_OF_BLOCK_CHAT + + private: + int m_looping; + socket_t m_fdAccept; + TPeerList m_peerList; + + CPeer * m_pkAuthPeer; + + // LoginKey, LoginData pair + typedef boost::unordered_map TLoginDataByLoginKey; + TLoginDataByLoginKey m_map_pkLoginData; + + // Login LoginData pair + typedef boost::unordered_map TLoginDataByLogin; + TLoginDataByLogin m_map_pkLoginDataByLogin; + + // AccountID LoginData pair + typedef boost::unordered_map TLoginDataByAID; + TLoginDataByAID m_map_pkLoginDataByAID; + + typedef boost::unordered_map TLogonAccountMap; + TLogonAccountMap m_map_kLogonAccount; + + int m_iPlayerIDStart; + int m_iPlayerDeleteLevelLimit; + int m_iPlayerDeleteLevelLimitLower; + bool m_bChinaEventServer; + + std::vector m_vec_mobTable; + std::vector m_vec_itemTable; + std::map m_map_itemTableByVnum; + + int m_iShopTableSize; + TShopTable * m_pShopTable; + + int m_iRefineTableSize; + TRefineTable* m_pRefineTable; + + std::vector m_vec_skillTable; + std::vector m_vec_banwordTable; + std::vector m_vec_itemAttrTable; + std::vector m_vec_itemRareTable; + + std::vector m_vec_kLandTable; + std::vector m_vec_kObjectProto; + std::map m_map_pkObjectTable; + + bool m_bShutdowned; + + TPlayerTableCacheMap m_map_playerCache; + + TItemCacheMap m_map_itemCache; + TItemCacheSetPtrMap m_map_pkItemCacheSetPtr; + + // MYSHOP_PRICE_LIST + + TItemPriceListCacheMap m_mapItemPriceListCache; + // END_OF_MYSHOP_PRICE_LIST + + TChannelStatusMap m_mChannelStatus; + + struct TPartyInfo + { + BYTE bRole; + BYTE bLevel; + + TPartyInfo() :bRole(0), bLevel(0) + { + } + }; + + typedef std::map TPartyMember; + typedef std::map TPartyMap; + typedef std::map TPartyChannelMap; + TPartyChannelMap m_map_pkChannelParty; + + typedef std::map TEventFlagMap; + TEventFlagMap m_map_lEventFlag; + + BYTE m_bLastHeader; + int m_iCacheFlushCount; + int m_iCacheFlushCountLimit; + + private : + TItemIDRangeTable m_itemRange; + + public : + bool InitializeNowItemID(); + DWORD GetItemID(); + DWORD GainItemID(); + TItemIDRangeTable GetItemRange() { return m_itemRange; } + + //BOOT_LOCALIZATION + public: + + bool InitializeLocalization(); + + private: + std::vector m_vec_Locale; + //END_BOOT_LOCALIZATION + //ADMIN_MANAGER + + bool __GetAdminInfo(const char *szIP, std::vector & rAdminVec); + bool __GetHostInfo(std::vector & rIPVec); + //END_ADMIN_MANAGER + + //RELOAD_ADMIN + void ReloadAdmin(CPeer * peer, TPacketReloadAdmin * p); + //END_RELOAD_ADMIN + void BreakMarriage(CPeer * peer, const char * data); + + struct TLogoutPlayer + { + DWORD pid; + time_t time; + + bool operator < (const TLogoutPlayer & r) + { + return (pid < r.pid); + } + }; + + typedef boost::unordered_map TLogoutPlayerMap; + TLogoutPlayerMap m_map_logout; + + void InsertLogoutPlayer(DWORD pid); + void DeleteLogoutPlayer(DWORD pid); + void UpdateLogoutPlayer(); + void UpdateItemCacheSet(DWORD pid); + + void FlushPlayerCacheSet(DWORD pid); + + //MONARCH + void Election(CPeer * peer, DWORD dwHandle, const char * p); + void Candidacy(CPeer * peer, DWORD dwHandle, const char * p); + void AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + void TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + void ComeToVote(CPeer * peer, DWORD dwHandle, const char * p); + void RMCandidacy(CPeer * peer, DWORD dwHandle, const char * p); + void SetMonarch(CPeer * peer, DWORD dwHandle, const char * p); + void RMMonarch(CPeer * peer, DWORD dwHandle, const char * p); + + void DecMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + //END_MONARCH + + void ChangeMonarchLord(CPeer* peer, DWORD dwHandle, TPacketChangeMonarchLord* info); + + void SendSpareItemIDRange(CPeer* peer); + + void UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer); + void AckHorseName(DWORD dwPID, CPeer* peer); + void DeleteLoginKey(TPacketDC *data); + void ResetLastPlayerID(const TPacketNeedLoginLogInfo* data); + //delete gift notify icon + void DeleteAwardId(TPacketDeleteAwardID* data); + void UpdateChannelStatus(TChannelStatus* pData); + void RequestChannelStatus(CPeer* peer, DWORD dwHandle); +#ifdef ENABLE_PROTO_FROM_DB + public: + bool InitializeMobTableFromDB(); + bool InitializeItemTableFromDB(); + protected: + bool bIsProtoReadFromDB; +#endif +}; + +template +Func CClientManager::for_each_peer(Func f) +{ + TPeerList::iterator it; + for (it = m_peerList.begin(); it!=m_peerList.end();++it) + { + f(*it); + } + return f; +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerBoot.cpp b/source-server/Srcs/Server/db/src/ClientManagerBoot.cpp new file mode 100644 index 000000000..55ae43e65 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerBoot.cpp @@ -0,0 +1,1606 @@ +#include +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Monarch.h" +#include "CsvReader.h" +#include "ProtoReader.h" + +using namespace std; + +extern int g_test_server; +extern std::string g_stLocaleNameColumn; + +bool CClientManager::InitializeTables() +{ +#ifdef ENABLE_PROTO_FROM_DB + if (!(bIsProtoReadFromDB?InitializeMobTableFromDB():InitializeMobTable())) +#else + if (!InitializeMobTable()) +#endif + { + sys_err("InitializeMobTable FAILED"); + return false; + } +#ifdef ENABLE_PROTO_FROM_DB + if (!(bIsProtoReadFromDB?InitializeItemTableFromDB():InitializeItemTable())) +#else + if (!InitializeItemTable()) +#endif + { + sys_err("InitializeItemTable FAILED"); + return false; + } + +#ifdef ENABLE_PROTO_FROM_DB + extern bool g_bMirror2DB; + if (g_bMirror2DB) + { + if (!MirrorMobTableIntoDB()) + { + sys_err("MirrorMobTableIntoDB FAILED"); + return false; + } + if (!MirrorItemTableIntoDB()) + { + sys_err("MirrorItemTableIntoDB FAILED"); + return false; + } + } +#endif + + if (!InitializeShopTable()) + { + sys_err("InitializeShopTable FAILED"); + return false; + } + + if (!InitializeSkillTable()) + { + sys_err("InitializeSkillTable FAILED"); + return false; + } + + if (!InitializeRefineTable()) + { + sys_err("InitializeRefineTable FAILED"); + return false; + } + + if (!InitializeItemAttrTable()) + { + sys_err("InitializeItemAttrTable FAILED"); + return false; + } + + if (!InitializeItemRareTable()) + { + sys_err("InitializeItemRareTable FAILED"); + return false; + } + + if (!InitializeBanwordTable()) + { + sys_err("InitializeBanwordTable FAILED"); + return false; + } + + if (!InitializeLandTable()) + { + sys_err("InitializeLandTable FAILED"); + return false; + } + + if (!InitializeObjectProto()) + { + sys_err("InitializeObjectProto FAILED"); + return false; + } + + if (!InitializeObjectTable()) + { + sys_err("InitializeObjectTable FAILED"); + return false; + } + + if (!InitializeMonarch()) + { + sys_err("InitializeMonarch FAILED"); + return false; + } + + return true; +} + +bool CClientManager::InitializeRefineTable() +{ + char query[2048]; + + snprintf(query, sizeof(query), + "SELECT id, cost, prob, vnum0, count0, vnum1, count1, vnum2, count2, vnum3, count3, vnum4, count4 FROM refine_proto%s", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + return true; + + if (m_pRefineTable) + { + sys_log(0, "RELOAD: refine_proto"); + delete [] m_pRefineTable; + m_pRefineTable = NULL; + } + + m_iRefineTableSize = pRes->uiNumRows; + + m_pRefineTable = new TRefineTable[m_iRefineTableSize]; + memset(m_pRefineTable, 0, sizeof(TRefineTable) * m_iRefineTableSize); + + TRefineTable* prt = m_pRefineTable; + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + //const char* s_szQuery = "SELECT src_vnum, result_vnum, cost, prob, " + //"vnum0, count0, vnum1, count1, vnum2, count2, vnum3, count3, vnum4, count4 " + + int col = 0; + //prt->src_vnum = atoi(data[col++]); + //prt->result_vnum = atoi(data[col++]); + str_to_number(prt->id, data[col++]); + str_to_number(prt->cost, data[col++]); + str_to_number(prt->prob, data[col++]); + + prt->material_count = REFINE_MATERIAL_MAX_NUM; // @fixme185 + for (int i = 0; i < REFINE_MATERIAL_MAX_NUM; i++) + { + str_to_number(prt->materials[i].vnum, data[col++]); + str_to_number(prt->materials[i].count, data[col++]); + if (prt->materials[i].vnum == 0) + { + prt->material_count = i; + break; + } + } + + sys_log(0, "REFINE: id %ld cost %d prob %d mat1 %lu cnt1 %d", prt->id, prt->cost, prt->prob, prt->materials[0].vnum, prt->materials[0].count); + + prt++; + } + return true; +} + +class FCompareVnum +{ + public: + bool operator () (const TEntityTable & a, const TEntityTable & b) const + { + return (a.dwVnum < b.dwVnum); + } +}; + +bool CClientManager::InitializeMobTable() +{ + //_______________________________________________// + + //===============================================// + + map localMap; + //bool isNameFile = true; + + cCsvTable nameData; + if(!nameData.Load("mob_names.txt",'\t')) + { + fprintf(stderr, "Could not load mob_names.txt\n"); + } else { + nameData.Next(); + while(nameData.Next()) { + if (nameData.ColCount() >= 2) // skip noname + localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); + } + } + //________________________________________________// + + cCsvTable data; + + if(!data.Load("mob_proto.txt",'\t')) + { + fprintf(stderr, "Could not load mob_proto.txt. Wrong file format?\n"); + return false; + } + data.Next(); + + if (!m_vec_mobTable.empty()) + { + sys_log(0, "RELOAD: mob_proto"); + m_vec_mobTable.clear(); + } + m_vec_mobTable.resize(data.m_File.GetRowCount()-1); + memset(&m_vec_mobTable[0], 0, sizeof(TMobTable) * m_vec_mobTable.size()); + TMobTable * mob_table = &m_vec_mobTable[0]; + + while (data.Next()) + { + if (!Set_Proto_Mob_Table(mob_table, data, localMap)) + { + fprintf(stderr, "Could not process entry.\n"); + } + + sys_log(1, "MOB #%-5d %-24s %-24s level: %-3u rank: %u empire: %d", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire); + ++mob_table; + + } + //_____________________________________________________// + + sort(m_vec_mobTable.begin(), m_vec_mobTable.end(), FCompareVnum()); + return true; +} + +bool CClientManager::InitializeShopTable() +{ + MYSQL_ROW data; + int col; + + static const char * s_szQuery = + "SELECT " + "shop.vnum, " + "shop.npc_vnum, " + "shop_item.item_vnum, " + "shop_item.count " + "FROM shop LEFT JOIN shop_item " + "ON shop.vnum = shop_item.shop_vnum ORDER BY shop.vnum, shop_item.item_vnum"; + + auto pkMsg2(CDBManager::instance().DirectQuery(s_szQuery)); + + SQLResult * pRes2 = pkMsg2->Get(); + + if (!pRes2->uiNumRows) + { + sys_err("InitializeShopTable : Table count is zero."); + return false; + } + + std::map map_shop; + + if (m_pShopTable) + { + delete [] (m_pShopTable); + m_pShopTable = NULL; + } + + TShopTable * shop_table = m_pShopTable; + + while ((data = mysql_fetch_row(pRes2->pSQLResult))) + { + col = 0; + + int iShopVnum = 0; + str_to_number(iShopVnum, data[col++]); + + if (map_shop.end() == map_shop.find(iShopVnum)) + { + shop_table = new TShopTable{}; + shop_table->dwVnum = iShopVnum; + + map_shop[iShopVnum] = shop_table; + } + else + shop_table = map_shop[iShopVnum]; + + str_to_number(shop_table->dwNPCVnum, data[col++]); + + if (!data[col]) + continue; + + TShopItemTable * pItem = &shop_table->items[shop_table->byItemCount]; + + str_to_number(pItem->vnum, data[col++]); + str_to_number(pItem->count, data[col++]); + + ++shop_table->byItemCount; + } + + m_pShopTable = new TShopTable[map_shop.size()]; + m_iShopTableSize = map_shop.size(); + + typeof(map_shop.begin()) it = map_shop.begin(); + + int i = 0; + + while (it != map_shop.end()) + { + thecore_memcpy((m_pShopTable + i), (it++)->second, sizeof(TShopTable)); + sys_log(0, "SHOP: #%d items: %d", (m_pShopTable + i)->dwVnum, (m_pShopTable + i)->byItemCount); + ++i; + } + + return true; +} + +bool CClientManager::InitializeQuestItemTable() +{ + using namespace std; + + static const char * s_szQuery = "SELECT vnum, name, %s FROM quest_item_proto ORDER BY vnum"; + + char query[1024]; + snprintf(query, sizeof(query), s_szQuery, g_stLocaleNameColumn.c_str()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("query error or no rows: %s", query); + return false; + } + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + int col = 0; + + TItemTable tbl; + memset(&tbl, 0, sizeof(tbl)); + + str_to_number(tbl.dwVnum, row[col++]); + + if (row[col]) + strlcpy(tbl.szName, row[col], sizeof(tbl.szName)); + + col++; + + if (row[col]) + strlcpy(tbl.szLocaleName, row[col], sizeof(tbl.szLocaleName)); + + col++; + + if (m_map_itemTableByVnum.find(tbl.dwVnum) != m_map_itemTableByVnum.end()) + { + sys_err("QUEST_ITEM_ERROR! %lu vnum already exist! (name %s)", tbl.dwVnum, tbl.szLocaleName); + continue; + } + + tbl.bType = ITEM_QUEST; + tbl.bSize = 1; + + m_vec_itemTable.emplace_back(tbl); + } + + return true; +} + +bool CClientManager::InitializeItemTable() +{ + //_______________________________________________// + + //=================================================================================// + //=================================================================================// + map localMap; + cCsvTable nameData; + if(!nameData.Load("item_names.txt",'\t')) + { + fprintf(stderr, "Could not load item_names.txt.\n"); + } else { + nameData.Next(); + while(nameData.Next()) { + if (nameData.ColCount() >= 2) // skip noname + localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); + } + } + //_________________________________________________________________// + + cCsvTable data; + if(!data.Load("item_proto.txt",'\t')) + { + fprintf(stderr, "Could not load item_proto.txt. Wrong file format?\n"); + return false; + } + data.Next(); + + if (!m_vec_itemTable.empty()) + { + sys_log(0, "RELOAD: item_proto"); + m_vec_itemTable.clear(); + m_map_itemTableByVnum.clear(); + } + + data.Destroy(); + if(!data.Load("item_proto.txt",'\t')) + { + fprintf(stderr, "Could not load item_proto.txt. Wrong file format?\n"); + return false; + } + data.Next(); + + m_vec_itemTable.resize(data.m_File.GetRowCount() - 1); + memset(&m_vec_itemTable[0], 0, sizeof(TItemTable) * m_vec_itemTable.size()); + + TItemTable * item_table = &m_vec_itemTable[0]; + + while (data.Next()) + { + if (!Set_Proto_Item_Table(item_table, data, localMap)) + { + fprintf(stderr, "Failed to load item_proto table.\n"); + } + + m_map_itemTableByVnum.emplace(item_table->dwVnum, item_table); + ++item_table; + } + //_______________________________________________________________________// + + // QUEST_ITEM_PROTO_DISABLE + // InitializeQuestItemTable(); + // END_OF_QUEST_ITEM_PROTO_DISABLE + + m_map_itemTableByVnum.clear(); + + itertype(m_vec_itemTable) it = m_vec_itemTable.begin(); + + while (it != m_vec_itemTable.end()) + { + TItemTable * item_table = &(*(it++)); + + sys_log(1, "ITEM: #%-5lu %-24s %-24s VAL: %ld %ld %ld %ld %ld %ld WEAR %lu ANTI %lu IMMUNE %lu REFINE %lu REFINE_SET %u MAGIC_PCT %u", + item_table->dwVnum, + item_table->szName, + item_table->szLocaleName, + item_table->alValues[0], + item_table->alValues[1], + item_table->alValues[2], + item_table->alValues[3], + item_table->alValues[4], + item_table->alValues[5], + item_table->dwWearFlags, + item_table->dwAntiFlags, + item_table->dwImmuneFlag, + item_table->dwRefinedVnum, + item_table->wRefineSet, + item_table->bAlterToMagicItemPct); + + m_map_itemTableByVnum.emplace(item_table->dwVnum, item_table); + } + sort(m_vec_itemTable.begin(), m_vec_itemTable.end(), FCompareVnum()); + return true; +} + +bool CClientManager::InitializeSkillTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT dwVnum, szName, bType, bMaxLevel, dwSplashRange, " + "szPointOn, szPointPoly, szSPCostPoly, szDurationPoly, szDurationSPCostPoly, " + "szCooldownPoly, szMasterBonusPoly, setFlag+0, setAffectFlag+0, " + "szPointOn2, szPointPoly2, szDurationPoly2, setAffectFlag2+0, " + "szPointOn3, szPointPoly3, szDurationPoly3, szGrandMasterAddSPCostPoly, " + "bLevelStep, bLevelLimit, prerequisiteSkillVnum, prerequisiteSkillLevel, iMaxHit, szSplashAroundDamageAdjustPoly, eSkillType+0, dwTargetRange " + "FROM skill_proto%s ORDER BY dwVnum", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from skill_proto"); + return false; + } + + if (!m_vec_skillTable.empty()) + { + sys_log(0, "RELOAD: skill_proto"); + m_vec_skillTable.clear(); + } + + m_vec_skillTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + int col; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TSkillTable t; + memset(&t, 0, sizeof(t)); + + col = 0; + + str_to_number(t.dwVnum, data[col++]); + strlcpy(t.szName, data[col++], sizeof(t.szName)); + str_to_number(t.bType, data[col++]); + str_to_number(t.bMaxLevel, data[col++]); + str_to_number(t.dwSplashRange, data[col++]); + + strlcpy(t.szPointOn, data[col++], sizeof(t.szPointOn)); + strlcpy(t.szPointPoly, data[col++], sizeof(t.szPointPoly)); + strlcpy(t.szSPCostPoly, data[col++], sizeof(t.szSPCostPoly)); + strlcpy(t.szDurationPoly, data[col++], sizeof(t.szDurationPoly)); + strlcpy(t.szDurationSPCostPoly, data[col++], sizeof(t.szDurationSPCostPoly)); + strlcpy(t.szCooldownPoly, data[col++], sizeof(t.szCooldownPoly)); + strlcpy(t.szMasterBonusPoly, data[col++], sizeof(t.szMasterBonusPoly)); + + str_to_number(t.dwFlag, data[col++]); + str_to_number(t.dwAffectFlag, data[col++]); + + strlcpy(t.szPointOn2, data[col++], sizeof(t.szPointOn2)); + strlcpy(t.szPointPoly2, data[col++], sizeof(t.szPointPoly2)); + strlcpy(t.szDurationPoly2, data[col++], sizeof(t.szDurationPoly2)); + str_to_number(t.dwAffectFlag2, data[col++]); + + // ADD_GRANDMASTER_SKILL + strlcpy(t.szPointOn3, data[col++], sizeof(t.szPointOn3)); + strlcpy(t.szPointPoly3, data[col++], sizeof(t.szPointPoly3)); + strlcpy(t.szDurationPoly3, data[col++], sizeof(t.szDurationPoly3)); + + strlcpy(t.szGrandMasterAddSPCostPoly, data[col++], sizeof(t.szGrandMasterAddSPCostPoly)); + // END_OF_ADD_GRANDMASTER_SKILL + + str_to_number(t.bLevelStep, data[col++]); + str_to_number(t.bLevelLimit, data[col++]); + str_to_number(t.preSkillVnum, data[col++]); + str_to_number(t.preSkillLevel, data[col++]); + + str_to_number(t.lMaxHit, data[col++]); + + strlcpy(t.szSplashAroundDamageAdjustPoly, data[col++], sizeof(t.szSplashAroundDamageAdjustPoly)); + + str_to_number(t.bSkillAttrType, data[col++]); + str_to_number(t.dwTargetRange, data[col++]); + + sys_log(0, "SKILL: #%d %s flag %u point %s affect %u cooldown %s", t.dwVnum, t.szName, t.dwFlag, t.szPointOn, t.dwAffectFlag, t.szCooldownPoly); + + m_vec_skillTable.emplace_back(t); + } + + return true; +} + +bool CClientManager::InitializeBanwordTable() +{ + m_vec_banwordTable.clear(); + + auto pkMsg(CDBManager::instance().DirectQuery("SELECT word FROM banword")); + + SQLResult * pRes = pkMsg->Get(); + + if (pRes->uiNumRows == 0) + return true; + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TBanwordTable t; + + if (data[0]) + { + strlcpy(t.szWord, data[0], sizeof(t.szWord)); + m_vec_banwordTable.emplace_back(t); + } + } + + sys_log(0, "BANWORD: total %d", m_vec_banwordTable.size()); + return true; +} + +bool CClientManager::InitializeItemAttrTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT apply, apply+0, prob, lv1, lv2, lv3, lv4, lv5, weapon, body, wrist, foots, neck, head, shield, ear " +#ifdef ENABLE_ITEM_ATTR_COSTUME + ", costume_body, costume_hair" +#if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + ", costume_weapon" +#endif +#endif +#ifdef ENABLE_PENDANT_SYSTEM + ", pendant" +#endif +#ifdef ENABLE_GLOVE_SYSTEM + ", glove" +#endif + " FROM item_attr%s ORDER BY apply", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from item_attr"); + return false; + } + + if (!m_vec_itemAttrTable.empty()) + { + sys_log(0, "RELOAD: item_attr"); + m_vec_itemAttrTable.clear(); + } + + m_vec_itemAttrTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TItemAttrTable t{}; + + int col = 0; + + strlcpy(t.szApply, data[col++], sizeof(t.szApply)); + str_to_number(t.dwApplyIndex, data[col++]); + str_to_number(t.dwProb, data[col++]); + str_to_number(t.lValues[0], data[col++]); + str_to_number(t.lValues[1], data[col++]); + str_to_number(t.lValues[2], data[col++]); + str_to_number(t.lValues[3], data[col++]); + str_to_number(t.lValues[4], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_EAR], data[col++]); +#ifdef ENABLE_ITEM_ATTR_COSTUME + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_HAIR], data[col++]); +#if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_WEAPON], data[col++]); +#endif +#endif +#ifdef ENABLE_PENDANT_SYSTEM + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_PENDANT], data[col++]); +#endif +#ifdef ENABLE_GLOVE_SYSTEM + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_GLOVE], data[col++]); +#endif + + sys_log(0, "ITEM_ATTR: %-20s %4lu { %3d %3d %3d %3d %3d } { %d %d %d %d %d %d %d" + #ifdef ENABLE_ITEM_ATTR_COSTUME + " %d %d" + #if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + " %d" + #endif + #endif + #ifdef ENABLE_PENDANT_SYSTEM + " %d" + #endif + #ifdef ENABLE_GLOVE_SYSTEM + " %d" + #endif + " }", + t.szApply, + t.dwProb, + t.lValues[0], + t.lValues[1], + t.lValues[2], + t.lValues[3], + t.lValues[4], + t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], + t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], + t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], + t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], + t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], + t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], + t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], + t.bMaxLevelBySet[ATTRIBUTE_SET_EAR] + #ifdef ENABLE_ITEM_ATTR_COSTUME + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_BODY] + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_HAIR] + #if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_WEAPON] + #endif + #endif + #ifdef ENABLE_PENDANT_SYSTEM + , t.bMaxLevelBySet[ATTRIBUTE_SET_PENDANT] + #endif + #ifdef ENABLE_GLOVE_SYSTEM + , t.bMaxLevelBySet[ATTRIBUTE_SET_GLOVE] + #endif + ); + + m_vec_itemAttrTable.emplace_back(t); + } + + return true; +} + +bool CClientManager::InitializeItemRareTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT apply, apply+0, prob, lv1, lv2, lv3, lv4, lv5, weapon, body, wrist, foots, neck, head, shield, ear " +#ifdef ENABLE_ITEM_ATTR_COSTUME + ", costume_body, costume_hair" +#if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + ", costume_weapon" +#endif +#endif +#ifdef ENABLE_PENDANT_SYSTEM + ", pendant" +#endif +#ifdef ENABLE_GLOVE_SYSTEM + ", glove" +#endif + " FROM item_attr_rare%s ORDER BY apply", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from item_attr_rare"); + return false; + } + + if (!m_vec_itemRareTable.empty()) + { + sys_log(0, "RELOAD: item_attr_rare"); + m_vec_itemRareTable.clear(); + } + + m_vec_itemRareTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TItemAttrTable t{}; + + int col = 0; + + strlcpy(t.szApply, data[col++], sizeof(t.szApply)); + str_to_number(t.dwApplyIndex, data[col++]); + str_to_number(t.dwProb, data[col++]); + str_to_number(t.lValues[0], data[col++]); + str_to_number(t.lValues[1], data[col++]); + str_to_number(t.lValues[2], data[col++]); + str_to_number(t.lValues[3], data[col++]); + str_to_number(t.lValues[4], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_EAR], data[col++]); + #ifdef ENABLE_ITEM_ATTR_COSTUME + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_HAIR], data[col++]); + #if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_WEAPON], data[col++]); + #endif + #endif + #ifdef ENABLE_PENDANT_SYSTEM + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_PENDANT], data[col++]); + #endif + #ifdef ENABLE_GLOVE_SYSTEM + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_GLOVE], data[col++]); + #endif + + sys_log(0, "ITEM_RARE: %-20s %4lu { %3d %3d %3d %3d %3d } { %d %d %d %d %d %d %d" + #ifdef ENABLE_ITEM_ATTR_COSTUME + " %d %d" + #if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + " %d" + #endif + #endif + #ifdef ENABLE_PENDANT_SYSTEM + " %d" + #endif + #ifdef ENABLE_GLOVE_SYSTEM + " %d" + #endif + " }", + t.szApply, + t.dwProb, + t.lValues[0], + t.lValues[1], + t.lValues[2], + t.lValues[3], + t.lValues[4], + t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], + t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], + t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], + t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], + t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], + t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], + t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], + t.bMaxLevelBySet[ATTRIBUTE_SET_EAR] + #ifdef ENABLE_ITEM_ATTR_COSTUME + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_BODY] + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_HAIR] + #if defined(ENABLE_ITEM_ATTR_COSTUME) && defined(ENABLE_WEAPON_COSTUME_SYSTEM) + , t.bMaxLevelBySet[ATTRIBUTE_SET_COSTUME_WEAPON] + #endif + #endif + #ifdef ENABLE_PENDANT_SYSTEM + , t.bMaxLevelBySet[ATTRIBUTE_SET_PENDANT] + #endif + #ifdef ENABLE_GLOVE_SYSTEM + , t.bMaxLevelBySet[ATTRIBUTE_SET_GLOVE] + #endif + ); + + m_vec_itemRareTable.emplace_back(t); + } + + return true; +} + +bool CClientManager::InitializeLandTable() +{ + using namespace building; + + char query[4096]; + + snprintf(query, sizeof(query), + "SELECT id, map_index, x, y, width, height, guild_id, guild_level_limit, price " + "FROM land%s WHERE enable='YES' ORDER BY id", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_vec_kLandTable.empty()) + { + sys_log(0, "RELOAD: land"); + m_vec_kLandTable.clear(); + } + + m_vec_kLandTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TLand t; + + memset(&t, 0, sizeof(t)); + + int col = 0; + + str_to_number(t.dwID, data[col++]); + str_to_number(t.lMapIndex, data[col++]); + str_to_number(t.x, data[col++]); + str_to_number(t.y, data[col++]); + str_to_number(t.width, data[col++]); + str_to_number(t.height, data[col++]); + str_to_number(t.dwGuildID, data[col++]); + str_to_number(t.bGuildLevelLimit, data[col++]); + str_to_number(t.dwPrice, data[col++]); + + sys_log(0, "LAND: %lu map %-4ld %7ldx%-7ld w %-4ld h %-4ld", t.dwID, t.lMapIndex, t.x, t.y, t.width, t.height); + + m_vec_kLandTable.emplace_back(t); + } + + return true; +} + +void parse_pair_number_string(const char * c_pszString, std::vector > & vec) +{ + // format: 10,1/20,3/300,50 + const char * t = c_pszString; + const char * p = strchr(t, '/'); + std::pair k; + + char szNum[32 + 1]; + char * comma; + + while (p) + { + if (isnhdigit(*t)) + { + strlcpy(szNum, t, MIN(sizeof(szNum), (p-t)+1)); + + comma = strchr(szNum, ','); + + if (comma) + { + *comma = '\0'; + str_to_number(k.second, comma+1); + } + else + k.second = 0; + + str_to_number(k.first, szNum); + vec.emplace_back(k); + } + + t = p + 1; + p = strchr(t, '/'); + } + + if (isnhdigit(*t)) + { + strlcpy(szNum, t, sizeof(szNum)); + + comma = strchr(const_cast(t), ','); + + if (comma) + { + *comma = '\0'; + str_to_number(k.second, comma+1); + } + else + k.second = 0; + + str_to_number(k.first, szNum); + vec.emplace_back(k); + } +} + +bool CClientManager::InitializeObjectProto() +{ + using namespace building; + + char query[4096]; + snprintf(query, sizeof(query), + "SELECT vnum, price, materials, upgrade_vnum, upgrade_limit_time, life, reg_1, reg_2, reg_3, reg_4, npc, group_vnum, dependent_group " + "FROM object_proto%s ORDER BY vnum", + GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_vec_kObjectProto.empty()) + { + sys_log(0, "RELOAD: object_proto"); + m_vec_kObjectProto.clear(); + } + + m_vec_kObjectProto.reserve(MAX(0, pRes->uiNumRows)); + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TObjectProto t; + + memset(&t, 0, sizeof(t)); + + int col = 0; + + str_to_number(t.dwVnum, data[col++]); + str_to_number(t.dwPrice, data[col++]); + + std::vector > vec; + parse_pair_number_string(data[col++], vec); + + for (unsigned int i = 0; i < OBJECT_MATERIAL_MAX_NUM && i < vec.size(); ++i) + { + std::pair & r = vec[i]; + + t.kMaterials[i].dwItemVnum = r.first; + t.kMaterials[i].dwCount = r.second; + } + + str_to_number(t.dwUpgradeVnum, data[col++]); + str_to_number(t.dwUpgradeLimitTime, data[col++]); + str_to_number(t.lLife, data[col++]); + str_to_number(t.lRegion[0], data[col++]); + str_to_number(t.lRegion[1], data[col++]); + str_to_number(t.lRegion[2], data[col++]); + str_to_number(t.lRegion[3], data[col++]); + + // ADD_BUILDING_NPC + str_to_number(t.dwNPCVnum, data[col++]); + str_to_number(t.dwGroupVnum, data[col++]); + str_to_number(t.dwDependOnGroupVnum, data[col++]); + + t.lNPCX = 0; + t.lNPCY = MAX(t.lRegion[1], t.lRegion[3])+300; + // END_OF_ADD_BUILDING_NPC + + sys_log(0, "OBJ_PROTO: vnum %lu price %lu mat %lu %lu", + t.dwVnum, t.dwPrice, t.kMaterials[0].dwItemVnum, t.kMaterials[0].dwCount); + + m_vec_kObjectProto.emplace_back(t); + } + + return true; +} + +bool CClientManager::InitializeObjectTable() +{ + using namespace building; + + char query[4096]; + snprintf(query, sizeof(query), "SELECT id, land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot, life FROM object%s ORDER BY id", GetTablePostfix()); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_map_pkObjectTable.empty()) + { + sys_log(0, "RELOAD: object"); + m_map_pkObjectTable.clear(); + } + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TObject * k = new TObject; + + memset(k, 0, sizeof(TObject)); + + int col = 0; + + str_to_number(k->dwID, data[col++]); + str_to_number(k->dwLandID, data[col++]); + str_to_number(k->dwVnum, data[col++]); + str_to_number(k->lMapIndex, data[col++]); + str_to_number(k->x, data[col++]); + str_to_number(k->y, data[col++]); + str_to_number(k->xRot, data[col++]); + str_to_number(k->yRot, data[col++]); + str_to_number(k->zRot, data[col++]); + str_to_number(k->lLife, data[col++]); + + sys_log(0, "OBJ: %lu vnum %lu map %-4ld %7ldx%-7ld life %ld", + k->dwID, k->dwVnum, k->lMapIndex, k->x, k->y, k->lLife); + + m_map_pkObjectTable.emplace(k->dwID, k); + } + + return true; +} + +bool CClientManager::InitializeMonarch() +{ + CMonarch::instance().LoadMonarch(); + + return true; +} + +bool CClientManager::MirrorMobTableIntoDB() +{ + for (itertype(m_vec_mobTable) it = m_vec_mobTable.begin(); it != m_vec_mobTable.end(); it++) + { + const TMobTable& t = *it; + char query[4096]; + if (g_stLocaleNameColumn == "name") + { + snprintf(query, sizeof(query), + "replace into mob_proto%s " + "(" + "vnum, name, type, `rank`, battle_type, level, size, ai_flag, setRaceFlag, setImmuneFlag, " + "on_click, empire, drop_item, resurrection_vnum, folder, " + "st, dx, ht, iq, damage_min, damage_max, max_hp, regen_cycle, regen_percent, exp, " + "gold_min, gold_max, def, attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item, " + + "enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate, " + "resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow, " + "resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, " + "dam_multiply, summon, drain_sp, " + + "skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, " + "skill_vnum3, skill_level3, skill_vnum4, skill_level4, " + "sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive" + ") " + "values (" + + "%u, \"%s\", %u, %u, %u, %u, %u, %u, %u, %u, " + "%u, %u, %u, %u, '%s', " + "%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, " + "%u, %u, %d, %d, %d, %u, %d, %d, %u, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%f, %u, %u, " + + "%u, %u, %u, %u, %u, %u, " + "%u, %u, %u, %u, " + "%u, %u, %u, %u, %u" + ")", + GetTablePostfix(), + + t.dwVnum, t.szName, t.bType, t.bRank, t.bBattleType, t.bLevel, t.bSize, t.dwAIFlag, t.dwRaceFlag, t.dwImmuneFlag, + t.bOnClickType, t.bEmpire, t.dwDropItemVnum, t.dwResurrectionVnum, t.szFolder, + t.bStr, t.bDex, t.bCon, t.bInt, t.dwDamageRange[0], t.dwDamageRange[1], t.dwMaxHP, t.bRegenCycle, t.bRegenPercent, t.dwExp, + + t.dwGoldMin, t.dwGoldMax, t.wDef, t.sAttackSpeed, t.sMovingSpeed, t.bAggresiveHPPct, t.wAggressiveSight, t.wAttackRange, t.dwPolymorphItemVnum, + t.cEnchants[0], t.cEnchants[1], t.cEnchants[2], t.cEnchants[3], t.cEnchants[4], t.cEnchants[5], + t.cResists[0], t.cResists[1], t.cResists[2], t.cResists[3], t.cResists[4], t.cResists[5], + t.cResists[6], t.cResists[7], t.cResists[8], t.cResists[9], t.cResists[10], + t.fDamMultiply, t.dwSummonVnum, t.dwDrainSP, + + t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, + t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, + t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint + ); + } + else + { + snprintf(query, sizeof(query), + "replace into mob_proto%s " + "(" + "vnum, name, %s, type, `rank`, battle_type, level, size, ai_flag, setRaceFlag, setImmuneFlag, " + "on_click, empire, drop_item, resurrection_vnum, folder, " + "st, dx, ht, iq, damage_min, damage_max, max_hp, regen_cycle, regen_percent, exp, " + "gold_min, gold_max, def, attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item, " + + "enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate, " + "resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow, " + "resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, " + "dam_multiply, summon, drain_sp, " + + "skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, " + "skill_vnum3, skill_level3, skill_vnum4, skill_level4, " + "sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive" + ") " + "values (" + + "%u, \"%s\", \"%s\", %u, %u, %u, %u, %u, %u, %u, %u, " + "%u, %u, %u, %u, '%s', " + "%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, " + "%u, %u, %d, %d, %d, %u, %d, %d, %u, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%f, %u, %u, " + + "%u, %u, %u, %u, %u, %u, " + "%u, %u, %u, %u, " + "%u, %u, %u, %u, %u" + ")", + GetTablePostfix(), g_stLocaleNameColumn.c_str(), + + t.dwVnum, t.szName, t.szLocaleName, t.bType, t.bRank, t.bBattleType, t.bLevel, t.bSize, t.dwAIFlag, t.dwRaceFlag, t.dwImmuneFlag, + t.bOnClickType, t.bEmpire, t.dwDropItemVnum, t.dwResurrectionVnum, t.szFolder, + t.bStr, t.bDex, t.bCon, t.bInt, t.dwDamageRange[0], t.dwDamageRange[1], t.dwMaxHP, t.bRegenCycle, t.bRegenPercent, t.dwExp, + + t.dwGoldMin, t.dwGoldMax, t.wDef, t.sAttackSpeed, t.sMovingSpeed, t.bAggresiveHPPct, t.wAggressiveSight, t.wAttackRange, t.dwPolymorphItemVnum, + t.cEnchants[0], t.cEnchants[1], t.cEnchants[2], t.cEnchants[3], t.cEnchants[4], t.cEnchants[5], + t.cResists[0], t.cResists[1], t.cResists[2], t.cResists[3], t.cResists[4], t.cResists[5], + t.cResists[6], t.cResists[7], t.cResists[8], t.cResists[9], t.cResists[10], + t.fDamMultiply, t.dwSummonVnum, t.dwDrainSP, + + t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, + t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, + t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint + ); + } + + CDBManager::instance().AsyncQuery(query); + } + return true; +} + +bool CClientManager::MirrorItemTableIntoDB() +{ + for (itertype(m_vec_itemTable) it = m_vec_itemTable.begin(); it != m_vec_itemTable.end(); it++) + { + if (g_stLocaleNameColumn != "name") + { + const TItemTable& t = *it; + char query[4096]; + snprintf(query, sizeof(query), + "replace into item_proto%s (" //1 + "vnum, type, subtype, name, %s, gold, shop_buy_price, weight, size, " //2 + "flag, wearflag, antiflag, immuneflag, " + "refined_vnum, refine_set, magic_pct, socket_pct, addon_type, " + "limittype0, limitvalue0, limittype1, limitvalue1, " + "applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, " + "value0, value1, value2, value3, value4, value5 ) " + "values (" + "%u, %u, %u, \"%s\", \"%s\", %u, %u, %u, %u, " //11 + "%u, %u, %u, %u, " //15 + "%u, %d, %u, %u, %d, " //20 + "%u, %ld, %u, %ld, " //24 + "%u, %ld, %u, %ld, %u, %ld, " //30 + "%ld, %ld, %ld, %ld, %ld, %ld )", //36 + GetTablePostfix(), g_stLocaleNameColumn.c_str(), //2 + t.dwVnum, t.bType, t.bSubType, t.szName, t.szLocaleName, t.dwGold, t.dwShopBuyPrice, t.bWeight, t.bSize, //11 + t.dwFlags, t.dwWearFlags, t.dwAntiFlags, t.dwImmuneFlag, //15 + t.dwRefinedVnum, t.wRefineSet, t.bAlterToMagicItemPct, t.bGainSocketPct, t.sAddonType, //20 + t.aLimits[0].bType, t.aLimits[0].lValue, t.aLimits[1].bType, t.aLimits[1].lValue, //24 + t.aApplies[0].bType, t.aApplies[0].lValue, t.aApplies[1].bType, t.aApplies[1].lValue, t.aApplies[2].bType, t.aApplies[2].lValue, //30 + t.alValues[0], t.alValues[1], t.alValues[2], t.alValues[3], t.alValues[4], t.alValues[5]); //36 + CDBManager::instance().AsyncQuery(query); + } + else + { + const TItemTable& t = *it; + char query[4096]; + snprintf(query, sizeof(query), + "replace into item_proto%s (" + "vnum, type, subtype, name, gold, shop_buy_price, weight, size, " + "flag, wearflag, antiflag, immuneflag, " + "refined_vnum, refine_set, magic_pct, socket_pct, addon_type, " + "limittype0, limitvalue0, limittype1, limitvalue1, " + "applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, " + "value0, value1, value2, value3, value4, value5 ) " + "values (" + "%d, %d, %d, \"%s\", %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%d, %ld, %d, %ld, " + "%d, %ld, %d, %ld, %d, %ld, " + "%ld, %ld, %ld, %ld, %ld, %ld )", + GetTablePostfix(), + t.dwVnum, t.bType, t.bSubType, t.szName, t.dwGold, t.dwShopBuyPrice, t.bWeight, t.bSize, + t.dwFlags, t.dwWearFlags, t.dwAntiFlags, t.dwImmuneFlag, + t.dwRefinedVnum, t.wRefineSet, t.bAlterToMagicItemPct, t.bGainSocketPct, t.sAddonType, + t.aLimits[0].bType, t.aLimits[0].lValue, t.aLimits[1].bType, t.aLimits[1].lValue, + t.aApplies[0].bType, t.aApplies[0].lValue, t.aApplies[1].bType, t.aApplies[1].lValue, t.aApplies[2].bType, t.aApplies[2].lValue, + t.alValues[0], t.alValues[1], t.alValues[2], t.alValues[3], t.alValues[4], t.alValues[5]); + CDBManager::instance().AsyncQuery(query); + } + } + return true; +} + +#ifdef ENABLE_PROTO_FROM_DB +#define VERIFY_IFIELD(x,y) if (data[x]!=NULL && data[x][0]!='\0') str_to_number(y, data[x]); +#define VERIFY_SFIELD(x,y) if (data[x]!=NULL && data[x][0]!='\0') strlcpy(y, data[x], sizeof(y)); + +#define ENABLE_AUTODETECT_VNUMRANGE + +namespace MProto +{ +enum MProtoT +{ + vnum, name, locale_name, type, rank, battle_type, level, size, + ai_flag, setRaceFlag, setImmuneFlag, on_click, empire, drop_item, + resurrection_vnum, folder, st, dx, ht, iq, damage_min, damage_max, max_hp, + regen_cycle, regen_percent, exp, gold_min, gold_max, def, + attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item, + enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate, +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + enchant_bleeding, +#endif + resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow, +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_CLAW_AS_DAGGER) + resist_claw, +#endif +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + resist_bleeding, +#endif + resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, dam_multiply, summon, drain_sp, + skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, skill_vnum3, skill_level3, + skill_vnum4, skill_level4, sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive +}; +} + +bool CClientManager::InitializeMobTableFromDB() +{ char query[2048]; + fprintf(stdout, "Loading mob_proto from MySQL\n"); + snprintf(query, sizeof(query), + "SELECT vnum, name, %s, type, `rank`, battle_type, level, size+0," + " ai_flag+0, setRaceFlag+0, setImmuneFlag+0, on_click, empire, drop_item," + " resurrection_vnum, folder, st, dx, ht, iq, damage_min, damage_max, max_hp," + " regen_cycle, regen_percent, exp, gold_min, gold_max, def," + " attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item," + " enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate," +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + " enchant_bleeding," +#endif + " resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow," +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_CLAW_AS_DAGGER) + " resist_claw," +#endif +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + " resist_bleeding," +#endif + " resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, dam_multiply, summon, drain_sp," + " skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, skill_vnum3, skill_level3," + " skill_vnum4, skill_level4, sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive" + " FROM mob_proto%s ORDER BY vnum;", + g_stLocaleNameColumn.c_str(), + GetTablePostfix() + ); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + DWORD addNumber = pRes->uiNumRows; + if (addNumber == 0) + return false; + + if (!m_vec_mobTable.empty()) + { + sys_log(0, "RELOAD: mob_proto"); + m_vec_mobTable.clear(); + } + + m_vec_mobTable.resize(addNumber); + memset(&m_vec_mobTable[0], 0, sizeof(TMobTable) * m_vec_mobTable.size()); + TMobTable * mob_table = &m_vec_mobTable[0]; + + MYSQL_ROW data = NULL; + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + // check whether or not the field is NULL or that contains an empty string + // ## GENERAL + VERIFY_IFIELD(MProto::vnum, mob_table->dwVnum); + VERIFY_SFIELD(MProto::name, mob_table->szName); + VERIFY_SFIELD(MProto::locale_name, mob_table->szLocaleName); + VERIFY_IFIELD(MProto::rank, mob_table->bRank); + VERIFY_IFIELD(MProto::type, mob_table->bType); + VERIFY_IFIELD(MProto::battle_type, mob_table->bBattleType); + VERIFY_IFIELD(MProto::level, mob_table->bLevel); + VERIFY_IFIELD(MProto::size, mob_table->bSize); + + // ## FLAG + VERIFY_IFIELD(MProto::ai_flag, mob_table->dwAIFlag); + VERIFY_IFIELD(MProto::setRaceFlag, mob_table->dwRaceFlag); + VERIFY_IFIELD(MProto::setImmuneFlag, mob_table->dwImmuneFlag); + + // ## OTHERS + VERIFY_IFIELD(MProto::empire, mob_table->bEmpire); + VERIFY_SFIELD(MProto::folder, mob_table->szFolder); + VERIFY_IFIELD(MProto::on_click, mob_table->bOnClickType); + VERIFY_IFIELD(MProto::st, mob_table->bStr); + VERIFY_IFIELD(MProto::dx, mob_table->bDex); + VERIFY_IFIELD(MProto::ht, mob_table->bCon); + VERIFY_IFIELD(MProto::iq, mob_table->bInt); + VERIFY_IFIELD(MProto::damage_min, mob_table->dwDamageRange[0]); + VERIFY_IFIELD(MProto::damage_max, mob_table->dwDamageRange[1]); + VERIFY_IFIELD(MProto::max_hp, mob_table->dwMaxHP); + VERIFY_IFIELD(MProto::regen_cycle, mob_table->bRegenCycle); + VERIFY_IFIELD(MProto::regen_percent, mob_table->bRegenPercent); + VERIFY_IFIELD(MProto::gold_min, mob_table->dwGoldMin); + VERIFY_IFIELD(MProto::gold_max, mob_table->dwGoldMax); + VERIFY_IFIELD(MProto::exp, mob_table->dwExp); + VERIFY_IFIELD(MProto::def, mob_table->wDef); + VERIFY_IFIELD(MProto::attack_speed, mob_table->sAttackSpeed); + VERIFY_IFIELD(MProto::move_speed, mob_table->sMovingSpeed); + VERIFY_IFIELD(MProto::aggressive_hp_pct,mob_table->bAggresiveHPPct); + VERIFY_IFIELD(MProto::aggressive_sight, mob_table->wAggressiveSight); + VERIFY_IFIELD(MProto::attack_range, mob_table->wAttackRange); + VERIFY_IFIELD(MProto::drop_item, mob_table->dwDropItemVnum); + VERIFY_IFIELD(MProto::resurrection_vnum,mob_table->dwResurrectionVnum); + + // ## ENCHANT 6 + VERIFY_IFIELD(MProto::enchant_curse, mob_table->cEnchants[MOB_ENCHANT_CURSE]); + VERIFY_IFIELD(MProto::enchant_slow, mob_table->cEnchants[MOB_ENCHANT_SLOW]); + VERIFY_IFIELD(MProto::enchant_poison, mob_table->cEnchants[MOB_ENCHANT_POISON]); + VERIFY_IFIELD(MProto::enchant_stun, mob_table->cEnchants[MOB_ENCHANT_STUN]); + VERIFY_IFIELD(MProto::enchant_critical, mob_table->cEnchants[MOB_ENCHANT_CRITICAL]); + VERIFY_IFIELD(MProto::enchant_penetrate,mob_table->cEnchants[MOB_ENCHANT_PENETRATE]); +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + VERIFY_IFIELD(MProto::enchant_bleeding, mob_table->cEnchants[MOB_ENCHANT_BLEEDING]); +#endif + + // ## RESIST 11 + VERIFY_IFIELD(MProto::resist_sword, mob_table->cResists[MOB_RESIST_SWORD]); + VERIFY_IFIELD(MProto::resist_twohand, mob_table->cResists[MOB_RESIST_TWOHAND]); + VERIFY_IFIELD(MProto::resist_dagger, mob_table->cResists[MOB_RESIST_DAGGER]); + VERIFY_IFIELD(MProto::resist_bell, mob_table->cResists[MOB_RESIST_BELL]); + VERIFY_IFIELD(MProto::resist_fan, mob_table->cResists[MOB_RESIST_FAN]); + VERIFY_IFIELD(MProto::resist_bow, mob_table->cResists[MOB_RESIST_BOW]); + VERIFY_IFIELD(MProto::resist_fire, mob_table->cResists[MOB_RESIST_FIRE]); + VERIFY_IFIELD(MProto::resist_elect, mob_table->cResists[MOB_RESIST_ELECT]); + VERIFY_IFIELD(MProto::resist_magic, mob_table->cResists[MOB_RESIST_MAGIC]); + VERIFY_IFIELD(MProto::resist_wind, mob_table->cResists[MOB_RESIST_WIND]); + VERIFY_IFIELD(MProto::resist_poison, mob_table->cResists[MOB_RESIST_POISON]); +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_CLAW_AS_DAGGER) + VERIFY_IFIELD(MProto::resist_claw, mob_table->cResists[MOB_RESIST_CLAW]); +#endif +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + VERIFY_IFIELD(MProto::resist_bleeding, mob_table->cResists[MOB_RESIST_BLEEDING]); +#endif + + // ## OTHERS #2 + VERIFY_IFIELD(MProto::dam_multiply, mob_table->fDamMultiply); + VERIFY_IFIELD(MProto::summon, mob_table->dwSummonVnum); + VERIFY_IFIELD(MProto::drain_sp, mob_table->dwDrainSP); + + VERIFY_IFIELD(MProto::polymorph_item, mob_table->dwPolymorphItemVnum); + + VERIFY_IFIELD(MProto::skill_vnum0, mob_table->Skills[0].dwVnum); + VERIFY_IFIELD(MProto::skill_level0, mob_table->Skills[0].bLevel); + VERIFY_IFIELD(MProto::skill_vnum1, mob_table->Skills[1].dwVnum); + VERIFY_IFIELD(MProto::skill_level1, mob_table->Skills[1].bLevel); + VERIFY_IFIELD(MProto::skill_vnum2, mob_table->Skills[2].dwVnum); + VERIFY_IFIELD(MProto::skill_level2, mob_table->Skills[2].bLevel); + VERIFY_IFIELD(MProto::skill_vnum3, mob_table->Skills[3].dwVnum); + VERIFY_IFIELD(MProto::skill_level3, mob_table->Skills[3].bLevel); + VERIFY_IFIELD(MProto::skill_vnum4, mob_table->Skills[4].dwVnum); + VERIFY_IFIELD(MProto::skill_level4, mob_table->Skills[4].bLevel); + + // ## SPECIAL + VERIFY_IFIELD(MProto::sp_berserk, mob_table->bBerserkPoint); + VERIFY_IFIELD(MProto::sp_stoneskin, mob_table->bStoneSkinPoint); + VERIFY_IFIELD(MProto::sp_godspeed, mob_table->bGodSpeedPoint); + VERIFY_IFIELD(MProto::sp_deathblow, mob_table->bDeathBlowPoint); + VERIFY_IFIELD(MProto::sp_revive, mob_table->bRevivePoint); + + sys_log(0, "MOB #%-5d %-24s %-24s level: %-3u rank: %u empire: %d", + mob_table->dwVnum, + mob_table->szName, + mob_table->szLocaleName, + mob_table->bLevel, + mob_table->bRank, + mob_table->bEmpire + ); + ++mob_table; + } + sort(m_vec_mobTable.begin(), m_vec_mobTable.end(), FCompareVnum()); + + fprintf(stdout, "Complete! %u Mobs loaded.\n", addNumber); + return true; +} + +namespace IProto +{ +enum IProtoT +{ + vnum, type, subtype, name, locale_name, gold, shop_buy_price, weight, size, + flag, wearflag, antiflag, immuneflag, refined_vnum, refine_set, magic_pct, + socket_pct, addon_type, limittype0, limitvalue0, limittype1, limitvalue1, + applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, + value0, value1, value2, value3, value4, value5 +#if !defined(ENABLE_AUTODETECT_VNUMRANGE) + , vnum_range +#endif +}; +} + +bool CClientManager::InitializeItemTableFromDB() +{ + char query[2048]; + fprintf(stdout, "Loading item_proto from MySQL\n"); + snprintf(query, sizeof(query), + "SELECT vnum, type, subtype, name, %s, gold, shop_buy_price, weight, size," + " flag, wearflag, antiflag, immuneflag+0, refined_vnum, refine_set, magic_pct," + " socket_pct, addon_type, limittype0, limitvalue0, limittype1, limitvalue1," + " applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2," + " value0, value1, value2, value3, value4, value5" +#if !defined(ENABLE_AUTODETECT_VNUMRANGE) + " , vnum_range" +#endif + " FROM item_proto%s ORDER BY vnum;", + g_stLocaleNameColumn.c_str(), + GetTablePostfix() + ); + + auto pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + DWORD addNumber = pRes->uiNumRows; + if (addNumber == 0) + return false; + + if (!m_vec_itemTable.empty()) + { + sys_log(0, "RELOAD: item_proto"); + m_vec_itemTable.clear(); + m_map_itemTableByVnum.clear(); + } + + m_vec_itemTable.resize(addNumber); + memset(&m_vec_itemTable[0], 0, sizeof(TItemTable) * m_vec_itemTable.size()); + TItemTable * item_table = &m_vec_itemTable[0]; + + MYSQL_ROW data = NULL; + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + // check whether or not the field is NULL or that contains an empty string + // ## GENERAL + VERIFY_IFIELD(IProto::vnum, item_table->dwVnum); + VERIFY_SFIELD(IProto::name, item_table->szName); + VERIFY_SFIELD(IProto::locale_name, item_table->szLocaleName); + VERIFY_IFIELD(IProto::type, item_table->bType); + VERIFY_IFIELD(IProto::subtype, item_table->bSubType); + VERIFY_IFIELD(IProto::weight, item_table->bWeight); + VERIFY_IFIELD(IProto::size, item_table->bSize); + item_table->bSize = MINMAX(1, item_table->bSize, 3); // @fixme179 + VERIFY_IFIELD(IProto::antiflag, item_table->dwAntiFlags); + VERIFY_IFIELD(IProto::flag, item_table->dwFlags); + VERIFY_IFIELD(IProto::wearflag, item_table->dwWearFlags); + VERIFY_IFIELD(IProto::immuneflag, item_table->dwImmuneFlag); + VERIFY_IFIELD(IProto::gold, item_table->dwGold); + VERIFY_IFIELD(IProto::shop_buy_price, item_table->dwShopBuyPrice); + VERIFY_IFIELD(IProto::refined_vnum, item_table->dwRefinedVnum); + VERIFY_IFIELD(IProto::refine_set, item_table->wRefineSet); + VERIFY_IFIELD(IProto::magic_pct, item_table->bAlterToMagicItemPct); + + // ## LIMIT + item_table->cLimitRealTimeFirstUseIndex = -1; + item_table->cLimitTimerBasedOnWearIndex = -1; + + VERIFY_IFIELD(IProto::limittype0, item_table->aLimits[0].bType); + VERIFY_IFIELD(IProto::limitvalue0, item_table->aLimits[0].lValue); + if (LIMIT_REAL_TIME_START_FIRST_USE == item_table->aLimits[0].bType) + item_table->cLimitRealTimeFirstUseIndex = 0; + else if (LIMIT_TIMER_BASED_ON_WEAR == item_table->aLimits[0].bType) + item_table->cLimitTimerBasedOnWearIndex = 0; + + VERIFY_IFIELD(IProto::limittype1, item_table->aLimits[1].bType); + VERIFY_IFIELD(IProto::limitvalue1, item_table->aLimits[1].lValue); + if (LIMIT_REAL_TIME_START_FIRST_USE == item_table->aLimits[1].bType) + item_table->cLimitRealTimeFirstUseIndex = 1; + else if (LIMIT_TIMER_BASED_ON_WEAR == item_table->aLimits[1].bType) + item_table->cLimitTimerBasedOnWearIndex = 1; + + if ((LIMIT_NONE!=item_table->aLimits[0].bType) && // just checking the first limit one is enough + (item_table->aLimits[0].bType == item_table->aLimits[1].bType)) + sys_log(0, "vnum(%u): limittype0(%u)==limittype1(%u)", item_table->dwVnum, item_table->aLimits[0].bType, item_table->aLimits[1].bType); // @warme012 + + // ## APPLY + VERIFY_IFIELD(IProto::applytype0, item_table->aApplies[0].bType); + VERIFY_IFIELD(IProto::applyvalue0, item_table->aApplies[0].lValue); + VERIFY_IFIELD(IProto::applytype1, item_table->aApplies[1].bType); + VERIFY_IFIELD(IProto::applyvalue1, item_table->aApplies[1].lValue); + VERIFY_IFIELD(IProto::applytype2, item_table->aApplies[2].bType); + VERIFY_IFIELD(IProto::applyvalue2, item_table->aApplies[2].lValue); + + // ## VALUE + VERIFY_IFIELD(IProto::value0, item_table->alValues[0]); + VERIFY_IFIELD(IProto::value1, item_table->alValues[1]); + VERIFY_IFIELD(IProto::value2, item_table->alValues[2]); + VERIFY_IFIELD(IProto::value3, item_table->alValues[3]); + VERIFY_IFIELD(IProto::value4, item_table->alValues[4]); + VERIFY_IFIELD(IProto::value5, item_table->alValues[5]); + + VERIFY_IFIELD(IProto::socket_pct, item_table->bGainSocketPct); + VERIFY_IFIELD(IProto::addon_type, item_table->sAddonType); + +#if !defined(ENABLE_AUTODETECT_VNUMRANGE) + VERIFY_IFIELD(IProto::vnum_range, item_table->dwVnumRange); +#else + if (item_table->bType==ITEM_DS) + item_table->dwVnumRange = 99; +#endif + +#ifdef ENABLE_CHECK_SELL_PRICE + auto dwPrice = item_table->dwShopBuyPrice; + #ifndef ENABLE_NO_SELL_PRICE_DIVIDED_BY_5 + dwPrice /= 5; + #endif + if (dwPrice > item_table->dwGold) + { + sys_err("ITEM: #%-5lu %-24s SELL_OVERFLOW dwGold: %u < dwShopBuyPrice %u", + item_table->dwVnum, + item_table->szLocaleName, + item_table->dwGold, + dwPrice + ); + item_table->dwGold = dwPrice; + } +#endif + + m_map_itemTableByVnum.emplace(item_table->dwVnum, item_table); + sys_log(0, "ITEM: #%-5lu %-24s %-24s VAL: %d %ld %d %d %d %d WEAR %d ANTI %d IMMUNE %d REFINE %lu REFINE_SET %u MAGIC_PCT %u", + item_table->dwVnum, + item_table->szName, + item_table->szLocaleName, + item_table->alValues[0], + item_table->alValues[1], + item_table->alValues[2], + item_table->alValues[3], + item_table->alValues[4], + item_table->alValues[5], + item_table->dwWearFlags, + item_table->dwAntiFlags, + item_table->dwImmuneFlag, + item_table->dwRefinedVnum, + item_table->wRefineSet, + item_table->bAlterToMagicItemPct + ); + item_table++; + } + sort(m_vec_itemTable.begin(), m_vec_itemTable.end(), FCompareVnum()); + + fprintf(stdout, "Complete! %u Items loaded.\n", addNumber); + return true; +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerEventFlag.cpp b/source-server/Srcs/Server/db/src/ClientManagerEventFlag.cpp new file mode 100644 index 000000000..6bf242f8f --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerEventFlag.cpp @@ -0,0 +1,77 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" + +void CClientManager::LoadEventFlag() +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "SELECT szName, lValue FROM quest%s WHERE dwPID = 0", GetTablePostfix()); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult* pRes = pmsg->Get(); + if (pRes->uiNumRows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + TPacketSetEventFlag p; + strlcpy(p.szFlagName, row[0], sizeof(p.szFlagName)); + str_to_number(p.lValue, row[1]); + sys_log(0, "EventFlag Load %s %d", p.szFlagName, p.lValue); + m_map_lEventFlag.emplace(p.szFlagName, p.lValue); + ForwardPacket(HEADER_DG_SET_EVENT_FLAG, &p, sizeof(TPacketSetEventFlag)); + } + } +} + +void CClientManager::SetEventFlag(TPacketSetEventFlag* p) +{ + ForwardPacket(HEADER_DG_SET_EVENT_FLAG, p, sizeof(TPacketSetEventFlag)); + + bool bChanged = false; + + typeof(m_map_lEventFlag.begin()) it = m_map_lEventFlag.find(p->szFlagName); + if (it == m_map_lEventFlag.end()) + { + bChanged = true; + m_map_lEventFlag.emplace(p->szFlagName, p->lValue); + } + else if (it->second != p->lValue) + { + bChanged = true; + it->second = p->lValue; + } + + if (bChanged) + { + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(0, '%s', '', %ld)", + GetTablePostfix(), p->szFlagName, p->lValue); + szQuery[1023] = '\0'; + + //CDBManager::instance().ReturnQuery(szQuery, QID_QUEST_SAVE, 0, NULL); + CDBManager::instance().AsyncQuery(szQuery); + sys_log(0, "HEADER_GD_SET_EVENT_FLAG : Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue); + return; + } + sys_log(0, "HEADER_GD_SET_EVENT_FLAG : No Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue); +} + +void CClientManager::SendEventFlagsOnSetup(CPeer* peer) +{ + typeof(m_map_lEventFlag.begin()) it; + for (it = m_map_lEventFlag.begin(); it != m_map_lEventFlag.end(); ++it) + { + TPacketSetEventFlag p; + strlcpy(p.szFlagName, it->first.c_str(), sizeof(p.szFlagName)); + p.lValue = it->second; + peer->EncodeHeader(HEADER_DG_SET_EVENT_FLAG, 0, sizeof(TPacketSetEventFlag)); + peer->Encode(&p, sizeof(TPacketSetEventFlag)); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerGuild.cpp b/source-server/Srcs/Server/db/src/ClientManagerGuild.cpp new file mode 100644 index 000000000..fb8cb5032 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerGuild.cpp @@ -0,0 +1,244 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" +#include "GuildManager.h" + +void CClientManager::GuildCreate(CPeer * peer, DWORD dwGuildID) +{ + sys_log(0, "GuildCreate %u", dwGuildID); + ForwardPacket(HEADER_DG_GUILD_LOAD, &dwGuildID, sizeof(DWORD)); + + CGuildManager::instance().Load(dwGuildID); +} + +void CClientManager::GuildChangeGrade(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildChangeGrade %u %u", p->dwGuild, p->dwInfo); + ForwardPacket(HEADER_DG_GUILD_CHANGE_GRADE, p, sizeof(TPacketGuild)); +} + +void CClientManager::GuildAddMember(CPeer* peer, TPacketGDGuildAddMember * p) +{ + CGuildManager::instance().TouchGuild(p->dwGuild); + sys_log(0, "GuildAddMember %u %u", p->dwGuild, p->dwPID); + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_member%s VALUES(%u, %u, %d, 0, 0)", + GetTablePostfix(), p->dwPID, p->dwGuild, p->bGrade); + + auto pmsg_insert(CDBManager::instance().DirectQuery(szQuery)); + + snprintf(szQuery, sizeof(szQuery), + "SELECT pid, grade, is_general, offer, level, job, name FROM guild_member%s, player%s WHERE guild_id = %u and pid = id and pid = %u", GetTablePostfix(), GetTablePostfix(), p->dwGuild, p->dwPID); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + if (pmsg->Get()->uiNumRows == 0) + { + sys_err("Query failed when getting guild member data %s", pmsg->stQuery.c_str()); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); + + if (!row[0] || !row[1]) + return; + + TPacketDGGuildMember dg; + + dg.dwGuild = p->dwGuild; + str_to_number(dg.dwPID, row[0]); + str_to_number(dg.bGrade, row[1]); + str_to_number(dg.isGeneral, row[2]); + str_to_number(dg.dwOffer, row[3]); + str_to_number(dg.bLevel, row[4]); + str_to_number(dg.bJob, row[5]); + strlcpy(dg.szName, row[6], sizeof(dg.szName)); + + ForwardPacket(HEADER_DG_GUILD_ADD_MEMBER, &dg, sizeof(TPacketDGGuildMember)); +} + +void CClientManager::GuildRemoveMember(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildRemoveMember %u %u", p->dwGuild, p->dwInfo); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE pid=%u and guild_id=%u", GetTablePostfix(), p->dwInfo, p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + // @fixme202 new_+withdraw_time + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(%u, 'guild_manage', 'new_withdraw_time', %u)", GetTablePostfix(), p->dwInfo, (DWORD) GetCurrentTime()); + CDBManager::instance().AsyncQuery(szQuery); + + ForwardPacket(HEADER_DG_GUILD_REMOVE_MEMBER, p, sizeof(TPacketGuild)); +} + +void CClientManager::GuildSkillUpdate(CPeer* peer, TPacketGuildSkillUpdate* p) +{ + sys_log(0, "GuildSkillUpdate %d", p->amount); + ForwardPacket(HEADER_DG_GUILD_SKILL_UPDATE, p, sizeof(TPacketGuildSkillUpdate)); +} + +void CClientManager::GuildExpUpdate(CPeer* peer, TPacketGuildExpUpdate* p) +{ + sys_log(0, "GuildExpUpdate %d", p->amount); + ForwardPacket(HEADER_DG_GUILD_EXP_UPDATE, p, sizeof(TPacketGuildExpUpdate), 0, peer); +} + +void CClientManager::GuildChangeMemberData(CPeer* peer, TPacketGuildChangeMemberData* p) +{ + sys_log(0, "GuildChangeMemberData %u %u %d %d", p->pid, p->offer, p->level, p->grade); + ForwardPacket(HEADER_DG_GUILD_CHANGE_MEMBER_DATA, p, sizeof(TPacketGuildChangeMemberData), 0, peer); +} + +void CClientManager::GuildDisband(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildDisband %u", p->dwGuild); + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild%s WHERE id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_grade%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + // @fixme401 (withdraw -> new_disband)_time + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) SELECT pid, 'guild_manage', 'new_disband_time', %u FROM guild_member%s WHERE guild_id = %u", GetTablePostfix(), (DWORD) GetCurrentTime(), GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_comment%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + ForwardPacket(HEADER_DG_GUILD_DISBAND, p, sizeof(TPacketGuild)); +} + +const char* __GetWarType(int n) +{ + switch (n) + { + case 0 : + return "Field"; + case 1 : + return "Theater"; + case 2 : + return "CTF"; //Capture The Flag + default : + return "Wrong number"; + } +} + +void CClientManager::GuildWar(CPeer* peer, TPacketGuildWar* p) +{ + switch (p->bWar) + { + case GUILD_WAR_SEND_DECLARE: + sys_log(0, "GuildWar: GUILD_WAR_SEND_DECLARE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().AddDeclare(p->bType, p->dwGuildFrom, p->dwGuildTo); + break; + + case GUILD_WAR_REFUSE: + sys_log(0, "GuildWar: GUILD_WAR_REFUSE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + break; + /* + case GUILD_WAR_WAIT_START: + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + + if (!CGuildManager::instance().WaitStart(p)) + p->bWar = GUILD_WAR_CANCEL; + + break; + */ + + case GUILD_WAR_WAIT_START: + sys_log(0, "GuildWar: GUILD_WAR_WAIT_START type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + case GUILD_WAR_RESERVE: + if (p->bWar != GUILD_WAR_WAIT_START) + sys_log(0, "GuildWar: GUILD_WAR_RESERVE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + + if (!CGuildManager::instance().ReserveWar(p)) + p->bWar = GUILD_WAR_CANCEL; + else + p->bWar = GUILD_WAR_RESERVE; + + break; + + case GUILD_WAR_ON_WAR: + sys_log(0, "GuildWar: GUILD_WAR_ON_WAR type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().StartWar(p->bType, p->dwGuildFrom, p->dwGuildTo); + break; + + case GUILD_WAR_OVER: + sys_log(0, "GuildWar: GUILD_WAR_OVER type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RecvWarOver(p->dwGuildFrom, p->dwGuildTo, p->bType, p->lWarPrice); + break; + + case GUILD_WAR_END: + sys_log(0, "GuildWar: GUILD_WAR_END type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RecvWarEnd(p->dwGuildFrom, p->dwGuildTo); + return; + + case GUILD_WAR_CANCEL : + sys_log(0, "GuildWar: GUILD_WAR_CANCEL type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().CancelWar(p->dwGuildFrom, p->dwGuildTo); + break; + } + + ForwardPacket(HEADER_DG_GUILD_WAR, p, sizeof(TPacketGuildWar)); +} + +void CClientManager::GuildWarScore(CPeer* peer, TPacketGuildWarScore * p) +{ + CGuildManager::instance().UpdateScore(p->dwGuildGainPoint, p->dwGuildOpponent, p->lScore, p->lBetScore); +} + +void CClientManager::GuildChangeLadderPoint(TPacketGuildLadderPoint* p) +{ + sys_log(0, "GuildChangeLadderPoint Recv %u %d", p->dwGuild, p->lChange); + CGuildManager::instance().ChangeLadderPoint(p->dwGuild, p->lChange); +} + +void CClientManager::GuildUseSkill(TPacketGuildUseSkill* p) +{ + sys_log(0, "GuildUseSkill Recv %u %d", p->dwGuild, p->dwSkillVnum); + CGuildManager::instance().UseSkill(p->dwGuild, p->dwSkillVnum, p->dwCooltime); + SendGuildSkillUsable(p->dwGuild, p->dwSkillVnum, false); +} + +void CClientManager::SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable) +{ + sys_log(0, "SendGuildSkillUsable Send %u %d %s", guild_id, dwSkillVnum, bUsable?"true":"false"); + + TPacketGuildSkillUsableChange p; + + p.dwGuild = guild_id; + p.dwSkillVnum = dwSkillVnum; + p.bUsable = bUsable; + + ForwardPacket(HEADER_DG_GUILD_SKILL_USABLE_CHANGE, &p, sizeof(TPacketGuildSkillUsableChange)); +} + +void CClientManager::GuildChangeMaster(TPacketChangeGuildMaster* p) +{ + if (CGuildManager::instance().ChangeMaster(p->dwGuildID, p->idFrom, p->idTo) == true) + { + TPacketChangeGuildMaster packet; + packet.dwGuildID = p->dwGuildID; + packet.idFrom = 0; + packet.idTo = 0; + + ForwardPacket(HEADER_DG_ACK_CHANGE_GUILD_MASTER, &packet, sizeof(packet)); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerHorseName.cpp b/source-server/Srcs/Server/db/src/ClientManagerHorseName.cpp new file mode 100644 index 000000000..62a9694a4 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerHorseName.cpp @@ -0,0 +1,39 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" + +void CClientManager::UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer) +{ + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO horse_name VALUES(%u, '%s')", data->dwPlayerID, data->szHorseName); + + auto pmsg_insert(CDBManager::instance().DirectQuery(szQuery)); + + ForwardPacket(HEADER_DG_UPDATE_HORSE_NAME, data, sizeof(TPacketUpdateHorseName), 0, peer); +} + +void CClientManager::AckHorseName(DWORD dwPID, CPeer* peer) +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "SELECT name FROM horse_name WHERE id = %u", dwPID); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + TPacketUpdateHorseName packet; + packet.dwPlayerID = dwPID; + + if (pmsg->Get()->uiNumRows == 0) + { + memset(packet.szHorseName, 0, sizeof (packet.szHorseName)); + } + else + { + MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); + strlcpy(packet.szHorseName, row[0], sizeof(packet.szHorseName)); + } + + peer->EncodeHeader(HEADER_DG_ACK_HORSE_NAME, 0, sizeof(TPacketUpdateHorseName)); + peer->Encode(&packet, sizeof(TPacketUpdateHorseName)); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerLogin.cpp b/source-server/Srcs/Server/db/src/ClientManagerLogin.cpp new file mode 100644 index 000000000..6abf4415d --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerLogin.cpp @@ -0,0 +1,538 @@ +#include "stdafx.h" +#include "../../common/CommonDefines.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "Config.h" +#include "QID.h" +#include "Cache.h" + +extern std::string g_stLocale; +extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab); +extern int g_test_server; +extern int g_log; + +bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin); + + if (m_map_kLogonAccount.end() != it) + return false; + + CLoginData * pkLD = GetLoginDataByLogin(c_pszLogin); + + if (!pkLD) + return false; + + pkLD->SetConnectedPeerHandle(dwHandle); + pkLD->SetIP(c_pszIP); + + m_map_kLogonAccount.emplace(szLogin, pkLD); + return true; +} + +bool CClientManager::DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin); + + if (it == m_map_kLogonAccount.end()) + return false; + + CLoginData * pkLD = it->second; + + if (pkLD->GetConnectedPeerHandle() != dwHandle) + { + sys_err("%s tried to logout in other peer handle %lu, current handle %lu", szLogin, dwHandle, pkLD->GetConnectedPeerHandle()); + return false; + } + + if (pkLD->IsPlay()) + { + pkLD->SetPlay(false); + SendLoginToBilling(pkLD, false); + } + + if (pkLD->IsDeleted()) + { + delete pkLD; + } + + m_map_kLogonAccount.erase(it); + return true; +} + +bool CClientManager::FindLogonAccount(const char * c_pszLogin) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + if (m_map_kLogonAccount.end() == m_map_kLogonAccount.find(szLogin)) + return false; + + return true; +} + +void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p) +{ +#ifdef ENABLE_LIMIT_TIME + static int s_updateCount = 0; + static int s_curTime = time(0); + if (s_updateCount > 100) + { + s_curTime = time(0); + s_updateCount = 0; + } + ++s_updateCount; + + if (s_curTime >= GLOBAL_LIMIT_TIME) + { + sys_err("Server life time expired."); + exit(0); + return; + } +#endif + + CLoginData * pkLoginData = GetLoginData(p->dwLoginKey); + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(p->szLogin, szLogin, sizeof(szLogin)); + + if (!pkLoginData) + { + sys_log(0, "LOGIN_BY_KEY key not exist %s %lu", szLogin, p->dwLoginKey); + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + TAccountTable & r = pkLoginData->GetAccountRef(); + + if (FindLogonAccount(r.login)) + { + sys_log(0, "LOGIN_BY_KEY already login %s %lu", r.login, p->dwLoginKey); + TPacketDGLoginAlready ptog; + strlcpy(ptog.szLogin, szLogin, sizeof(ptog.szLogin)); + pkPeer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, dwHandle, sizeof(TPacketDGLoginAlready)); + pkPeer->Encode(&ptog, sizeof(TPacketDGLoginAlready)); + return; + } + + if (strcasecmp(r.login, szLogin)) + { + sys_log(0, "LOGIN_BY_KEY login differ %s %lu input %s", r.login, p->dwLoginKey, szLogin); + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + if (memcmp(pkLoginData->GetClientKey(), p->adwClientKey, sizeof(DWORD) * 4)) + { + const DWORD * pdwClientKey = pkLoginData->GetClientKey(); + + sys_log(0, "LOGIN_BY_KEY client key differ %s %lu %lu %lu %lu, %lu %lu %lu %lu", + r.login, + p->adwClientKey[0], p->adwClientKey[1], p->adwClientKey[2], p->adwClientKey[3], + pdwClientKey[0], pdwClientKey[1], pdwClientKey[2], pdwClientKey[3]); + + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + TAccountTable * pkTab = new TAccountTable; + memset(pkTab, 0, sizeof(TAccountTable)); + + pkTab->id = r.id; + trim_and_lower(r.login, pkTab->login, sizeof(pkTab->login)); + strlcpy(pkTab->passwd, r.passwd, sizeof(pkTab->passwd)); + strlcpy(pkTab->social_id, r.social_id, sizeof(pkTab->social_id)); + strlcpy(pkTab->status, "OK", sizeof(pkTab->status)); + + ClientHandleInfo * info = new ClientHandleInfo(dwHandle); + info->pAccountTable = pkTab; + strlcpy(info->ip, p->szIP, sizeof(info->ip)); + + sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip); + char szQuery[QUERY_MAX_LEN]; +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); +#else + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); +#endif + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); +} + +void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + if (msg->uiSQLErrno != 0) + { + peer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle); + delete info; + return; + } + + char szQuery[QUERY_MAX_LEN]; + + if (msg->Get()->uiNumRows == 0) + { + DWORD account_id = info->pAccountTable->id; + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, pid3, pid4," + #ifdef ENABLE_PLAYER_PER_ACCOUNT5 + " pid5," + #endif + " empire FROM player_index%s WHERE id=%u", GetTablePostfix(), account_id); + auto pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER)); + + sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id); + + // PLAYER_INDEX_CREATE_BUG_FIX + //snprintf(szQuery, sizeof(szQuery), "INSERT IGNORE INTO player_index%s (id) VALUES(%lu)", GetTablePostfix(), info->pAccountTable->id); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO player_index%s (id) VALUES(%u)", GetTablePostfix(), info->pAccountTable->id); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_INDEX_CREATE, peer->GetHandle(), info); + // END_PLAYER_INDEX_CREATE_BUF_FIX + } + return; + } + + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + int col = 0; + + for (; col < PLAYER_PER_ACCOUNT; ++col) + str_to_number(info->pAccountTable->players[col].dwID, row[col]); + + str_to_number(info->pAccountTable->bEmpire, row[col++]); + info->account_index = 1; + + snprintf(szQuery, sizeof(szQuery), + "SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair," + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "part_acce," + #endif + " x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN, peer->GetHandle(), info); +} + +// PLAYER_INDEX_CREATE_BUG_FIX +void CClientManager::RESULT_PLAYER_INDEX_CREATE(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + char szQuery[QUERY_MAX_LEN]; +#ifdef ENABLE_PLAYER_PER_ACCOUNT5 + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), + info->pAccountTable->id); +#else + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), + info->pAccountTable->id); +#endif + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); +} +// END_PLAYER_INDEX_CREATE_BUG_FIX + +TAccountTable * CreateAccountTableFromRes(MYSQL_RES * res) +{ + char input_pwd[PASSWD_MAX_LEN + 1]; + MYSQL_ROW row = NULL; + DWORD col; + + row = mysql_fetch_row(res); + col = 0; + + TAccountTable * pkTab = new TAccountTable; + memset(pkTab, 0, sizeof(TAccountTable)); + + strlcpy(input_pwd, row[col++], sizeof(input_pwd)); + str_to_number(pkTab->id, row[col++]); + strlcpy(pkTab->login, row[col++], sizeof(pkTab->login)); + strlcpy(pkTab->passwd, row[col++], sizeof(pkTab->passwd)); + strlcpy(pkTab->social_id, row[col++], sizeof(pkTab->social_id)); + str_to_number(pkTab->bEmpire, row[col++]); + + for (int j = 0; j < PLAYER_PER_ACCOUNT; ++j) + str_to_number(pkTab->players[j].dwID, row[col++]); + + strlcpy(pkTab->status, row[col++], sizeof(pkTab->status)); + + if (strcmp(pkTab->passwd, input_pwd)) + { + delete pkTab; + return NULL; + } + + return pkTab; +} + +void CreateAccountPlayerDataFromRes(MYSQL_RES * pRes, TAccountTable * pkTab) +{ + if (!pRes) + return; + + for (DWORD i = 0; i < mysql_num_rows(pRes); ++i) + { + MYSQL_ROW row = mysql_fetch_row(pRes); + int col = 0; + + DWORD player_id = 0; + !row[col++] ? 0 : str_to_number(player_id, row[col - 1]); + + if (!player_id) + continue; + + int j; + + for (j = 0; j < PLAYER_PER_ACCOUNT; ++j) + { + if (pkTab->players[j].dwID == player_id) + { + CPlayerTableCache * pc = CClientManager::instance().GetPlayerCache(player_id); + TPlayerTable * pt = pc ? pc->Get(false) : NULL; + + if (pt) + { + strlcpy(pkTab->players[j].szName, pt->name, sizeof(pkTab->players[j].szName)); + + pkTab->players[j].byJob = pt->job; + pkTab->players[j].byLevel = pt->level; + pkTab->players[j].dwPlayMinutes = pt->playtime; + pkTab->players[j].byST = pt->st; + pkTab->players[j].byHT = pt->ht; + pkTab->players[j].byDX = pt->dx; + pkTab->players[j].byIQ = pt->iq; + pkTab->players[j].wMainPart = pt->parts[PART_MAIN]; + pkTab->players[j].wHairPart = pt->parts[PART_HAIR]; +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + pkTab->players[j].wAccePart = pt->parts[PART_ACCE]; +#endif + pkTab->players[j].x = pt->x; + pkTab->players[j].y = pt->y; + pkTab->players[j].skill_group = pt->skill_group; + pkTab->players[j].bChangeName = 0; + } + else + { + if (!row[col++]) + *pkTab->players[j].szName = '\0'; + else + strlcpy(pkTab->players[j].szName, row[col - 1], sizeof(pkTab->players[j].szName)); + + pkTab->players[j].byJob = 0; + pkTab->players[j].byLevel = 0; + pkTab->players[j].dwPlayMinutes = 0; + pkTab->players[j].byST = 0; + pkTab->players[j].byHT = 0; + pkTab->players[j].byDX = 0; + pkTab->players[j].byIQ = 0; + pkTab->players[j].wMainPart = 0; + pkTab->players[j].wHairPart = 0; +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + pkTab->players[j].wAccePart = 0; +#endif + pkTab->players[j].x = 0; + pkTab->players[j].y = 0; + pkTab->players[j].skill_group = 0; + pkTab->players[j].bChangeName = 0; + + str_to_number(pkTab->players[j].byJob, row[col++]); + str_to_number(pkTab->players[j].byLevel, row[col++]); + str_to_number(pkTab->players[j].dwPlayMinutes, row[col++]); + str_to_number(pkTab->players[j].byST, row[col++]); + str_to_number(pkTab->players[j].byHT, row[col++]); + str_to_number(pkTab->players[j].byDX, row[col++]); + str_to_number(pkTab->players[j].byIQ, row[col++]); + str_to_number(pkTab->players[j].wMainPart, row[col++]); + str_to_number(pkTab->players[j].wHairPart, row[col++]); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + str_to_number(pkTab->players[j].wAccePart, row[col++]); +#endif + str_to_number(pkTab->players[j].x, row[col++]); + str_to_number(pkTab->players[j].y, row[col++]); + str_to_number(pkTab->players[j].skill_group, row[col++]); + str_to_number(pkTab->players[j].bChangeName, row[col++]); + } + + sys_log(0, "%s %lu %lu hair %u", + pkTab->players[j].szName, pkTab->players[j].x, pkTab->players[j].y, pkTab->players[j].wHairPart); + break; + } + } + } +} + +void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + if (info->account_index == 0) + { + if (msg->Get()->uiNumRows == 0) + { + sys_log(0, "RESULT_LOGIN: no account"); + peer->EncodeHeader(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle, 0); + delete info; + return; + } + + info->pAccountTable = CreateAccountTableFromRes(msg->Get()->pSQLResult); + + if (!info->pAccountTable) + { + sys_log(0, "RESULT_LOGIN: no account : WRONG_PASSWD"); + peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle); + delete info; + } + else + { + ++info->account_index; + + char queryStr[512]; + snprintf(queryStr, sizeof(queryStr), + "SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair," + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "part_acce, " + #endif + " x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + + CDBManager::instance().ReturnQuery(queryStr, QID_LOGIN, peer->GetHandle(), info); + } + return; + } + else + { + if (!info->pAccountTable) + { + peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle); + delete info; + return; + } + + if (!InsertLogonAccount(info->pAccountTable->login, peer->GetHandle(), info->ip)) + { + sys_log(0, "RESULT_LOGIN: already logon %s", info->pAccountTable->login); + + TPacketDGLoginAlready p; + strlcpy(p.szLogin, info->pAccountTable->login, sizeof(p.szLogin)); + + peer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, info->dwHandle, sizeof(TPacketDGLoginAlready)); + peer->Encode(&p, sizeof(p)); + } + else + { + sys_log(0, "RESULT_LOGIN: login success %s rows: %lu", info->pAccountTable->login, msg->Get()->uiNumRows); + + if (msg->Get()->uiNumRows > 0) + CreateAccountPlayerDataFromRes(msg->Get()->pSQLResult, info->pAccountTable); + + //PREVENT_COPY_ITEM + CLoginData * p = GetLoginDataByLogin(info->pAccountTable->login); + memcpy(&p->GetAccountRef(), info->pAccountTable, sizeof(TAccountTable)); + + //END_PREVENT_COPY_ITEM + peer->EncodeHeader(HEADER_DG_LOGIN_SUCCESS, info->dwHandle, sizeof(TAccountTable)); + peer->Encode(info->pAccountTable, sizeof(TAccountTable)); + + } + + delete info->pAccountTable; + info->pAccountTable = NULL; + delete info; + } +} + +void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data) +{ + TLogoutPacket* packet = (TLogoutPacket*)data; + + if (!*packet->login) + return; + + CLoginData * pLoginData = GetLoginDataByLogin(packet->login); + + if (pLoginData == NULL) + return; + + int pid[PLAYER_PER_ACCOUNT]; + + for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n) + { + if (pLoginData->GetAccountRef().players[n].dwID == 0) + { + if (g_test_server) + sys_log(0, "LOGOUT %s %d", packet->login, pLoginData->GetAccountRef().players[n].dwID); + continue; + } + + pid[n] = pLoginData->GetAccountRef().players[n].dwID; + + if (g_log) + sys_log(0, "LOGOUT InsertLogoutPlayer %s %d", packet->login, pid[n]); + + InsertLogoutPlayer(pid[n]); + } + + if (DeleteLogonAccount(packet->login, peer->GetHandle())) + { + if (g_log) + sys_log(0, "LOGOUT %s ", packet->login); + } +} + +void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p) +{ + char queryStr[QUERY_MAX_LEN]; + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid); + + auto pMsg(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER)); + + if (pMsg->Get()->uiNumRows) + { + if (!pMsg->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + if (*row[0] != '0') + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + snprintf(queryStr, sizeof(queryStr), + "UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid); + + auto pMsg0(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER)); + + TPacketDGChangeName pdg; + peer->EncodeHeader(HEADER_DG_CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName)); + pdg.pid = p->pid; + strlcpy(pdg.name, p->name, sizeof(pdg.name)); + peer->Encode(&pdg, sizeof(TPacketDGChangeName)); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerParty.cpp b/source-server/Srcs/Server/db/src/ClientManagerParty.cpp new file mode 100644 index 000000000..d8cdd6be5 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerParty.cpp @@ -0,0 +1,136 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" + +void CClientManager::QUERY_PARTY_CREATE(CPeer* peer, TPacketPartyCreate* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + + if (pm.find(p->dwLeaderPID) == pm.end()) + { + pm.emplace(p->dwLeaderPID, TPartyMember()); + ForwardPacket(HEADER_DG_PARTY_CREATE, p, sizeof(TPacketPartyCreate), peer->GetChannel(), peer); + sys_log(0, "PARTY Create [%lu]", p->dwLeaderPID); + } + else + { + sys_err("PARTY Create - Already exists [%lu]", p->dwLeaderPID); + } +} + +void CClientManager::QUERY_PARTY_DELETE(CPeer* peer, TPacketPartyDelete* p) +{ + TPartyMap& pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Delete - Non exists [%lu]", p->dwLeaderPID); + return; + } + + pm.erase(it); + ForwardPacket(HEADER_DG_PARTY_DELETE, p, sizeof(TPacketPartyDelete), peer->GetChannel(), peer); + sys_log(0, "PARTY Delete [%lu]", p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_ADD(CPeer* peer, TPacketPartyAdd* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Add - Non exists [%lu]", p->dwLeaderPID); + return; + } + + if (it->second.find(p->dwPID) == it->second.end()) + { + it->second.emplace(p->dwPID, TPartyInfo()); + ForwardPacket(HEADER_DG_PARTY_ADD, p, sizeof(TPacketPartyAdd), peer->GetChannel(), peer); + sys_log(0, "PARTY Add [%lu] to [%lu]", p->dwPID, p->dwLeaderPID); + } + else + sys_err("PARTY Add - Already [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_REMOVE(CPeer* peer, TPacketPartyRemove* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Remove - Non exists [%lu] cannot remove [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit != it->second.end()) + { + it->second.erase(pit); + ForwardPacket(HEADER_DG_PARTY_REMOVE, p, sizeof(TPacketPartyRemove), peer->GetChannel(), peer); + sys_log(0, "PARTY Remove [%lu] to [%lu]", p->dwPID, p->dwLeaderPID); + } + else + sys_err("PARTY Remove - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_STATE_CHANGE(CPeer* peer, TPacketPartyStateChange* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY StateChange - Non exists [%lu] cannot state change [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit == it->second.end()) + { + sys_err("PARTY StateChange - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); + return; + } + + if (p->bFlag) + pit->second.bRole = p->bRole; + else + pit->second.bRole = 0; + + ForwardPacket(HEADER_DG_PARTY_STATE_CHANGE, p, sizeof(TPacketPartyStateChange), peer->GetChannel(), peer); + sys_log(0, "PARTY StateChange [%lu] at [%lu] from %d %d",p->dwPID, p->dwLeaderPID, p->bRole, p->bFlag); +} + +void CClientManager::QUERY_PARTY_SET_MEMBER_LEVEL(CPeer* peer, TPacketPartySetMemberLevel* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY SetMemberLevel - Non exists [%lu] cannot level change [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit == it->second.end()) + { + sys_err("PARTY SetMemberLevel - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); + return; + } + + pit->second.bLevel = p->bLevel; + + ForwardPacket(HEADER_DG_PARTY_SET_MEMBER_LEVEL, p, sizeof(TPacketPartySetMemberLevel), peer->GetChannel()); + sys_log(0, "PARTY SetMemberLevel pid [%lu] level %d",p->dwPID, p->bLevel); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ClientManagerPlayer.cpp b/source-server/Srcs/Server/db/src/ClientManagerPlayer.cpp new file mode 100644 index 000000000..6735074f8 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ClientManagerPlayer.cpp @@ -0,0 +1,1307 @@ +#include "stdafx.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "QID.h" +#include "ItemAwardManager.h" +#include "HB.h" +#include "Cache.h" + +extern bool g_bHotBackup; + +extern std::string g_stLocale; +extern int g_test_server; +extern int g_log; + +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Check all SELECT syntax on item table before change this function!!! +// +bool CreateItemTableFromRes(MYSQL_RES * res, std::vector * pVec, DWORD dwPID) +{ + if (!res) + { + pVec->clear(); + return true; + } + + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) + { + pVec->clear(); + return true; + } + + pVec->resize(rows); + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TPlayerItem & item = pVec->at(i); + + int cur = 0; + + // Check all SELECT syntax on item table before change this function!!! + // Check all SELECT syntax on item table before change this function!!! + // Check all SELECT syntax on item table before change this function!!! + str_to_number(item.id, row[cur++]); + str_to_number(item.window, row[cur++]); + str_to_number(item.pos, row[cur++]); + str_to_number(item.count, row[cur++]); + str_to_number(item.vnum, row[cur++]); + str_to_number(item.alSockets[0], row[cur++]); + str_to_number(item.alSockets[1], row[cur++]); + str_to_number(item.alSockets[2], row[cur++]); + + for (int j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) + { + str_to_number(item.aAttr[j].bType, row[cur++]); + str_to_number(item.aAttr[j].sValue, row[cur++]); + } + + item.owner = dwPID; + } + + return true; +} + +size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab) +{ + size_t queryLen; + + queryLen = snprintf(pszQuery, querySize, + "UPDATE player%s SET " + "job = %d, " + "voice = %d, " + "dir = %d, " + "x = %d, " + "y = %d, " + "z = %d, " + "map_index = %d, " + "exit_x = %ld, " + "exit_y = %ld, " + "exit_map_index = %ld, " + "hp = %d, " + "mp = %d, " + "stamina = %d, " + "random_hp = %d, " + "random_sp = %d, " + "playtime = %d, " + "level = %d, " + "level_step = %d, " + "st = %d, " + "ht = %d, " + "dx = %d, " + "iq = %d, " + "gold = %d, " + "exp = %u, " + "stat_point = %d, " + "skill_point = %d, " + "sub_skill_point = %d, " + "stat_reset_count = %d, " + "ip = '%s', " + "part_main = %d, " + "part_hair = %d, " + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "part_acce = %d, " + #endif + "last_play = NOW(), " + "skill_group = %d, " + "alignment = %ld, " + "horse_level = %d, " + "horse_riding = %d, " + "horse_hp = %d, " + "horse_hp_droptime = %u, " + "horse_stamina = %d, " + "horse_skill_point = %d, " +#ifdef ENABLE_CHEQUE_SYSTEM + "cheque = %d, " +#endif + , + GetTablePostfix(), + pkTab->job, + pkTab->voice, + pkTab->dir, + pkTab->x, + pkTab->y, + pkTab->z, + pkTab->lMapIndex, + pkTab->lExitX, + pkTab->lExitY, + pkTab->lExitMapIndex, + pkTab->hp, + pkTab->sp, + pkTab->stamina, + pkTab->sRandomHP, + pkTab->sRandomSP, + pkTab->playtime, + pkTab->level, + pkTab->level_step, + pkTab->st, + pkTab->ht, + pkTab->dx, + pkTab->iq, + pkTab->gold, + pkTab->exp, + pkTab->stat_point, + pkTab->skill_point, + pkTab->sub_skill_point, + pkTab->stat_reset_count, + pkTab->ip, + pkTab->parts[PART_MAIN], + pkTab->parts[PART_HAIR], +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + pkTab->parts[PART_ACCE], +#endif + pkTab->skill_group, + pkTab->lAlignment, + pkTab->horse.bLevel, + pkTab->horse.bRiding, + pkTab->horse.sHealth, + pkTab->horse.dwHorseHealthDropTime, + pkTab->horse.sStamina, + pkTab->horse_skill_point +#ifdef ENABLE_CHEQUE_SYSTEM + , pkTab->cheque +#endif + ); + + static char text[8192 + 1]; + + CDBManager::instance().EscapeString(text, pkTab->skills, sizeof(pkTab->skills)); + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "skill_level = '%s', ", text); + + CDBManager::instance().EscapeString(text, pkTab->quickslot, sizeof(pkTab->quickslot)); + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "quickslot = '%s' ", text); + + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, " WHERE id=%d", pkTab->id); + return queryLen; +} + +CPlayerTableCache * CClientManager::GetPlayerCache(DWORD id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find(id); + + if (it == m_map_playerCache.end()) + return NULL; + + TPlayerTable* pTable = it->second->Get(false); + pTable->logoff_interval = GetCurrentTime() - it->second->GetLastUpdateTime(); + return it->second; +} + +void CClientManager::PutPlayerCache(TPlayerTable * pNew) +{ + CPlayerTableCache * c; + + c = GetPlayerCache(pNew->id); + + if (!c) + { + c = new CPlayerTableCache; + m_map_playerCache.emplace(pNew->id, c); + } + + if (g_bHotBackup) + PlayerHB::instance().Put(pNew->id); + + c->Put(pNew); +} + +/* + * PLAYER LOAD + */ +void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket * packet) +{ + CPlayerTableCache * c; + TPlayerTable * pTab; + + CLoginData * pLoginData = GetLoginDataByAID(packet->account_id); + + if (pLoginData) + { + for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n) + if (pLoginData->GetAccountRef().players[n].dwID != 0) + DeleteLogoutPlayer(pLoginData->GetAccountRef().players[n].dwID); + } + + //---------------------------------------------------------------- + + // --------------------------------------------------------------- + + //---------------------------------- + + //---------------------------------- + if ((c = GetPlayerCache(packet->player_id))) + { + CLoginData * pkLD = GetLoginDataByAID(packet->account_id); + + if (!pkLD || pkLD->IsPlay()) + { + sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0); + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, dwHandle, 0); + return; + } + + pTab = c->Get(); + + pkLD->SetPlay(true); + SendLoginToBilling(pkLD, true); + thecore_memcpy(pTab->aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(pTab->aiPremiumTimes)); + + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_SUCCESS, dwHandle, sizeof(TPlayerTable)); + peer->Encode(*pTab); + + if (packet->player_id != pkLD->GetLastPlayerID()) + { + TPacketNeedLoginLogInfo logInfo; + logInfo.dwPlayerID = packet->player_id; + + pkLD->SetLastPlayerID( packet->player_id ); + + peer->EncodeHeader( HEADER_DG_NEED_LOGIN_LOG, dwHandle, sizeof(TPacketNeedLoginLogInfo) ); + peer->Encode(logInfo); + } + + char szQuery[1024] = { 0, }; + + TItemCacheSet * pSet = GetItemCacheSet(pTab->id); + + sys_log(0, "[PLAYER_LOAD] ID %s pid %d gold %d ", pTab->name, pTab->id, pTab->gold); + + //-------------------------------------------- + + //-------------------------------------------- + + ///////////////////////////////////////////// + + ///////////////////////////////////////////// + if (pSet) + { + static std::vector s_items; + s_items.resize(pSet->size()); + + DWORD dwCount = 0; + TItemCacheSet::iterator it = pSet->begin(); + + while (it != pSet->end()) + { + CItemCache * c = *it++; + TPlayerItem * p = c->Get(); + + if (p->vnum) + thecore_memcpy(&s_items[dwCount++], p, sizeof(TPlayerItem)); + } + + if (g_test_server) + sys_log(0, "ITEM_CACHE: HIT! %s count: %u", pTab->name, dwCount); + + peer->EncodeHeader(HEADER_DG_ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount); + peer->EncodeDWORD(dwCount); + + if (dwCount) + peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount); + + // Quest + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d AND lValue<>0", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle,0,packet->account_id)); + + // Affect + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + // @fixme402 ClientHandleInfo+pTab->id + CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, pTab->id)); + } + ///////////////////////////////////////////// + + ///////////////////////////////////////////// + else + { + snprintf(szQuery, sizeof(szQuery), + "SELECT id,`window`+0,pos,count,vnum,socket0,socket1,socket2,attrtype0,attrvalue0,attrtype1,attrvalue1,attrtype2,attrvalue2,attrtype3,attrvalue3,attrtype4,attrvalue4,attrtype5,attrvalue5,attrtype6,attrvalue6 " + "FROM item%s WHERE owner_id=%d AND (`window` in ('INVENTORY','EQUIPMENT','DRAGON_SOUL_INVENTORY','BELT_INVENTORY'))", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, + QID_ITEM, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID, szName, szState, lValue FROM quest%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, + QID_QUEST, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, + QID_AFFECT, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + } + //ljw + //return; + } + //---------------------------------- + + //---------------------------------- + else + { + sys_log(0, "[PLAYER_LOAD] Load from PlayerDB pid[%d]", packet->player_id); + + char queryStr[QUERY_MAX_LEN]; + + //-------------------------------------------------------------- + + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT " + "id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime," + "gold,level,level_step,st,ht,dx,iq,exp," + "stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair," + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "part_acce, " + #endif + "skill_level,quickslot,skill_group,alignment,horse_level,horse_riding,horse_hp,horse_hp_droptime,horse_stamina," + "UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_play),horse_skill_point" +#ifdef ENABLE_CHEQUE_SYSTEM + ", cheque " +#endif + " FROM player%s WHERE id=%d", + GetTablePostfix(), packet->player_id); + + ClientHandleInfo * pkInfo = new ClientHandleInfo(dwHandle, packet->player_id); + pkInfo->account_id = packet->account_id; + CDBManager::instance().ReturnQuery(queryStr, QID_PLAYER, peer->GetHandle(), pkInfo); + + //-------------------------------------------------------------- + + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT id,`window`+0,pos,count,vnum,socket0,socket1,socket2,attrtype0,attrvalue0,attrtype1,attrvalue1,attrtype2,attrvalue2,attrtype3,attrvalue3,attrtype4,attrvalue4,attrtype5,attrvalue5,attrtype6,attrvalue6 " + "FROM item%s WHERE owner_id=%d AND (`window` in ('INVENTORY','EQUIPMENT','DRAGON_SOUL_INVENTORY','BELT_INVENTORY'))", + GetTablePostfix(), packet->player_id); + CDBManager::instance().ReturnQuery(queryStr, QID_ITEM, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id)); + + //-------------------------------------------------------------- + + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d", + GetTablePostfix(), packet->player_id); + CDBManager::instance().ReturnQuery(queryStr, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id,packet->account_id)); + + //-------------------------------------------------------------- + + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), packet->player_id); + CDBManager::instance().ReturnQuery(queryStr, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id)); + } +} + +void CClientManager::ItemAward(CPeer * peer,char* login) +{ + char login_t[LOGIN_MAX_LEN + 1] = ""; + strlcpy(login_t,login,LOGIN_MAX_LEN + 1); + std::set * pSet = ItemAwardManager::instance().GetByLogin(login_t); + if(pSet == NULL) + return; + typeof(pSet->begin()) it = pSet->begin(); + while(it != pSet->end() ) + { + TItemAward * pItemAward = *(it++); + char* whyStr = pItemAward->szWhy; + char cmdStr[100] = ""; + strcpy(cmdStr,whyStr); + char command[20] = ""; + // @fixme203 directly GetCommand instead of strcpy + GetCommand(cmdStr, command); + if( !(strcmp(command,"GIFT") )) + { + TPacketItemAwardInfromer giftData; + strcpy(giftData.login, pItemAward->szLogin); + strcpy(giftData.command, command); + giftData.vnum = pItemAward->dwVnum; + ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer)); + } + } +} +char* CClientManager::GetCommand(char* str, char* command) // @fixme203 +{ + char* tok; + + if( str[0] == '[' ) + { + tok = strtok(str,"]"); + strcat(command,&tok[1]); + } + + return command; +} + +bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab) +{ + if (mysql_num_rows(res) == 0) + return false; + + memset(pkTab, 0, sizeof(TPlayerTable)); + + MYSQL_ROW row = mysql_fetch_row(res); + + int col = 0; + + // "id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime," + // "gold,level,level_step,st,ht,dx,iq,exp," + // "stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair," + // "skill_level,quickslot,skill_group,alignment,horse_level,horse_riding,horse_hp,horse_stamina FROM player%s WHERE id=%d", + str_to_number(pkTab->id, row[col++]); + strlcpy(pkTab->name, row[col++], sizeof(pkTab->name)); + str_to_number(pkTab->job, row[col++]); + str_to_number(pkTab->voice, row[col++]); + str_to_number(pkTab->dir, row[col++]); + str_to_number(pkTab->x, row[col++]); + str_to_number(pkTab->y, row[col++]); + str_to_number(pkTab->z, row[col++]); + str_to_number(pkTab->lMapIndex, row[col++]); + str_to_number(pkTab->lExitX, row[col++]); + str_to_number(pkTab->lExitY, row[col++]); + str_to_number(pkTab->lExitMapIndex, row[col++]); + str_to_number(pkTab->hp, row[col++]); + str_to_number(pkTab->sp, row[col++]); + str_to_number(pkTab->stamina, row[col++]); + str_to_number(pkTab->sRandomHP, row[col++]); + str_to_number(pkTab->sRandomSP, row[col++]); + str_to_number(pkTab->playtime, row[col++]); + str_to_number(pkTab->gold, row[col++]); + str_to_number(pkTab->level, row[col++]); + str_to_number(pkTab->level_step, row[col++]); + str_to_number(pkTab->st, row[col++]); + str_to_number(pkTab->ht, row[col++]); + str_to_number(pkTab->dx, row[col++]); + str_to_number(pkTab->iq, row[col++]); + str_to_number(pkTab->exp, row[col++]); + str_to_number(pkTab->stat_point, row[col++]); + str_to_number(pkTab->skill_point, row[col++]); + str_to_number(pkTab->sub_skill_point, row[col++]); + str_to_number(pkTab->stat_reset_count, row[col++]); + str_to_number(pkTab->part_base, row[col++]); + str_to_number(pkTab->parts[PART_HAIR], row[col++]); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + str_to_number(pkTab->parts[PART_ACCE], row[col++]); +#endif + + if (row[col]) + thecore_memcpy(pkTab->skills, row[col], sizeof(pkTab->skills)); + else + memset(&pkTab->skills, 0, sizeof(pkTab->skills)); + + col++; + + if (row[col]) + thecore_memcpy(pkTab->quickslot, row[col], sizeof(pkTab->quickslot)); + else + memset(pkTab->quickslot, 0, sizeof(pkTab->quickslot)); + + col++; + + str_to_number(pkTab->skill_group, row[col++]); + str_to_number(pkTab->lAlignment, row[col++]); + + str_to_number(pkTab->horse.bLevel, row[col++]); + str_to_number(pkTab->horse.bRiding, row[col++]); + str_to_number(pkTab->horse.sHealth, row[col++]); + str_to_number(pkTab->horse.dwHorseHealthDropTime, row[col++]); + str_to_number(pkTab->horse.sStamina, row[col++]); + str_to_number(pkTab->logoff_interval, row[col++]); + str_to_number(pkTab->horse_skill_point, row[col++]); +#ifdef ENABLE_CHEQUE_SYSTEM + str_to_number(pkTab->cheque, row[col++]); +#endif + // reset sub_skill_point + { + pkTab->skills[123].bLevel = 0; // SKILL_CREATE + + if (pkTab->level > 9) + { + int max_point = pkTab->level - 9; + + int skill_point = + MIN(20, pkTab->skills[121].bLevel) + + MIN(20, pkTab->skills[124].bLevel) + + MIN(10, pkTab->skills[131].bLevel) + + MIN(20, pkTab->skills[141].bLevel) + + MIN(20, pkTab->skills[142].bLevel); + + pkTab->sub_skill_point = max_point - skill_point; + } + else + pkTab->sub_skill_point = 0; + } + + return true; +} + +void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID) +{ + CQueryInfo * qi = (CQueryInfo *) pMsg->pvUserData; + std::unique_ptr info((ClientHandleInfo *) qi->pvData); + + MYSQL_RES * pSQLResult = pMsg->Get()->pSQLResult; + if (!pSQLResult) + { + sys_err("null MYSQL_RES QID %u", dwQID); + return; + } + + switch (dwQID) + { + case QID_PLAYER: + sys_log(0, "QID_PLAYER %u %u", info->dwHandle, info->player_id); + RESULT_PLAYER_LOAD(peer, pSQLResult, info.get()); + + break; + + case QID_ITEM: + sys_log(0, "QID_ITEM %u", info->dwHandle); + RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + break; + + case QID_QUEST: + { + sys_log(0, "QID_QUEST %u", info->dwHandle); + RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + + ClientHandleInfo* temp1 = info.get(); + if (temp1 == NULL) + break; + + CLoginData* pLoginData1 = GetLoginDataByAID(temp1->account_id); + + if( pLoginData1->GetAccountRef().login == NULL) + break; + if( pLoginData1 == NULL ) + break; + sys_log(0,"info of pLoginData1 before call ItemAwardfunction %d",pLoginData1); + ItemAward(peer,pLoginData1->GetAccountRef().login); + } + break; + + case QID_AFFECT: + sys_log(0, "QID_AFFECT %u", info->dwHandle); + // @fixme402 RESULT_AFFECT_LOAD+info->player_id + RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + break; + /* + case QID_PLAYER_ITEM_QUEST_AFFECT: + sys_log(0, "QID_PLAYER_ITEM_QUEST_AFFECT %u", info->dwHandle); + RESULT_PLAYER_LOAD(peer, pSQLResult, info->dwHandle); + + if (!pMsg->Next()) + { + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: ITEM FAILED"); + return; + } + + case QID_ITEM_QUEST_AFFECT: + sys_log(0, "QID_ITEM_QUEST_AFFECT %u", info->dwHandle); + RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + + if (!pMsg->Next()) + { + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: QUEST FAILED"); + return; + } + + case QID_QUEST_AFFECT: + sys_log(0, "QID_QUEST_AFFECT %u", info->dwHandle); + RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle); + + if (!pMsg->Next()) + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: AFFECT FAILED"); + else + RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle); + + break; + */ + } +} + +void CClientManager::RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo) +{ + TPlayerTable tab; + + if (!CreatePlayerTableFromRes(pRes, &tab)) + { + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0); + return; + } + + CLoginData * pkLD = GetLoginDataByAID(pkInfo->account_id); + + if (!pkLD || pkLD->IsPlay()) + { + sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0); + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0); + return; + } + + pkLD->SetPlay(true); + SendLoginToBilling(pkLD, true); + thecore_memcpy(tab.aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(tab.aiPremiumTimes)); + + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_SUCCESS, pkInfo->dwHandle, sizeof(TPlayerTable)); + peer->Encode(tab); + + if (tab.id != pkLD->GetLastPlayerID()) + { + TPacketNeedLoginLogInfo logInfo; + logInfo.dwPlayerID = tab.id; + + pkLD->SetLastPlayerID( tab.id ); + + peer->EncodeHeader( HEADER_DG_NEED_LOGIN_LOG, pkInfo->dwHandle, sizeof(TPacketNeedLoginLogInfo) ); + peer->Encode(logInfo); + } +} + +void CClientManager::RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID) +{ + static std::vector s_items; + + CreateItemTableFromRes(pRes, &s_items, dwPID); + DWORD dwCount = s_items.size(); + + peer->EncodeHeader(HEADER_DG_ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount); + peer->EncodeDWORD(dwCount); + + CreateItemCacheSet(dwPID); + + // ITEM_LOAD_LOG_ATTACH_PID + sys_log(0, "ITEM_LOAD: count %u pid %u", dwCount, dwPID); + // END_OF_ITEM_LOAD_LOG_ATTACH_PID + + if (dwCount) + { + peer->Encode(s_items); + + for (DWORD i = 0; i < dwCount; ++i) + PutItemCache(&s_items[i], true); + } +} + +// @fixme402 (RESULT_AFFECT_LOAD +dwRealPID) +void CClientManager::RESULT_AFFECT_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwRealPID) +{ + int iNumRows; + if ((iNumRows = mysql_num_rows(pRes)) == 0) + { + // @fixme402 begin + static DWORD dwPID; + static DWORD dwCount = 0; + static std::vector emptyElements; + + dwPID = dwRealPID; + sys_log(0, "AFFECT_LOAD: count %u PID %u RealPID %u", dwCount, dwPID, dwRealPID); + + peer->EncodeHeader(HEADER_DG_AFFECT_LOAD, dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount); + peer->Encode(dwPID); + peer->Encode(dwCount); + peer->Encode(emptyElements); + // @fixme402 end + return; + } + + static std::vector s_elements; + s_elements.resize(iNumRows); + + DWORD dwPID = 0; + + MYSQL_ROW row; + + for (int i = 0; i < iNumRows; ++i) + { + TPacketAffectElement & r = s_elements[i]; + row = mysql_fetch_row(pRes); + + if (dwPID == 0) + str_to_number(dwPID, row[0]); + + str_to_number(r.dwType, row[1]); + str_to_number(r.bApplyOn, row[2]); + str_to_number(r.lApplyValue, row[3]); + str_to_number(r.dwFlag, row[4]); + str_to_number(r.lDuration, row[5]); + str_to_number(r.lSPCost, row[6]); + } + + DWORD dwCount = s_elements.size(); + sys_log(0, "AFFECT_LOAD: count %d PID %u", dwCount, dwPID); + + peer->EncodeHeader(HEADER_DG_AFFECT_LOAD, dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount); + peer->Encode(dwPID); + peer->Encode(dwCount); + peer->Encode(s_elements); +} + +void CClientManager::RESULT_QUEST_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD pid) +{ + int iNumRows; + + if ((iNumRows = mysql_num_rows(pRes)) == 0) + { + DWORD dwCount = 0; + peer->EncodeHeader(HEADER_DG_QUEST_LOAD, dwHandle, sizeof(DWORD)); + peer->Encode(dwCount); + return; + } + + static std::vector s_table; + s_table.resize(iNumRows); + + MYSQL_ROW row; + + for (int i = 0; i < iNumRows; ++i) + { + TQuestTable & r = s_table[i]; + + row = mysql_fetch_row(pRes); + + str_to_number(r.dwPID, row[0]); + strlcpy(r.szName, row[1], sizeof(r.szName)); + strlcpy(r.szState, row[2], sizeof(r.szState)); + str_to_number(r.lValue, row[3]); + } + + DWORD dwCount = s_table.size(); + sys_log(0, "QUEST_LOAD: count %d PID %u", dwCount, s_table[0].dwPID); + + peer->EncodeHeader(HEADER_DG_QUEST_LOAD, dwHandle, sizeof(DWORD) + sizeof(TQuestTable) * s_table.size()); + peer->Encode(dwCount); + peer->Encode(s_table); +} + +/* + * PLAYER SAVE + */ +void CClientManager::QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable * pkTab) +{ + if (g_test_server) + sys_log(0, "PLAYER_SAVE: %s", pkTab->name); + + PutPlayerCache(pkTab); +} + +typedef std::map time_by_id_map_t; +static time_by_id_map_t s_createTimeByAccountID; + +/* + * PLAYER CREATE + */ +void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet) +{ + char queryStr[QUERY_MAX_LEN]; + int queryLen; + int player_id; + + time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id); + + if (it != s_createTimeByAccountID.end()) + { + time_t curtime = time(0); + + if (curtime - it->second < 30) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + } + + queryLen = snprintf(queryStr, sizeof(queryStr), + "SELECT pid%u FROM player_index%s WHERE id=%d", packet->account_index + 1, GetTablePostfix(), packet->account_id); + + auto pMsg0(CDBManager::instance().DirectQuery(queryStr)); + if (pMsg0->Get()->uiNumRows != 0) + { + if (!pMsg0->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg0->Get()->pSQLResult); + + DWORD dwPID = 0; str_to_number(dwPID, row[0]); + if (row[0] && dwPID > 0) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + sys_log(0, "ALREADY EXIST AccountChrIdx %d ID %d", packet->account_index, dwPID); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + if (g_stLocale == "sjis") + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci", + GetTablePostfix(), packet->player_table.name); + else + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s'", GetTablePostfix(), packet->player_table.name); + + auto pMsg1(CDBManager::instance().DirectQuery(queryStr)); + if (pMsg1->Get()->uiNumRows) + { + if (!pMsg1->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg1->Get()->pSQLResult); + + if (*row[0] != '0') + { + sys_log(0, "ALREADY EXIST name %s, row[0] %s query %s", packet->player_table.name, row[0], queryStr); + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + queryLen = snprintf(queryStr, sizeof(queryStr), + "INSERT INTO player%s " + "(id, account_id, name, level, st, ht, dx, iq, " + "job, voice, dir, x, y, z, " + "hp, mp, random_hp, random_sp, stat_point, stamina, part_base, part_main, part_hair," + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "part_acce, " + #endif + " gold, playtime, " + "skill_level, quickslot) " + "VALUES(0, %u, '%s', %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, 0, " + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + "0, " + #endif + "%d, 0, ", + GetTablePostfix(), + packet->account_id, packet->player_table.name, packet->player_table.level, packet->player_table.st, packet->player_table.ht, packet->player_table.dx, packet->player_table.iq, + packet->player_table.job, packet->player_table.voice, packet->player_table.dir, packet->player_table.x, packet->player_table.y, packet->player_table.z, + packet->player_table.hp, packet->player_table.sp, packet->player_table.sRandomHP, packet->player_table.sRandomSP, packet->player_table.stat_point, packet->player_table.stamina, packet->player_table.part_base, packet->player_table.part_base, packet->player_table.gold); + + sys_log(0, "PlayerCreate accountid %d name %s level %d gold %d, st %d ht %d job %d", + packet->account_id, + packet->player_table.name, + packet->player_table.level, + packet->player_table.gold, + packet->player_table.st, + packet->player_table.ht, + packet->player_table.job); + + static char text[4096 + 1]; + + CDBManager::instance().EscapeString(text, packet->player_table.skills, sizeof(packet->player_table.skills)); + queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s', ", text); + if (g_test_server) + sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text); + + CDBManager::instance().EscapeString(text, packet->player_table.quickslot, sizeof(packet->player_table.quickslot)); + queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s')", text); + + auto pMsg2(CDBManager::instance().DirectQuery(queryStr)); + if (g_test_server) + sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text); + + if (pMsg2->Get()->uiAffectedRows <= 0) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + sys_log(0, "ALREADY EXIST3 query: %s AffectedRows %lu", queryStr, pMsg2->Get()->uiAffectedRows); + return; + } + + player_id = pMsg2->Get()->uiInsertID; + + snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%d=%d WHERE id=%d", + GetTablePostfix(), packet->account_index + 1, player_id, packet->account_id); + + auto pMsg3(CDBManager::instance().DirectQuery(queryStr)); + if (pMsg3->Get()->uiAffectedRows <= 0) + { + sys_err("QUERY_ERROR: %s", queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), player_id); + CDBManager::instance().DirectQuery(queryStr); + + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + TPacketDGCreateSuccess pack; + memset(&pack, 0, sizeof(pack)); + + pack.bAccountCharacterIndex = packet->account_index; + + pack.player.dwID = player_id; + strlcpy(pack.player.szName, packet->player_table.name, sizeof(pack.player.szName)); + pack.player.byJob = packet->player_table.job; + pack.player.byLevel = 1; + pack.player.dwPlayMinutes = 0; + pack.player.byST = packet->player_table.st; + pack.player.byHT = packet->player_table.ht; + pack.player.byDX = packet->player_table.dx; + pack.player.byIQ = packet->player_table.iq; + pack.player.wMainPart = packet->player_table.part_base; + pack.player.x = packet->player_table.x; + pack.player.y = packet->player_table.y; + + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_SUCCESS, dwHandle, sizeof(TPacketDGCreateSuccess)); + peer->Encode(pack); + + sys_log(0, "7 name %s job %d", pack.player.szName, pack.player.byJob); + + s_createTimeByAccountID[packet->account_id] = time(0); +} + +/* + * PLAYER DELETE + */ +void CClientManager::__QUERY_PLAYER_DELETE(CPeer* peer, DWORD dwHandle, TPlayerDeletePacket* packet) +{ + if (!packet->login[0] || !packet->player_id || packet->account_index >= PLAYER_PER_ACCOUNT) + return; + + CLoginData * ld = GetLoginDataByLogin(packet->login); + + if (!ld) + { + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + TAccountTable & r = ld->GetAccountRef(); + + // block for japan + if (g_stLocale != "sjis") + { + if (!IsChinaEventServer()) + { + if (strlen(r.social_id) < 7 || strncmp(packet->private_code, r.social_id + strlen(r.social_id) - 7, 7)) + { + sys_log(0, "PLAYER_DELETE FAILED len(%d)", strlen(r.social_id)); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + CPlayerTableCache * pkPlayerCache = GetPlayerCache(packet->player_id); + if (pkPlayerCache) + { + TPlayerTable * pTab = pkPlayerCache->Get(); + + if (pTab->level >= m_iPlayerDeleteLevelLimit) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimit); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + if (pTab->level < m_iPlayerDeleteLevelLimitLower) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimitLower); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + } + } + } + + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "SELECT p.id, p.level, p.name FROM player_index%s AS i, player%s AS p WHERE pid%u=%u AND pid%u=p.id", + GetTablePostfix(), GetTablePostfix(), packet->account_index + 1, packet->player_id, packet->account_index + 1); + + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle, packet->player_id); + pi->account_index = packet->account_index; + + sys_log(0, "PLAYER_DELETE TRY: %s %d pid%d", packet->login, packet->player_id, packet->account_index + 1); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_DELETE, peer->GetHandle(), pi); +} + +// + +// +void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; + + if (msg->Get() && msg->Get()->uiNumRows) + { + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + DWORD dwPID = 0; + str_to_number(dwPID, row[0]); + + int deletedLevelLimit = 0; + str_to_number(deletedLevelLimit, row[1]); + + char szName[64]; + strlcpy(szName, row[2], sizeof(szName)); + + if (deletedLevelLimit >= m_iPlayerDeleteLevelLimit && !IsChinaEventServer()) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimit); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + if (deletedLevelLimit < m_iPlayerDeleteLevelLimitLower) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimitLower); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + char queryStr[QUERY_MAX_LEN]; + + snprintf(queryStr, sizeof(queryStr), "INSERT INTO player%s_deleted SELECT * FROM player%s WHERE id=%d", + GetTablePostfix(), GetTablePostfix(), pi->player_id); + auto pIns(CDBManager::instance().DirectQuery(queryStr)); + + if (pIns->Get()->uiAffectedRows == 0 || pIns->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "PLAYER_DELETE FAILED %u CANNOT INSERT TO player%s_deleted", dwPID, GetTablePostfix()); + + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + sys_log(0, "PLAYER_DELETE SUCCESS %u", dwPID); + + char account_index_string[16]; + + snprintf(account_index_string, sizeof(account_index_string), "player_id%d", m_iPlayerIDStart + pi->account_index); + + CPlayerTableCache * pkPlayerCache = GetPlayerCache(pi->player_id); + + if (pkPlayerCache) + { + m_map_playerCache.erase(pi->player_id); + delete pkPlayerCache; + } + + TItemCacheSet * pSet = GetItemCacheSet(pi->player_id); + + if (pSet) + { + TItemCacheSet::iterator it = pSet->begin(); + + while (it != pSet->end()) + { + CItemCache * pkItemCache = *it++; + DeleteItemCache(pkItemCache->Get()->id); + } + + pSet->clear(); + delete pSet; + + m_map_pkItemCacheSetPtr.erase(pi->player_id); + } + + snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%u=0 WHERE pid%u=%d", + GetTablePostfix(), + pi->account_index + 1, + pi->account_index + 1, + pi->player_id); + + auto pMsg(CDBManager::instance().DirectQuery(queryStr)); + if (pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "PLAYER_DELETE FAIL WHEN UPDATE account table"); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().DirectQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM item%s WHERE owner_id=%d AND (`window` in ('INVENTORY','EQUIPMENT','DRAGON_SOUL_INVENTORY','BELT_INVENTORY'))", GetTablePostfix(), pi->player_id); + CDBManager::instance().DirectQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM quest%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM affect%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM guild_member%s WHERE pid=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + // MYSHOP_PRICE_LIST + snprintf(queryStr, sizeof(queryStr), "DELETE FROM myshop_pricelist%s WHERE owner_id=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + // END_OF_MYSHOP_PRICE_LIST + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM messenger_list%s WHERE account='%s' OR companion='%s'", GetTablePostfix(), szName, szName); + CDBManager::instance().AsyncQuery(queryStr); + + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_SUCCESS, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + } + else + { + sys_log(0, "PLAYER_DELETE FAIL NO ROW"); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + } +} + +void CClientManager::QUERY_ADD_AFFECT(CPeer * peer, TPacketGDAddAffect * p) +{ + char queryStr[QUERY_MAX_LEN]; + /* + snprintf(queryStr, sizeof(queryStr), + "INSERT INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) " + "VALUES(%u, %u, %u, %d, %u, %d, %d) " + "ON DUPLICATE KEY UPDATE lApplyValue=%d, dwFlag=%u, lDuration=%d, lSPCost=%d", + GetTablePostfix(), + p->dwPID, + p->elem.dwType, + p->elem.bApplyOn, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost); + */ + snprintf(queryStr, sizeof(queryStr), + "REPLACE INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) " + "VALUES(%u, %u, %u, %ld, %u, %ld, %ld)", + GetTablePostfix(), + p->dwPID, + p->elem.dwType, + p->elem.bApplyOn, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost); + + CDBManager::instance().AsyncQuery(queryStr); +} + +void CClientManager::QUERY_REMOVE_AFFECT(CPeer * peer, TPacketGDRemoveAffect * p) +{ + char queryStr[QUERY_MAX_LEN]; + + snprintf(queryStr, sizeof(queryStr), + "DELETE FROM affect%s WHERE dwPID=%u AND bType=%u AND bApplyOn=%u", + GetTablePostfix(), p->dwPID, p->dwType, p->bApplyOn); + + CDBManager::instance().AsyncQuery(queryStr); +} + +void CClientManager::InsertLogoutPlayer(DWORD pid) +{ + TLogoutPlayerMap::iterator it = m_map_logout.find(pid); + + if (it != m_map_logout.end()) + { + if (g_log) + sys_log(0, "LOGOUT: Update player time pid(%d)", pid); + + it->second->time = time(0); + return; + } + + TLogoutPlayer * pLogout = new TLogoutPlayer; + pLogout->pid = pid; + pLogout->time = time(0); + m_map_logout.emplace(pid, pLogout); + + if (g_log) + sys_log(0, "LOGOUT: Insert player pid(%d)", pid); +} + +void CClientManager::DeleteLogoutPlayer(DWORD pid) +{ + TLogoutPlayerMap::iterator it = m_map_logout.find(pid); + + if (it != m_map_logout.end()) + { + delete it->second; + m_map_logout.erase(it); + } +} + +extern int g_iLogoutSeconds; + +void CClientManager::UpdateLogoutPlayer() +{ + time_t now = time(0); + + TLogoutPlayerMap::iterator it = m_map_logout.begin(); + + while (it != m_map_logout.end()) + { + TLogoutPlayer* pLogout = it->second; + + if (now - g_iLogoutSeconds > pLogout->time) + { + FlushItemCacheSet(pLogout->pid); + FlushPlayerCacheSet(pLogout->pid); + + delete pLogout; + m_map_logout.erase(it++); + } + else + ++it; + } +} + +void CClientManager::FlushPlayerCacheSet(DWORD pid) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find(pid); + + if (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = it->second; + m_map_playerCache.erase(it); + + c->Flush(); + delete c; + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Config.cpp b/source-server/Srcs/Server/db/src/Config.cpp new file mode 100644 index 000000000..835eea746 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Config.cpp @@ -0,0 +1,246 @@ +#include "stdafx.h" +#include "Config.h" + +CConfig::CConfig() +{ +} + +CConfig::~CConfig() +{ + Destroy(); +} + +void CConfig::Destroy() +{ + m_valueMap.clear(); +} + +void CConfig::NextLine(FILE *fp) +{ + int c; + + while ((c = fgetc(fp)) != EOF) + { + if (c == '\n') + return; + } +} + +bool CConfig::GetWord(FILE *fp, char *tar) +{ + int i = 0; + int c; + + int semicolon_mode = 0; + + while ((c = fgetc(fp)) != EOF) + { + if (c == 13) + continue; + + if (semicolon_mode) + { + if (c == '"') + { + tar[i] = '\0'; + return true; + } + + tar[i++] = c; + continue; + } + else + { + if (i == 0) + { + if (c == '"') + { + semicolon_mode = 1; + continue; + } + + if (c == ' ' || c == '\t' || c == '\n') + { + continue; + } + } + + if ((c == ' ' || c == '\t' || c == '\n')) + { + tar[i] = '\0'; + return true; + } + + tar[i] = c; + ++i; + } + } + + return (i != 0); +} + +bool CConfig::GetLine(FILE* fp, char*dest) +{ + int c; + int i = 0; + while ((c = fgetc(fp)) != EOF) + { + if (c == '\n') + return true; + dest[i] = c; + ++i; + } + return true; +} + +bool CConfig::LoadFile(const char* filename) +{ + char szTmp[256]; + char comment[256]; + + FILE * fp = fopen(filename, "rb"); + + if (fp == NULL) + return false; + + int mode = 0; + + while (GetWord(fp, szTmp)) + { + if (strcmp(szTmp, "//") == 0) + { + NextLine(fp); + continue; + } + + switch (mode) + { + case 0: + strlcpy(comment, szTmp, sizeof(comment)); + ++mode; + break; + + case 1: + if (*szTmp == '=') + ++mode; + break; + + case 2: + mode = 0; + m_valueMap.emplace(comment, szTmp); + break; + } + + // ITEM_ID_RANGE + if (mode == 2 && strcmp(comment, "ITEM_ID_RANGE") == 0) + { + GetLine(fp, szTmp); + m_valueMap.emplace(comment, szTmp); + mode = 0; + + } + // ITEM_ID_RANGE_END + } + + fclose(fp); + return true; +} + +std::string * CConfig::Search(const char* key) +{ + itertype(m_valueMap) i = m_valueMap.find(key); + + if (i == m_valueMap.end()) + return NULL; + else + return (&i->second); +} + +bool CConfig::GetParam(const char*key, int index, DWORD *Param) +{ + std::string * pstStr = Search(key); + if (!pstStr) + return false; + + char szParam[5][32]; + + sscanf(pstStr->c_str(), "%s %s %s %s %s", szParam[0],szParam[1],szParam[2],szParam[3],szParam[4]); + + str_to_number(*Param, szParam[index]); + + sys_log(0, "GetParam %d", *Param); + return true; +} +const char * CConfig::Get(const char* key) +{ + std::string * pstStr = Search(key); + + if (!pstStr) + { + static std::string stTemp = ""; + return stTemp.c_str(); + } + + return pstStr->c_str(); +} + +bool CConfig::GetValue(const char * key, int* dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, float *dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, DWORD *dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, BYTE *dest) +{ + if (!Search(key)) + return false; + + *dest = *(BYTE *) Get(key); + return true; +} + +bool CConfig::GetValue(const char * key, char *dest, size_t destSize) +{ + if (!Search(key)) + return false; + + strlcpy(dest, Get(key), destSize); + + if (!*dest) + return false; + + return true; +} + +bool CConfig::GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2) +{ + if (!GetParam(key, 0, dest1)) + return false; + + if (!GetParam(key, 1, dest2)) + return false; + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Config.h b/source-server/Srcs/Server/db/src/Config.h new file mode 100644 index 000000000..abcd3a735 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Config.h @@ -0,0 +1,35 @@ +#ifndef __INC_CONFIG_H__ +#define __INC_CONFIG_H__ + +typedef std::map TValueMap; + +class CConfig : public singleton +{ + public: + CConfig(); + ~CConfig(); + + bool LoadFile(const char* filename); + bool GetValue(const char* key, int* dest); + bool GetValue(const char* key, float* dest); + bool GetValue(const char* key, DWORD* dest); + bool GetValue(const char* key, BYTE* dest); + bool GetValue(const char* key, char* dest, size_t destSize); + bool GetWord(FILE* fp, char* dest); + bool GetLine(FILE* fp, char* dest); + bool GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2); + void NextLine(FILE* fp); + + private: + void Destroy(); + bool GetParam(const char*key,int index, DWORD *Param); + + const char * Get(const char* key); + std::string * Search(const char* key); + + private: + TValueMap m_valueMap; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/CsvReader.cpp b/source-server/Srcs/Server/db/src/CsvReader.cpp new file mode 100644 index 000000000..9b8911469 --- /dev/null +++ b/source-server/Srcs/Server/db/src/CsvReader.cpp @@ -0,0 +1,372 @@ +#include "stdafx.h" +#include "CsvReader.h" +#include +#include + +#ifndef Assert + #include + #define Assert assert + #define LogToFile (void)(0); +#endif + +namespace +{ + enum ParseState + { + STATE_NORMAL = 0, + STATE_QUOTE + }; + + std::string Trim(std::string str) + { + str = str.erase(str.find_last_not_of(" \t\r\n") + 1); + str = str.erase(0, str.find_first_not_of(" \t\r\n")); + return str; + } + + std::string Lower(std::string original) + { + std::transform(original.begin(), original.end(), original.begin(), tolower); + return original; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +void cCsvAlias::AddAlias(const char* name, size_t index) +{ + std::string converted(Lower(name)); + + Assert(m_Name2Index.find(converted) == m_Name2Index.end()); + Assert(m_Index2Name.find(index) == m_Index2Name.end()); + + m_Name2Index.emplace(converted, index); + m_Index2Name.emplace(index, name); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +void cCsvAlias::Destroy() +{ + m_Name2Index.clear(); + m_Index2Name.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +const char* cCsvAlias::operator [] (size_t index) const +{ + INDEX2NAME_MAP::const_iterator itr(m_Index2Name.find(index)); + if (itr == m_Index2Name.end()) + { + //LogToFile(NULL, "cannot find suitable conversion for %d", index); + Assert(false && "cannot find suitable conversion"); + return NULL; + } + + return itr->second.c_str(); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +size_t cCsvAlias::operator [] (const char* name) const +{ + NAME2INDEX_MAP::const_iterator itr(m_Name2Index.find(Lower(name))); + if (itr == m_Name2Index.end()) + { + //LogToFile(NULL, "cannot find suitable conversion for %s", name); + Assert(false && "cannot find suitable conversion"); + return 0; + } + + return itr->second; +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +bool cCsvFile::Load(const char* fileName, const char seperator, const char quote) +{ + Assert(seperator != quote); + + std::ifstream file(fileName, std::ios::in); + if (!file) return false; + + Destroy(); + + cCsvRow* row = NULL; + ParseState state = STATE_NORMAL; + std::string token = ""; + char buf[2048+1] = {0,}; + + while (file.good()) + { + file.getline(buf, 2048); + buf[sizeof(buf)-1] = 0; + + std::string line(Trim(buf)); + if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue; + + std::string text = std::string(line) + " "; + size_t cur = 0; + + while (cur < text.size()) + { + if (state == STATE_QUOTE) + { + if (text[cur] == quote) + { + if (text[cur+1] == quote) + { + token += quote; + ++cur; + } + + else + { + state = STATE_NORMAL; + } + } + else + { + token += text[cur]; + } + } + + else if (state == STATE_NORMAL) + { + if (row == NULL) + row = new cCsvRow(); + + if (text[cur] == seperator) + { + row->emplace_back(token); + token.clear(); + } + + else if (text[cur] == quote) + { + state = STATE_QUOTE; + } + + else + { + token += text[cur]; + } + } + + ++cur; + } + + if (state == STATE_NORMAL) + { + Assert(row != NULL); + row->emplace_back(token.substr(0, token.size()-2)); + m_Rows.emplace_back(row); + token.clear(); + row = NULL; + } + else + { + token = token.substr(0, token.size()-2) + "\r\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quote) const +{ + Assert(seperator != quote); + + std::ofstream file; + if (append) { file.open(fileName, std::ios::out | std::ios::app); } + else { file.open(fileName, std::ios::out | std::ios::trunc); } + + if (!file) return false; + + char special_chars[5] = { seperator, quote, '\r', '\n', 0 }; + char quote_escape_string[3] = { quote, quote, 0 }; + + for (size_t i=0; isize(); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +int cCsvTable::AsInt(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + if (index >= row->size()) + sys_err("file %s index %d >= row size %d of row %s", m_fileName.c_str(), index, row->size(), row->AsString(0)); + Assert(index < row->size()); + return row->AsInt(index); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +double cCsvTable::AsDouble(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + if (index >= row->size()) + sys_err("file %s index %d >= row size %d of row %s", m_fileName.c_str(), index, row->size(), row->AsString(0)); + Assert(index < row->size()); + return row->AsDouble(index); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +const char* cCsvTable::AsStringByIndex(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + if (index >= row->size()) + sys_err("file %s index %d >= row size %d of row %s", m_fileName.c_str(), index, row->size(), row->AsString(0)); + Assert(index < row->size()); + return row->AsString(index); +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +void cCsvTable::Destroy() +{ + m_File.Destroy(); + m_Alias.Destroy(); + m_CurRow = -1; +} + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +const cCsvRow* const cCsvTable::CurRow() const +{ + if (m_CurRow < 0) + { + Assert(false && "call Next() first!"); + return NULL; + } + else if (m_CurRow >= (int)m_File.GetRowCount()) + { + Assert(false && "no more rows!"); + return NULL; + } + + return m_File[m_CurRow]; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/CsvReader.h b/source-server/Srcs/Server/db/src/CsvReader.h new file mode 100644 index 000000000..3de76b6d3 --- /dev/null +++ b/source-server/Srcs/Server/db/src/CsvReader.h @@ -0,0 +1,253 @@ +#ifndef __CSVFILE_H__ +#define __CSVFILE_H__ + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvAlias + +/// + +/// +///
+/// int a = row.AsInt(0);
+/// int b = row.AsInt(1);
+/// 
+/// + +/// +///
+/// int a = row.AsInt(0);
+/// int c = row.AsInt(1);
+
+/// 
+/// + +//////////////////////////////////////////////////////////////////////////////// + +class cCsvAlias +{ +private: + typedef std::map NAME2INDEX_MAP; + typedef std::map INDEX2NAME_MAP; + + NAME2INDEX_MAP m_Name2Index; + INDEX2NAME_MAP m_Index2Name; + +public: + + cCsvAlias() {} + + virtual ~cCsvAlias() {} + +public: + + void AddAlias(const char* name, size_t index); + + void Destroy(); + + const char* operator [] (size_t index) const; + + size_t operator [] (const char* name) const; + +private: + + cCsvAlias(const cCsvAlias&) {} + + const cCsvAlias& operator = (const cCsvAlias&) { return *this; } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvRow + +/// + +/// +///
+
+/// ---------------------+----------------------------------------------------
+/// ItemPrice            | ItemPrice
+/// Item,Price           | "Item,Price"
+/// Item"Price           | "Item""Price"
+/// "ItemPrice"          | """ItemPrice"""
+/// "Item,Price"         | """Item,Price"""
+/// Item",Price          | "Item"",Price"
+/// 
+/// + +/// +/// \sa cCsvFile +//////////////////////////////////////////////////////////////////////////////// + +class cCsvRow : public std::vector +{ +public: + + cCsvRow() {} + + ~cCsvRow() {} + +public: + + int AsInt(size_t index) const { return atoi(at(index).c_str()); } + + double AsDouble(size_t index) const { return atof(at(index).c_str()); } + + const char* AsString(size_t index) const { return at(index).c_str(); } + + int AsInt(const char* name, const cCsvAlias& alias) const { + return atoi( at(alias[name]).c_str() ); + } + + double AsDouble(const char* name, const cCsvAlias& alias) const { + return atof( at(alias[name]).c_str() ); + } + + const char* AsString(const char* name, const cCsvAlias& alias) const { + return at(alias[name]).c_str(); + } + + cCsvRow(const cCsvRow&) {} + +private: + + const cCsvRow& operator = (const cCsvRow&) { return *this; } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvFile +/// +/// sample +///
+/// cCsvFile file;
+///
+/// cCsvRow row1, row2, row3;
+/// row1.emplace_back("ItemPrice");
+/// row1.emplace_back("Item,Price");
+/// row1.emplace_back("Item\"Price");
+///
+/// row2.reserve(3);
+/// row2[0] = "\"ItemPrice\"";
+/// row2[1] = "\"Item,Price\"";
+/// row2[2] = "Item\",Price\"";
+///
+/// row3 = "\"ItemPrice\"\"Item,Price\"Item\",Price\"";
+///
+/// file.add(row1);
+/// file.add(row2);
+/// file.add(row3);
+/// file.save("test.csv", false);
+/// 
+/// + +//////////////////////////////////////////////////////////////////////////////// + +class cCsvFile +{ +private: + typedef std::vector ROWS; + + ROWS m_Rows; + +public: + + cCsvFile() {} + + virtual ~cCsvFile() { Destroy(); } + +public: + + bool Load(const char* fileName, const char seperator=',', const char quote='"'); + + bool Save(const char* fileName, bool append=false, char seperator=',', char quote='"') const; + + void Destroy(); + + cCsvRow* operator [] (size_t index); + + const cCsvRow* operator [] (size_t index) const; + + size_t GetRowCount() const { return m_Rows.size(); } + +private: + + cCsvFile(const cCsvFile&) {} + + const cCsvFile& operator = (const cCsvFile&) { return *this; } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvTable + +/// + +/// +/// sample +///
+/// cCsvTable table;
+///
+/// table.alias(0, "ItemClass");
+/// table.alias(1, "ItemType");
+///
+/// if (table.load("test.csv"))
+/// {
+///     while (table.next())
+///     {
+///         std::string item_class = table.AsString("ItemClass");
+///         int         item_type  = table.AsInt("ItemType");
+///     }
+/// }
+/// 
+//////////////////////////////////////////////////////////////////////////////// + +class cCsvTable +{ +public : + cCsvFile m_File; + std::string m_fileName; +private: + cCsvAlias m_Alias; + int m_CurRow; + +public: + + cCsvTable(); + + virtual ~cCsvTable(); + +public: + + bool Load(const char* fileName, const char seperator=',', const char quote='"'); + + void AddAlias(const char* name, size_t index) { m_Alias.AddAlias(name, index); } + + bool Next(); + + size_t ColCount() const; + + int AsInt(size_t index) const; + + double AsDouble(size_t index) const; + + const char* AsStringByIndex(size_t index) const; + + int AsInt(const char* name) const { return AsInt(m_Alias[name]); } + + double AsDouble(const char* name) const { return AsDouble(m_Alias[name]); } + + const char* AsString(const char* name) const { return AsStringByIndex(m_Alias[name]); } + + void Destroy(); + +private: + + const cCsvRow* const CurRow() const; + + cCsvTable(const cCsvTable&) {} + + const cCsvTable& operator = (const cCsvTable&) { return *this; } +}; + +#endif //__CSVFILE_H__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/DBManager.cpp b/source-server/Srcs/Server/db/src/DBManager.cpp new file mode 100644 index 000000000..40dd92b28 --- /dev/null +++ b/source-server/Srcs/Server/db/src/DBManager.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "DBManager.h" +#include "ClientManager.h" + +extern std::string g_stLocale; + +CDBManager::CDBManager() +{ + Initialize(); +} + +CDBManager::~CDBManager() +{ + Destroy(); +} + +void CDBManager::Initialize() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + m_mainSQL[i].release(); + m_directSQL[i].release(); + m_asyncSQL[i].release(); + } +} + +void CDBManager::Destroy() +{ + Clear(); +} + +void CDBManager::Clear() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + m_mainSQL[i].release(); + m_directSQL[i].release(); + m_asyncSQL[i].release(); + } + + Initialize(); +} + +void CDBManager::Quit() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + if (m_mainSQL[i]) + m_mainSQL[i]->Quit(); + + if (m_asyncSQL[i]) + m_asyncSQL[i]->Quit(); + + if (m_directSQL[i]) + m_directSQL[i]->Quit(); + } +} + +SQLMsg * CDBManager::PopResult() +{ + SQLMsg * p; + + for (int i = 0; i < SQL_MAX_NUM; ++i) + if (m_mainSQL[i] && m_mainSQL[i]->PopResult(&p)) + return p; + + return NULL; +} + +SQLMsg * CDBManager::PopResult(eSQL_SLOT slot) +{ + SQLMsg * p; + + if (m_mainSQL[slot] && m_mainSQL[slot]->PopResult(&p)) + return p; + + return NULL; +} +int CDBManager::Connect(int iSlot, const char * db_address, const int db_port, const char * db_name, const char * user, const char * pwd) +{ + if (db_address == NULL || db_name == NULL) + return false; + + if (iSlot < 0 || iSlot >= SQL_MAX_NUM) + return false; + + sys_log(0, "CREATING DIRECT_SQL"); + m_directSQL[iSlot] = std::make_unique(); + if (!m_directSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), true, db_port)) + { + Clear(); + return false; + } + + sys_log(0, "CREATING MAIN_SQL"); + m_mainSQL[iSlot] = std::make_unique(); + if (!m_mainSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port)) + { + Clear(); + return false; + } + + sys_log(0, "CREATING ASYNC_SQL"); + m_asyncSQL[iSlot] = std::make_unique(); + if (!m_asyncSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port)) + { + Clear(); + return false; + } + + return true; +} + +std::unique_ptr CDBManager::DirectQuery(const char * c_pszQuery, int iSlot) +{ + return m_directSQL[iSlot]->DirectQuery(c_pszQuery); +} + +extern CPacketInfo g_query_info; +extern int g_query_count[2]; + +void CDBManager::ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * udata, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + //sys_log(0, "ReturnQuery %s", c_pszQuery); + CQueryInfo * p = new CQueryInfo; + + p->iType = iType; + p->dwIdent = dwIdent; + p->pvData = udata; + + m_mainSQL[iSlot]->ReturnQuery(c_pszQuery, p); + + //g_query_info.Add(iType); + ++g_query_count[0]; +} + +void CDBManager::AsyncQuery(const char * c_pszQuery, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + m_asyncSQL[iSlot]->AsyncQuery(c_pszQuery); + ++g_query_count[1]; +} + +unsigned long CDBManager::EscapeString(void *to, const void *from, unsigned long length, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + return mysql_real_escape_string(m_directSQL[iSlot]->GetSQLHandle(), (char *) to, (const char *) from, length); +} + +void CDBManager::SetLocale(const char * szLocale) +{ + const std::string stLocale(szLocale); + sys_log(0, "SetLocale start %s", szLocale); + for (int n = 0; n < SQL_MAX_NUM; ++n) + { + m_mainSQL[n]->SetLocale(stLocale); + m_directSQL[n]->SetLocale(stLocale); + m_asyncSQL[n]->SetLocale(stLocale); + } + sys_log(0, "SetLocale end %s", szLocale); +} + +void CDBManager::QueryLocaleSet() +{ + for (int n = 0; n < SQL_MAX_NUM; ++n) + { + m_mainSQL[n]->QueryLocaleSet(); + m_directSQL[n]->QueryLocaleSet(); + m_asyncSQL[n]->QueryLocaleSet(); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/DBManager.h b/source-server/Srcs/Server/db/src/DBManager.h new file mode 100644 index 000000000..0cec9e482 --- /dev/null +++ b/source-server/Srcs/Server/db/src/DBManager.h @@ -0,0 +1,100 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN2_DB_DBMANAGER_H__ +#define __INC_METIN2_DB_DBMANAGER_H__ + +#include + +#include "../../libsql/AsyncSQL.h" +#include + +#define SQL_SAFE_LENGTH(size) (size * 2 + 1) +#define QUERY_SAFE_LENGTH(size) (1024 + SQL_SAFE_LENGTH(size)) + +class CQueryInfo +{ + public: + int iType; + DWORD dwIdent; + void * pvData; +}; + +enum eSQL_SLOT +{ + SQL_PLAYER, + SQL_ACCOUNT, + SQL_COMMON, + SQL_HOTBACKUP, +#ifdef ENABLE_DB_SQL_LOG + SQL_LOG, +#endif + SQL_MAX_NUM, +}; + +class CDBManager : public singleton +{ + protected: + void Initialize(); + void Destroy(); + + public: + CDBManager(); + virtual ~CDBManager(); + + void Clear(); + void Quit(); + + int Connect(int iSlot, const char * host, int port, const char* dbname, const char* user, const char* pass); + + void ReturnQuery(const char * c_pszQuery, int iType, DWORD dwIdent, void * pvData, int iSlot = SQL_PLAYER); + void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); + std::unique_ptr DirectQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); + + SQLMsg * PopResult(); + SQLMsg * PopResult(eSQL_SLOT slot ); + + unsigned long EscapeString(void * to, const void * from, unsigned long length, int iSlot = SQL_PLAYER); + + DWORD CountReturnQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQuery() : 0; } + DWORD CountReturnResult(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountResult() : 0; } + DWORD CountReturnQueryFinished(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQueryFinished() : 0; } + DWORD CountReturnCopiedQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->GetCopiedQueryCount() : 0; } + + DWORD CountAsyncQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQuery() : 0; } + DWORD CountAsyncResult(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountResult() : 0; } + DWORD CountAsyncQueryFinished(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQueryFinished() : 0; } + DWORD CountAsyncCopiedQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->GetCopiedQueryCount() : 0; } + + void ResetCounter() + { + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + if (m_mainSQL[i]) + { + m_mainSQL[i]->ResetQueryFinished(); + m_mainSQL[i]->ResetCopiedQueryCount(); + } + + if (m_asyncSQL[i]) + { + m_asyncSQL[i]->ResetQueryFinished(); + m_asyncSQL[i]->ResetCopiedQueryCount(); + } + } + } + + private: + std::unique_ptr m_mainSQL[SQL_MAX_NUM]; + std::unique_ptr m_directSQL[SQL_MAX_NUM]; + std::unique_ptr m_asyncSQL[SQL_MAX_NUM]; + + //CHARSET + public: + void SetLocale(const char * szLocale ); + void QueryLocaleSet(); + private: + + //END_CHARSET +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/GuildManager.cpp b/source-server/Srcs/Server/db/src/GuildManager.cpp new file mode 100644 index 000000000..71596b454 --- /dev/null +++ b/source-server/Srcs/Server/db/src/GuildManager.cpp @@ -0,0 +1,1475 @@ +#include "stdafx.h" +#include "GuildManager.h" +#include "Main.h" +#include "ClientManager.h" +#include "QID.h" +#include "Config.h" +#include + +extern std::string g_stLocale; + +const int GUILD_RANK_MAX_NUM = 20; + +bool isEurope() +{ + do + { + if (g_stLocale.compare("germany") == 0) break; + if (g_stLocale.compare("france") == 0) break; + if (g_stLocale.compare("italy") == 0) break; + if (g_stLocale.compare("spain") == 0) break; + if (g_stLocale.compare("uk") == 0) break; + if (g_stLocale.compare("turkey") == 0) break; + if (g_stLocale.compare("poland") == 0) break; + if (g_stLocale.compare("portugal") == 0) break; + if (g_stLocale.compare("greek") == 0) break; + + return false; + } while (false); + + return true; +} + +DWORD GetGuildWarWaitStartDuration() +{ + // const int GUILD_WAR_WAIT_START_DURATION = 60; + // const int GUILD_WAR_WAIT_START_DURATION = 5; + + if (isEurope() == true) return 60; + else return 5; +} + +DWORD GetGuildWarReserveSeconds() +{ + // const int GUILD_WAR_RESERVE_SECONDS = 180; + // const int GUILD_WAR_RESERVE_SECONDS = 10; + + if (isEurope() == true) return 180; + else return 10; +} + +namespace +{ + struct FSendPeerWar + { + FSendPeerWar(BYTE bType, BYTE bWar, DWORD GID1, DWORD GID2) + { + if (number(0, 1)) + std::swap(GID1, GID2); + + memset(&p, 0, sizeof(TPacketGuildWar)); + + p.bWar = bWar; + p.bType = bType; + p.dwGuildFrom = GID1; + p.dwGuildTo = GID2; + } + + void operator() (CPeer* peer) + { + if (peer->GetChannel() == 0) + return; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR, 0, sizeof(TPacketGuildWar)); + peer->Encode(&p, sizeof(TPacketGuildWar)); + } + + TPacketGuildWar p; + }; + + struct FSendGuildWarScore + { + FSendGuildWarScore(DWORD guild_gain, DWORD dwOppGID, int iScore, int iBetScore) + { + pck.dwGuildGainPoint = guild_gain; + pck.dwGuildOpponent = dwOppGID; + pck.lScore = iScore; + pck.lBetScore = iBetScore; + } + + void operator() (CPeer* peer) + { + if (peer->GetChannel() == 0) + return; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_SCORE, 0, sizeof(pck)); + peer->Encode(&pck, sizeof(pck)); + } + + TPacketGuildWarScore pck; + }; +} + +CGuildManager::CGuildManager() +{ +} + +CGuildManager::~CGuildManager() +{ + while (!m_pqOnWar.empty()) + { + if (!m_pqOnWar.top().second->bEnd) + delete m_pqOnWar.top().second; + + m_pqOnWar.pop(); + } +} + +TGuild & CGuildManager::TouchGuild(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it != m_map_kGuild.end()) + return it->second; + + TGuild info; + m_map_kGuild.emplace(GID, info); + return m_map_kGuild[GID]; +} + +void CGuildManager::ParseResult(SQLResult * pRes) +{ + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + DWORD GID = strtoul(row[0], NULL, 10); + + TGuild & r_info = TouchGuild(GID); + + strlcpy(r_info.szName, row[1], sizeof(r_info.szName)); + str_to_number(r_info.ladder_point, row[2]); + str_to_number(r_info.win, row[3]); + str_to_number(r_info.draw, row[4]); + str_to_number(r_info.loss, row[5]); + str_to_number(r_info.gold, row[6]); + str_to_number(r_info.level, row[7]); + + sys_log(0, + "GuildWar: %-24s ladder %-5d win %-3d draw %-3d loss %-3d", + r_info.szName, + r_info.ladder_point, + r_info.win, + r_info.draw, + r_info.loss); + } +} + +void CGuildManager::Initialize() +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "SELECT id, name, ladder_point, win, draw, loss, gold, level FROM guild%s", GetTablePostfix()); + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiNumRows) + ParseResult(pmsg->Get()); + + char str[128 + 1]; + + if (!CConfig::instance().GetValue("POLY_POWER", str, sizeof(str))) + *str = '\0'; + + if (!polyPower.Analyze(str)) + sys_err("cannot set power poly: %s", str); + else + sys_log(0, "POWER_POLY: %s", str); + + if (!CConfig::instance().GetValue("POLY_HANDICAP", str, sizeof(str))) + *str = '\0'; + + if (!polyHandicap.Analyze(str)) + sys_err("cannot set handicap poly: %s", str); + else + sys_log(0, "HANDICAP_POLY: %s", str); + + QueryRanking(); +} + +void CGuildManager::Load(DWORD dwGuildID) +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "SELECT id, name, ladder_point, win, draw, loss, gold, level FROM guild%s WHERE id=%u", GetTablePostfix(), dwGuildID); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + if (pmsg->Get()->uiNumRows) + ParseResult(pmsg->Get()); +} + +void CGuildManager::QueryRanking() +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT id,name,ladder_point FROM guild%s ORDER BY ladder_point DESC LIMIT 20", GetTablePostfix()); + + CDBManager::instance().ReturnQuery(szQuery, QID_GUILD_RANKING, 0, 0); +} + +int CGuildManager::GetRanking(DWORD dwGID) +{ + itertype(map_kLadderPointRankingByGID) it = map_kLadderPointRankingByGID.find(dwGID); + + if (it == map_kLadderPointRankingByGID.end()) + return GUILD_RANK_MAX_NUM; + + return MINMAX(0, it->second, GUILD_RANK_MAX_NUM); +} + +void CGuildManager::ResultRanking(MYSQL_RES * pRes) +{ + if (!pRes) + return; + + int iLastLadderPoint = -1; + int iRank = 0; + + map_kLadderPointRankingByGID.clear(); + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes))) + { + DWORD dwGID = 0; str_to_number(dwGID, row[0]); + int iLadderPoint = 0; str_to_number(iLadderPoint, row[2]); + + if (iLadderPoint != iLastLadderPoint) + ++iRank; + + sys_log(0, "GUILD_RANK: %-24s %2d %d", row[1], iRank, iLadderPoint); + + map_kLadderPointRankingByGID.emplace(dwGID, iRank); + } +} + +void CGuildManager::Update() +{ + ProcessReserveWar(); + + time_t now = CClientManager::instance().GetCurrentTime(); + + if (!m_pqOnWar.empty()) + { + // UNKNOWN_GUILD_MANAGE_UPDATE_LOG + /* + sys_log(0, "GuildManager::Update size %d now %d top %d, %s(%u) vs %s(%u)", + m_WarMap.size(), + now, + m_pqOnWar.top().first, + m_map_kGuild[m_pqOnWar.top().second->GID[0]].szName, + m_pqOnWar.top().second->GID[0], + m_map_kGuild[m_pqOnWar.top().second->GID[1]].szName, + m_pqOnWar.top().second->GID[1]); + */ + // END_OF_UNKNOWN_GUILD_MANAGE_UPDATE_LOG + + while (!m_pqOnWar.empty() && (m_pqOnWar.top().first <= now || (m_pqOnWar.top().second && m_pqOnWar.top().second->bEnd))) + { + TGuildWarPQElement * e = m_pqOnWar.top().second; + + m_pqOnWar.pop(); + + if (e) + { + if (!e->bEnd) + WarEnd(e->GID[0], e->GID[1], false); + + delete e; + } + } + } + + // GUILD_SKILL_COOLTIME_BUG_FIX + while (!m_pqSkill.empty() && m_pqSkill.top().first <= now) + { + const TGuildSkillUsed& s = m_pqSkill.top().second; + CClientManager::instance().SendGuildSkillUsable(s.GID, s.dwSkillVnum, true); + m_pqSkill.pop(); + } + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX + + while (!m_pqWaitStart.empty() && m_pqWaitStart.top().first <= now) + { + const TGuildWaitStartInfo & ws = m_pqWaitStart.top().second; + m_pqWaitStart.pop(); + + StartWar(ws.bType, ws.GID[0], ws.GID[1], ws.pkReserve); // insert new element to m_WarMap and m_pqOnWar + + if (ws.lInitialScore) + { + UpdateScore(ws.GID[0], ws.GID[1], ws.lInitialScore, 0); + UpdateScore(ws.GID[1], ws.GID[0], ws.lInitialScore, 0); + } + + TPacketGuildWar p; + + p.bType = ws.bType; + p.bWar = GUILD_WAR_ON_WAR; + p.dwGuildFrom = ws.GID[0]; + p.dwGuildTo = ws.GID[1]; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR, &p, sizeof(p)); + sys_log(0, "GuildWar: GUILD sending start of wait start war %d %d", ws.GID[0], ws.GID[1]); + } +} + +#define for_all(cont, it) for (typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); ++it) + +void CGuildManager::OnSetup(CPeer* peer) +{ + for_all(m_WarMap, it_cont) + for_all(it_cont->second, it) + { + DWORD g1 = it_cont->first; + DWORD g2 = it->first; + TGuildWarPQElement* p = it->second.pElement; + + if (!p || p->bEnd) + continue; + + FSendPeerWar(p->bType, GUILD_WAR_ON_WAR, g1, g2) (peer); + FSendGuildWarScore(p->GID[0], p->GID[1], p->iScore[0], p->iBetScore[0]); + FSendGuildWarScore(p->GID[1], p->GID[0], p->iScore[1], p->iBetScore[1]); + } + + for_all(m_DeclareMap, it) + { + FSendPeerWar(it->bType, GUILD_WAR_SEND_DECLARE, it->dwGuildID[0], it->dwGuildID[1]) (peer); + } + + for_all(m_map_kWarReserve, it) + { + it->second->OnSetup(peer); + } +} + +void CGuildManager::GuildWarWin(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.win; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET win=%d WHERE id=%u", GetTablePostfix(), it->second.win, GID); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::GuildWarLose(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.loss; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET loss=%d WHERE id=%u", GetTablePostfix(), it->second.loss, GID); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::GuildWarDraw(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.draw; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET draw=%d WHERE id=%u", GetTablePostfix(), it->second.draw, GID); + CDBManager::instance().AsyncQuery(buf); +} + +bool CGuildManager::IsHalfWinLadderPoint(DWORD dwGuildWinner, DWORD dwGuildLoser) +{ + DWORD GID1 = dwGuildWinner; + DWORD GID2 = dwGuildLoser; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_mapGuildWarEndTime[GID1]) it = m_mapGuildWarEndTime[GID1].find(GID2); + + if (it != m_mapGuildWarEndTime[GID1].end() && + it->second + GUILD_WAR_LADDER_HALF_PENALTY_TIME > CClientManager::instance().GetCurrentTime()) + return true; + + return false; +} + +void CGuildManager::ProcessDraw(DWORD dwGuildID1, DWORD dwGuildID2) +{ + sys_log(0, "GuildWar: \tThe war between %d and %d is ended in draw", dwGuildID1, dwGuildID2); + + GuildWarDraw(dwGuildID1); + GuildWarDraw(dwGuildID2); + ChangeLadderPoint(dwGuildID1, 0); + ChangeLadderPoint(dwGuildID2, 0); + + QueryRanking(); +} + +void CGuildManager::ProcessWinLose(DWORD dwGuildWinner, DWORD dwGuildLoser) +{ + GuildWarWin(dwGuildWinner); + GuildWarLose(dwGuildLoser); + sys_log(0, "GuildWar: \tWinner : %d Loser : %d", dwGuildWinner, dwGuildLoser); + + int iPoint = GetLadderPoint(dwGuildLoser); + int gain = (int)(iPoint * 0.05); + int loss = (int)(iPoint * 0.07); + + if (IsHalfWinLadderPoint(dwGuildWinner, dwGuildLoser)) + gain /= 2; + + sys_log(0, "GuildWar: \tgain : %d loss : %d", gain, loss); + + ChangeLadderPoint(dwGuildWinner, gain); + ChangeLadderPoint(dwGuildLoser, -loss); + + QueryRanking(); +} + +void CGuildManager::RemoveWar(DWORD GID1, DWORD GID2) +{ + sys_log(0, "GuildWar: RemoveWar(%d, %d)", GID1, GID2); + + if (GID1 > GID2) + std::swap(GID2, GID1); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it == m_WarMap[GID1].end()) + { + if (m_WarMap[GID1].empty()) + m_WarMap.erase(GID1); + + return; + } + + if (it->second.pElement) + it->second.pElement->bEnd = true; + + m_mapGuildWarEndTime[GID1][GID2] = CClientManager::instance().GetCurrentTime(); + + m_WarMap[GID1].erase(it); + + if (m_WarMap[GID1].empty()) + m_WarMap.erase(GID1); +} + +// + +// +void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw) +{ + if (GID1 > GID2) + std::swap(GID2, GID1); + + sys_log(0, "GuildWar: WarEnd %d %d", GID1, GID2); + + itertype(m_WarMap[GID1]) itWarMap = m_WarMap[GID1].find(GID2); + + if (itWarMap == m_WarMap[GID1].end()) + { + sys_err("GuildWar: war not exist or already ended. [1]"); + return; + } + + TGuildWarInfo gwi = itWarMap->second; + TGuildWarPQElement * pData = gwi.pElement; + + if (!pData || pData->bEnd) + { + sys_err("GuildWar: war not exist or already ended. [2]"); + return; + } + + DWORD win_guild = pData->GID[0]; + DWORD lose_guild = pData->GID[1]; + + bool bDraw = false; + + if (!bForceDraw) + { + if (pData->iScore[0] > pData->iScore[1]) + { + win_guild = pData->GID[0]; + lose_guild = pData->GID[1]; + } + else if (pData->iScore[1] > pData->iScore[0]) + { + win_guild = pData->GID[1]; + lose_guild = pData->GID[0]; + } + else + bDraw = true; + } + else + bDraw = true; + + if (bDraw) + ProcessDraw(win_guild, lose_guild); + else + ProcessWinLose(win_guild, lose_guild); + + CClientManager::instance().for_each_peer(FSendPeerWar(0, GUILD_WAR_END, GID1, GID2)); + + RemoveWar(GID1, GID2); +} + +// + +// +void CGuildManager::RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bDraw, long lWarPrice) +{ + sys_log(0, "GuildWar: RecvWarOver : winner %u vs %u draw? %d war_price %d", dwGuildWinner, dwGuildLoser, bDraw ? 1 : 0, lWarPrice); + + DWORD GID1 = dwGuildWinner; + DWORD GID2 = dwGuildLoser; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it == m_WarMap[GID1].end()) + return; + + TGuildWarInfo & gw = it->second; + + // Award + if (bDraw) + { + // give bet money / 2 to both guild + DepositMoney(dwGuildWinner, lWarPrice / 2); + DepositMoney(dwGuildLoser, lWarPrice / 2); + ProcessDraw(dwGuildWinner, dwGuildLoser); + } + else + { + // give bet money to winner guild + DepositMoney(dwGuildWinner, lWarPrice); + ProcessWinLose(dwGuildWinner, dwGuildLoser); + } + + if (gw.pkReserve) + { + if (bDraw || !gw.pElement) + gw.pkReserve->Draw(); + else if (gw.pElement->bType == GUILD_WAR_TYPE_BATTLE) + gw.pkReserve->End(gw.pElement->iBetScore[0], gw.pElement->iBetScore[1]); + } + + RemoveWar(GID1, GID2); +} + +void CGuildManager::RecvWarEnd(DWORD GID1, DWORD GID2) +{ + sys_log(0, "GuildWar: RecvWarEnded : %u vs %u", GID1, GID2); + WarEnd(GID1, GID2, true); +} + +void CGuildManager::StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve) +{ + sys_log(0, "GuildWar: StartWar(%d,%d,%d)", bType, GID1, GID2); + + if (GID1 > GID2) + std::swap(GID1, GID2); + + TGuildWarInfo & gw = m_WarMap[GID1][GID2]; // map insert + + if (bType == GUILD_WAR_TYPE_FIELD) + gw.tEndTime = CClientManager::instance().GetCurrentTime() + GUILD_WAR_DURATION; + else + gw.tEndTime = CClientManager::instance().GetCurrentTime() + 172800; + + gw.pElement = new TGuildWarPQElement(bType, GID1, GID2); + gw.pkReserve = pkReserve; + + m_pqOnWar.push(std::make_pair(gw.tEndTime, gw.pElement)); +} + +void CGuildManager::UpdateScore(DWORD dwGainGID, DWORD dwOppGID, int iScoreDelta, int iBetScoreDelta) +{ + DWORD GID1 = dwGainGID; + DWORD GID2 = dwOppGID; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it != m_WarMap[GID1].end()) + { + TGuildWarPQElement * p = it->second.pElement; + + if (!p || p->bEnd) + { + sys_err("GuildWar: war not exist or already ended."); + return; + } + + int iNewScore = 0; + int iNewBetScore = 0; + + if (p->GID[0] == dwGainGID) + { + p->iScore[0] += iScoreDelta; + p->iBetScore[0] += iBetScoreDelta; + + iNewScore = p->iScore[0]; + iNewBetScore = p->iBetScore[0]; + } + else + { + p->iScore[1] += iScoreDelta; + p->iBetScore[1] += iBetScoreDelta; + + iNewScore = p->iScore[1]; + iNewBetScore = p->iBetScore[1]; + } + + sys_log(0, "GuildWar: SendGuildWarScore guild %u wartype %u score_delta %d betscore_delta %d result %u, %u", + dwGainGID, p->bType, iScoreDelta, iBetScoreDelta, iNewScore, iNewBetScore); + + CClientManager::instance().for_each_peer(FSendGuildWarScore(dwGainGID, dwOppGID, iNewScore, iNewBetScore)); + } +} + +void CGuildManager::AddDeclare(BYTE bType, DWORD guild_from, DWORD guild_to) +{ + TGuildDeclareInfo di(bType, guild_from, guild_to); + + if (m_DeclareMap.find(di) == m_DeclareMap.end()) + m_DeclareMap.emplace(di); + + sys_log(0, "GuildWar: AddDeclare(Type:%d,from:%d,to:%d)", bType, guild_from, guild_to); +} + +void CGuildManager::RemoveDeclare(DWORD guild_from, DWORD guild_to) +{ + typeof(m_DeclareMap.begin()) it = m_DeclareMap.find(TGuildDeclareInfo(0, guild_from, guild_to)); + + if (it != m_DeclareMap.end()) + m_DeclareMap.erase(it); + + it = m_DeclareMap.find(TGuildDeclareInfo(0,guild_to, guild_from)); + + if (it != m_DeclareMap.end()) + m_DeclareMap.erase(it); + + sys_log(0, "GuildWar: RemoveDeclare(from:%d,to:%d)", guild_from, guild_to); +} + +bool CGuildManager::TakeBetPrice(DWORD dwGuildTo, DWORD dwGuildFrom, long lWarPrice) +{ + itertype(m_map_kGuild) it_from = m_map_kGuild.find(dwGuildFrom); + itertype(m_map_kGuild) it_to = m_map_kGuild.find(dwGuildTo); + + if (it_from == m_map_kGuild.end() || it_to == m_map_kGuild.end()) + { + sys_log(0, "TakeBetPrice: guild not exist %u %u", + dwGuildFrom, dwGuildTo); + return false; + } + + if (it_from->second.gold < lWarPrice || it_to->second.gold < lWarPrice) + { + sys_log(0, "TakeBetPrice: not enough gold %u %d to %u %d", + dwGuildFrom, it_from->second.gold, dwGuildTo, it_to->second.gold); + return false; + } + + it_from->second.gold -= lWarPrice; + it_to->second.gold -= lWarPrice; + + MoneyChange(dwGuildFrom, it_from->second.gold); + MoneyChange(dwGuildTo, it_to->second.gold); + return true; +} + +bool CGuildManager::WaitStart(TPacketGuildWar * p) +{ + if (p->lWarPrice > 0) + if (!TakeBetPrice(p->dwGuildFrom, p->dwGuildTo, p->lWarPrice)) + return false; + + DWORD dwCurTime = CClientManager::instance().GetCurrentTime(); + + TGuildWaitStartInfo info(p->bType, p->dwGuildFrom, p->dwGuildTo, p->lWarPrice, p->lInitialScore, NULL); + m_pqWaitStart.push(std::make_pair(dwCurTime + GetGuildWarWaitStartDuration(), info)); + + sys_log(0, + "GuildWar: WaitStart g1 %d g2 %d price %d start at %u", + p->dwGuildFrom, + p->dwGuildTo, + p->lWarPrice, + dwCurTime + GetGuildWarWaitStartDuration()); + + return true; +} + +int CGuildManager::GetLadderPoint(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return 0; + + return it->second.ladder_point; +} + +void CGuildManager::ChangeLadderPoint(DWORD GID, int change) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + TGuild & r = it->second; + + r.ladder_point += change; + + if (r.ladder_point < 0) + r.ladder_point = 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET ladder_point=%d WHERE id=%u", GetTablePostfix(), r.ladder_point, GID); + CDBManager::instance().AsyncQuery(buf); + + sys_log(0, "GuildManager::ChangeLadderPoint %u %d", GID, r.ladder_point); + sys_log(0, "%s", buf); + + TPacketGuildLadder p; + + p.dwGuild = GID; + p.lLadderPoint = r.ladder_point; + p.lWin = r.win; + p.lDraw = r.draw; + p.lLoss = r.loss; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_LADDER, &p, sizeof(TPacketGuildLadder)); +} + +void CGuildManager::UseSkill(DWORD GID, DWORD dwSkillVnum, DWORD dwCooltime) +{ + // GUILD_SKILL_COOLTIME_BUG_FIX + sys_log(0, "UseSkill(gid=%d, skill=%d) CoolTime(%d:%d)", GID, dwSkillVnum, dwCooltime, CClientManager::instance().GetCurrentTime() + dwCooltime); + m_pqSkill.push(std::make_pair(CClientManager::instance().GetCurrentTime() + dwCooltime, TGuildSkillUsed(GID, dwSkillVnum))); + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX +} + +void CGuildManager::MoneyChange(DWORD dwGuild, DWORD dwGold) +{ + sys_log(0, "GuildManager::MoneyChange %d %d", dwGuild, dwGold); + + TPacketDGGuildMoneyChange p; + p.dwGuild = dwGuild; + p.iTotalGold = dwGold; + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_MONEY_CHANGE, &p, sizeof(p)); + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET gold=%u WHERE id = %u", GetTablePostfix(), dwGold, dwGuild); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::DepositMoney(DWORD dwGuild, INT iGold) +{ + if (iGold <= 0) + return; + + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + { + sys_err("No guild by id %u", dwGuild); + return; + } + + it->second.gold += iGold; + sys_log(0, "GUILD: %u Deposit %u Total %d", dwGuild, iGold, it->second.gold); + + MoneyChange(dwGuild, it->second.gold); +} + +void CGuildManager::WithdrawMoney(CPeer* peer, DWORD dwGuild, INT iGold) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + { + sys_err("No guild by id %u", dwGuild); + return; + } + + if (it->second.gold >= iGold) + { + it->second.gold -= iGold; + sys_log(0, "GUILD: %u Withdraw %d Total %d", dwGuild, iGold, it->second.gold); + + TPacketDGGuildMoneyWithdraw p; + p.dwGuild = dwGuild; + p.iChangeGold = iGold; + + peer->EncodeHeader(HEADER_DG_GUILD_WITHDRAW_MONEY_GIVE, 0, sizeof(TPacketDGGuildMoneyWithdraw)); + peer->Encode(&p, sizeof(TPacketDGGuildMoneyWithdraw)); + } +} + +void CGuildManager::WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT iGold) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + return; + + sys_log(0, "GuildManager::WithdrawMoneyReply : guild %u success %d gold %d", dwGuild, bGiveSuccess, iGold); + + if (!bGiveSuccess) + it->second.gold += iGold; + else + MoneyChange(dwGuild, it->second.gold); +} + +// + +// +const int c_aiScoreByLevel[GUILD_MAX_LEVEL+1] = +{ + 500, // level 0 = 500 probably error + 500, // 1 + 1000, + 2000, + 3000, + 4000, + 6000, + 8000, + 10000, + 12000, + 15000, // 10 + 18000, + 21000, + 24000, + 28000, + 32000, + 36000, + 40000, + 45000, + 50000, + 55000, +}; + +const int c_aiScoreByRanking[GUILD_RANK_MAX_NUM+1] = +{ + 0, + 55000, + 50000, + 45000, + 40000, + 36000, + 32000, + 28000, + 24000, + 21000, + 18000, + 15000, + 12000, + 10000, + 8000, + 6000, + 4000, + 3000, + 2000, + 1000, + 500 +}; + +void CGuildManager::BootReserveWar() +{ + const char * c_apszQuery[2] = + { + "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=1 AND winner=-1", + "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=0" + }; + + for (int i = 0; i < 2; ++i) + { + auto pmsg(CDBManager::instance().DirectQuery(c_apszQuery[i])); + + if (pmsg->Get()->uiNumRows == 0) + continue; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pmsg->Get()->pSQLResult))) + { + int col = 0; + + TGuildWarReserve t; + + str_to_number(t.dwID, row[col++]); + str_to_number(t.dwGuildFrom, row[col++]); + str_to_number(t.dwGuildTo, row[col++]); + str_to_number(t.dwTime, row[col++]); + str_to_number(t.bType, row[col++]); + str_to_number(t.lWarPrice, row[col++]); + str_to_number(t.lInitialScore, row[col++]); + str_to_number(t.dwBetFrom, row[col++]); + str_to_number(t.dwBetTo, row[col++]); + str_to_number(t.lPowerFrom, row[col++]); + str_to_number(t.lPowerTo, row[col++]); + str_to_number(t.lHandicap, row[col++]); + t.bStarted = 0; + + CGuildWarReserve * pkReserve = new CGuildWarReserve(t); + + char buf[512]; + snprintf(buf, sizeof(buf), "GuildWar: BootReserveWar : step %d id %u GID1 %u GID2 %u", i, t.dwID, t.dwGuildFrom, t.dwGuildTo); + + //if (i == 0 || (int) t.dwTime - CClientManager::instance().GetCurrentTime() < 60 * 5) + if (i == 0 || (int) t.dwTime - CClientManager::instance().GetCurrentTime() < 0) + { + if (i == 0) + sys_log(0, "%s : DB was shutdowned while war is being.", buf); + else + sys_log(0, "%s : left time lower than 5 minutes, will be canceled", buf); + + pkReserve->Draw(); + delete pkReserve; + } + else + { + sys_log(0, "%s : OK", buf); + m_map_kWarReserve.emplace(t.dwID, pkReserve); + } + } + } +} + +int GetAverageGuildMemberLevel(DWORD dwGID) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "SELECT AVG(level) FROM guild_member%s, player%s AS p WHERE guild_id=%u AND guild_member%s.pid=p.id", + GetTablePostfix(), GetTablePostfix(), dwGID, GetTablePostfix()); + + auto msg(CDBManager::instance().DirectQuery(szQuery)); + + MYSQL_ROW row; + row = mysql_fetch_row(msg->Get()->pSQLResult); + + int nAverageLevel = 0; str_to_number(nAverageLevel, row[0]); + return nAverageLevel; +} + +int GetGuildMemberCount(DWORD dwGID) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM guild_member%s WHERE guild_id=%u", GetTablePostfix(), dwGID); + + auto msg(CDBManager::instance().DirectQuery(szQuery)); + + MYSQL_ROW row; + row = mysql_fetch_row(msg->Get()->pSQLResult); + + DWORD dwCount = 0; str_to_number(dwCount, row[0]); + return dwCount; +} + +bool CGuildManager::ReserveWar(TPacketGuildWar * p) +{ + DWORD GID1 = p->dwGuildFrom; + DWORD GID2 = p->dwGuildTo; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + if (p->lWarPrice > 0) + if (!TakeBetPrice(GID1, GID2, p->lWarPrice)) + return false; + + TGuildWarReserve t; + memset(&t, 0, sizeof(TGuildWarReserve)); + + t.dwGuildFrom = GID1; + t.dwGuildTo = GID2; + t.dwTime = CClientManager::instance().GetCurrentTime() + GetGuildWarReserveSeconds(); + t.bType = p->bType; + t.lWarPrice = p->lWarPrice; + t.lInitialScore = p->lInitialScore; + + int lvp, rkp, alv, mc; + + TGuild & k1 = TouchGuild(GID1); + + lvp = c_aiScoreByLevel[MIN(GUILD_MAX_LEVEL, k1.level)]; + rkp = c_aiScoreByRanking[GetRanking(GID1)]; + alv = GetAverageGuildMemberLevel(GID1); + mc = GetGuildMemberCount(GID1); + + polyPower.SetVar("lvp", lvp); + polyPower.SetVar("rkp", rkp); + polyPower.SetVar("alv", alv); + polyPower.SetVar("mc", mc); + + t.lPowerFrom = (long) polyPower.Eval(); + sys_log(0, "GuildWar: %u lvp %d rkp %d alv %d mc %d power %d", GID1, lvp, rkp, alv, mc, t.lPowerFrom); + + TGuild & k2 = TouchGuild(GID2); + + lvp = c_aiScoreByLevel[MIN(GUILD_MAX_LEVEL, k2.level)]; + rkp = c_aiScoreByRanking[GetRanking(GID2)]; + alv = GetAverageGuildMemberLevel(GID2); + mc = GetGuildMemberCount(GID2); + + polyPower.SetVar("lvp", lvp); + polyPower.SetVar("rkp", rkp); + polyPower.SetVar("alv", alv); + polyPower.SetVar("mc", mc); + + t.lPowerTo = (long) polyPower.Eval(); + sys_log(0, "GuildWar: %u lvp %d rkp %d alv %d mc %d power %d", GID2, lvp, rkp, alv, mc, t.lPowerTo); + + if (t.lPowerTo > t.lPowerFrom) + { + polyHandicap.SetVar("pA", t.lPowerTo); + polyHandicap.SetVar("pB", t.lPowerFrom); + } + else + { + polyHandicap.SetVar("pA", t.lPowerFrom); + polyHandicap.SetVar("pB", t.lPowerTo); + } + + t.lHandicap = (long) polyHandicap.Eval(); + sys_log(0, "GuildWar: handicap %d", t.lHandicap); + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_war_reservation (guild1, guild2, time, type, warprice, initscore, power1, power2, handicap) " + "VALUES(%u, %u, DATE_ADD(NOW(), INTERVAL 180 SECOND), %u, %ld, %ld, %ld, %ld, %ld)", + GID1, GID2, p->bType, p->lWarPrice, p->lInitialScore, t.lPowerFrom, t.lPowerTo, t.lHandicap); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiInsertID == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_err("GuildWar: Cannot insert row"); + return false; + } + + t.dwID = pmsg->Get()->uiInsertID; + + m_map_kWarReserve.emplace(t.dwID, new CGuildWarReserve(t)); + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_ADD, &t, sizeof(TGuildWarReserve)); + return true; +} + +void CGuildManager::ProcessReserveWar() +{ + DWORD dwCurTime = CClientManager::instance().GetCurrentTime(); + + itertype(m_map_kWarReserve) it = m_map_kWarReserve.begin(); + + while (it != m_map_kWarReserve.end()) + { + itertype(m_map_kWarReserve) it2 = it++; + + CGuildWarReserve * pk = it2->second; + TGuildWarReserve & r = pk->GetDataRef(); + + if (!r.bStarted && r.dwTime - 1800 <= dwCurTime) + { + int iMin = (int) ceil((int)(r.dwTime - dwCurTime) / 60.0); + + TGuild & r_1 = m_map_kGuild[r.dwGuildFrom]; + TGuild & r_2 = m_map_kGuild[r.dwGuildTo]; + + sys_log(0, "GuildWar: started GID1 %u GID2 %u %d time %d min %d", r.dwGuildFrom, r.dwGuildTo, r.bStarted, dwCurTime - r.dwTime, iMin); + + if (iMin <= 0) + { + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1 WHERE id=%u", r.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_DEL, &r.dwID, sizeof(DWORD)); + + r.bStarted = true; + + TGuildWaitStartInfo info(r.bType, r.dwGuildFrom, r.dwGuildTo, r.lWarPrice, r.lInitialScore, pk); + m_pqWaitStart.push(std::make_pair(dwCurTime + GetGuildWarWaitStartDuration(), info)); + + TPacketGuildWar pck; + + pck.bType = r.bType; + pck.bWar = GUILD_WAR_WAIT_START; + pck.dwGuildFrom = r.dwGuildFrom; + pck.dwGuildTo = r.dwGuildTo; + pck.lWarPrice = r.lWarPrice; + pck.lInitialScore = r.lInitialScore; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR, &pck, sizeof(TPacketGuildWar)); + //m_map_kWarReserve.erase(it2); + } + else + { + if (iMin != pk->GetLastNoticeMin()) + { + pk->SetLastNoticeMin(iMin); + + CClientManager::instance().SendNotice("The war between %s and %s will start after %d minutes!", r_1.szName, r_2.szName, iMin); + } + } + } + } +} + +bool CGuildManager::Bet(DWORD dwID, const char * c_pszLogin, DWORD dwGold, DWORD dwGuild) +{ + itertype(m_map_kWarReserve) it = m_map_kWarReserve.find(dwID); + + char szQuery[1024]; + + if (it == m_map_kWarReserve.end()) + { + sys_log(0, "WAR_RESERVE: Bet: cannot find reserve war by id %u", dwID); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES('%s', %d, %u, NOW())", + c_pszLogin, ITEM_ELK_VNUM, dwGold); + CDBManager::instance().AsyncQuery(szQuery); + return false; + } + + if (!it->second->Bet(c_pszLogin, dwGold, dwGuild)) + { + sys_log(0, "WAR_RESERVE: Bet: cannot bet id %u, login %s, gold %u, guild %u", dwID, c_pszLogin, dwGold, dwGuild); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES('%s', %d, %u, NOW())", + c_pszLogin, ITEM_ELK_VNUM, dwGold); + CDBManager::instance().AsyncQuery(szQuery); + return false; + } + + return true; +} + +void CGuildManager::CancelWar(DWORD GID1, DWORD GID2) +{ + RemoveDeclare(GID1, GID2); + RemoveWar(GID1, GID2); +} + +bool CGuildManager::ChangeMaster(DWORD dwGID, DWORD dwFrom, DWORD dwTo) +{ + itertype(m_map_kGuild) iter = m_map_kGuild.find(dwGID); + + if (iter == m_map_kGuild.end()) + return false; + + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild%s SET master=%u WHERE id=%u", GetTablePostfix(), dwTo, dwGID); + CDBManager::instance().DirectQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_member%s SET grade=1 WHERE pid=%u", GetTablePostfix(), dwTo); + CDBManager::instance().DirectQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_member%s SET grade=15 WHERE pid=%u", GetTablePostfix(), dwFrom); + CDBManager::instance().DirectQuery(szQuery); + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Guild War Reserve Class +////////////////////////////////////////////////////////////////////////////////////////// +CGuildWarReserve::CGuildWarReserve(const TGuildWarReserve & rTable) +{ + thecore_memcpy(&m_data, &rTable, sizeof(TGuildWarReserve)); + m_iLastNoticeMin = -1; + + Initialize(); +} + +void CGuildWarReserve::Initialize() +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT login, guild, gold FROM guild_war_bet WHERE war_id=%u", m_data.dwID); + + auto msgbet(CDBManager::instance().DirectQuery(szQuery)); + if (msgbet->Get()->uiNumRows) + { + MYSQL_RES * res = msgbet->Get()->pSQLResult; + MYSQL_ROW row; + + char szLogin[LOGIN_MAX_LEN+1]; + DWORD dwGuild; + DWORD dwGold; + + while ((row = mysql_fetch_row(res))) + { + dwGuild = dwGold = 0; + strlcpy(szLogin, row[0], sizeof(szLogin)); + str_to_number(dwGuild, row[1]); + str_to_number(dwGold, row[2]); + + mapBet.emplace(szLogin, std::make_pair(dwGuild, dwGold)); + } + } +} + +void CGuildWarReserve::OnSetup(CPeer * peer) +{ + if (m_data.bStarted) + return; + + FSendPeerWar(m_data.bType, GUILD_WAR_RESERVE, m_data.dwGuildFrom, m_data.dwGuildTo) (peer); + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_RESERVE_ADD, 0, sizeof(TGuildWarReserve)); + peer->Encode(&m_data, sizeof(TGuildWarReserve)); + + TPacketGDGuildWarBet pckBet; + pckBet.dwWarID = m_data.dwID; + + itertype(mapBet) it = mapBet.begin(); + + while (it != mapBet.end()) + { + strlcpy(pckBet.szLogin, it->first.c_str(), sizeof(pckBet.szLogin)); + pckBet.dwGuild = it->second.first; + pckBet.dwGold = it->second.second; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_BET, 0, sizeof(TPacketGDGuildWarBet)); + peer->Encode(&pckBet, sizeof(TPacketGDGuildWarBet)); + + ++it; + } +} + +bool CGuildWarReserve::Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild) +{ + char szQuery[1024]; + + if (m_data.dwGuildFrom != dwGuild && m_data.dwGuildTo != dwGuild) + { + sys_log(0, "GuildWarReserve::Bet: invalid guild id"); + return false; + } + + if (m_data.bStarted) + { + sys_log(0, "GuildWarReserve::Bet: war is already started"); + return false; + } + + if (mapBet.find(pszLogin) != mapBet.end()) + { + sys_log(0, "GuildWarReserve::Bet: failed. already bet"); + return false; + } + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_war_bet (war_id, login, gold, guild) VALUES(%u, '%s', %u, %u)", + m_data.dwID, pszLogin, dwGold, dwGuild); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "GuildWarReserve::Bet: failed. cannot insert row to guild_war_bet table"); + return false; + } + + if (m_data.dwGuildFrom == dwGuild) + m_data.dwBetFrom += dwGold; + else + m_data.dwBetTo += dwGold; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_ADD, &m_data, sizeof(TGuildWarReserve)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET bet_from=%u,bet_to=%u WHERE id=%u", + m_data.dwBetFrom, m_data.dwBetTo, m_data.dwID); + + CDBManager::instance().AsyncQuery(szQuery); + + sys_log(0, "GuildWarReserve::Bet: success. %s %u war_id %u bet %u : %u", pszLogin, dwGuild, m_data.dwID, m_data.dwBetFrom, m_data.dwBetTo); + mapBet.emplace(pszLogin, std::make_pair(dwGuild, dwGold)); + + TPacketGDGuildWarBet pckBet; + pckBet.dwWarID = m_data.dwID; + strlcpy(pckBet.szLogin, pszLogin, sizeof(pckBet.szLogin)); + pckBet.dwGuild = dwGuild; + pckBet.dwGold = dwGold; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_BET, &pckBet, sizeof(TPacketGDGuildWarBet)); + return true; +} + +// + +// +void CGuildWarReserve::Draw() +{ + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1,winner=0 WHERE id=%u", m_data.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + if (mapBet.empty()) + return; + + sys_log(0, "WAR_REWARD: Draw. war_id %u", m_data.dwID); + + itertype(mapBet) it = mapBet.begin(); + + while (1) + { + int iLen = 0; + int iRow = 0; + + iLen += snprintf(szQuery, sizeof(szQuery) - iLen, "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES"); + + while (it != mapBet.end()) + { + if (iRow == 0) + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, "('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, it->second.second); + else + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, ",('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, it->second.second); + + it++; + + if (iLen > 384) + break; + + ++iRow; + } + + if (iRow > 0) + { + sys_log(0, "WAR_REWARD: QUERY: %s", szQuery); + CDBManager::instance().AsyncQuery(szQuery); + } + + if (it == mapBet.end()) + break; + } +} + +void CGuildWarReserve::End(int iScoreFrom, int iScoreTo) +{ + DWORD dwWinner; + + sys_log(0, "WAR_REWARD: End: From %u %d To %u %d", m_data.dwGuildFrom, iScoreFrom, m_data.dwGuildTo, iScoreTo); + + if (m_data.lPowerFrom > m_data.lPowerTo) + { + if (m_data.lHandicap > iScoreFrom - iScoreTo) + { + sys_log(0, "WAR_REWARD: End: failed to overcome handicap, From is strong but To won"); + dwWinner = m_data.dwGuildTo; + } + else + { + sys_log(0, "WAR_REWARD: End: success to overcome handicap, From win!"); + dwWinner = m_data.dwGuildFrom; + } + } + else + { + if (m_data.lHandicap > iScoreTo - iScoreFrom) + { + sys_log(0, "WAR_REWARD: End: failed to overcome handicap, To is strong but From won"); + dwWinner = m_data.dwGuildFrom; + } + else + { + sys_log(0, "WAR_REWARD: End: success to overcome handicap, To win!"); + dwWinner = m_data.dwGuildTo; + } + } + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1,winner=%u,result1=%d,result2=%d WHERE id=%u", + dwWinner, iScoreFrom, iScoreTo, m_data.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + if (mapBet.empty()) + return; + + DWORD dwTotalBet = m_data.dwBetFrom + m_data.dwBetTo; + DWORD dwWinnerBet = 0; + + if (dwWinner == m_data.dwGuildFrom) + dwWinnerBet = m_data.dwBetFrom; + else if (dwWinner == m_data.dwGuildTo) + dwWinnerBet = m_data.dwBetTo; + else + { + sys_err("WAR_REWARD: fatal error, winner does not exist!"); + return; + } + + if (dwWinnerBet == 0) + { + sys_err("WAR_REWARD: total bet money on winner is zero"); + return; + } + + sys_log(0, "WAR_REWARD: End: Total bet: %u, Winner bet: %u", dwTotalBet, dwWinnerBet); + + itertype(mapBet) it = mapBet.begin(); + + while (1) + { + int iLen = 0; + int iRow = 0; + + iLen += snprintf(szQuery, sizeof(szQuery) - iLen, "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES"); + + while (it != mapBet.end()) + { + if (it->second.first != dwWinner) + { + ++it; + continue; + } + + double ratio = (double) it->second.second / dwWinnerBet; + + sys_log(0, "WAR_REWARD: %s %u ratio %f", it->first.c_str(), it->second.second, ratio); + + DWORD dwGold = (DWORD) (dwTotalBet * ratio * 0.9); + + if (iRow == 0) + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, "('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, dwGold); + else + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, ",('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, dwGold); + + ++it; + + if (iLen > 384) + break; + + ++iRow; + } + + if (iRow > 0) + { + sys_log(0, "WAR_REWARD: query: %s", szQuery); + CDBManager::instance().AsyncQuery(szQuery); + } + + if (it == mapBet.end()) + break; + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/GuildManager.h b/source-server/Srcs/Server/db/src/GuildManager.h new file mode 100644 index 000000000..51eea4164 --- /dev/null +++ b/source-server/Srcs/Server/db/src/GuildManager.h @@ -0,0 +1,260 @@ +// vim:ts=8 sw=4 +#ifndef __INC_GUILD_MANAGER_H +#define __INC_GUILD_MANAGER_H + +#include "Peer.h" +#include +#include +#include "../../libsql/libsql.h" +#include "../../libpoly/Poly.h" + +enum +{ + GUILD_WARP_WAR_CHANNEL = 99 +}; + +class CGuildWarReserve; + +struct TGuildDeclareInfo +{ + BYTE bType; + DWORD dwGuildID[2]; + + TGuildDeclareInfo(BYTE _bType, DWORD _dwGuildID1, DWORD _dwGuildID2) + : bType(_bType) + { + dwGuildID[0] = _dwGuildID1; + dwGuildID[1] = _dwGuildID2; + } + + bool operator < (const TGuildDeclareInfo& r) const + { + return ((dwGuildID[0] < r.dwGuildID[0]) || ((dwGuildID[0] == r.dwGuildID[0]) && (dwGuildID[1] < r.dwGuildID[1]))); + } + + TGuildDeclareInfo& operator = (const TGuildDeclareInfo& r) + { + bType = r.bType; + dwGuildID[0] = r.dwGuildID[0]; + dwGuildID[1] = r.dwGuildID[1]; + return *this; + } +}; + +struct TGuildWaitStartInfo +{ + BYTE bType; + DWORD GID[2]; + long lWarPrice; + long lInitialScore; + CGuildWarReserve * pkReserve; + + TGuildWaitStartInfo(BYTE _bType, + DWORD _g1, + DWORD _g2, + long _lWarPrice, + long _lInitialScore, + CGuildWarReserve * _pkReserve) + : bType(_bType), lWarPrice(_lWarPrice), lInitialScore(_lInitialScore), pkReserve(_pkReserve) + { + GID[0] = _g1; + GID[1] = _g2; + } + + bool operator < (const TGuildWaitStartInfo& r) const + { + return ((GID[0] < r.GID[0]) || ((GID[0] == r.GID[0]) && (GID[1] < r.GID[1]))); + } +}; + +struct TGuildWarPQElement +{ + bool bEnd; + BYTE bType; + DWORD GID[2]; + DWORD iScore[2]; + DWORD iBetScore[2]; + + TGuildWarPQElement(BYTE _bType, DWORD GID1, DWORD GID2) : bEnd(false), bType(_bType) + { + bType = _bType; + GID[0] = GID1; + GID[1] = GID2; + iScore[0] = iScore[1] = 0; + iBetScore[0] = iBetScore[1] = 0; + } +}; + +struct TGuildSkillUsed +{ + DWORD GID; + DWORD dwSkillVnum; + + // GUILD_SKILL_COOLTIME_BUG_FIX + TGuildSkillUsed(DWORD _GID, DWORD _dwSkillVnum) : GID(_GID), dwSkillVnum(_dwSkillVnum) + { + } + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX +}; + +inline bool operator < (const TGuildSkillUsed& a, const TGuildSkillUsed& b) +{ + return ((a.GID < b.GID) || ((a.GID == b.GID) && (a.dwSkillVnum < b.dwSkillVnum))); +} + +typedef struct SGuild +{ + SGuild() : ladder_point(0), win(0), draw(0), loss(0), gold(0), level(0) + { + memset(szName, 0, sizeof(szName)); + } + + char szName[GUILD_NAME_MAX_LEN+1]; + int ladder_point; + int win; + int draw; + int loss; + int gold; + int level; +} TGuild; + +typedef struct SGuildWarInfo +{ + time_t tEndTime; + TGuildWarPQElement * pElement; + CGuildWarReserve * pkReserve; + + SGuildWarInfo() : pElement(NULL) + { + } +} TGuildWarInfo; + +class CGuildWarReserve +{ + public: + CGuildWarReserve(const TGuildWarReserve& rTable); + + void Initialize(); + + TGuildWarReserve & GetDataRef() + { + return m_data; + } + + void OnSetup(CPeer * peer); + bool Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild); + void Draw(); + void End(int iScoreFrom, int iScoreTo); + + int GetLastNoticeMin() { return m_iLastNoticeMin; } + void SetLastNoticeMin(int iMin) { m_iLastNoticeMin = iMin; } + + private: + CGuildWarReserve(); + + TGuildWarReserve m_data; + // > + std::map > mapBet; + int m_iLastNoticeMin; +}; + +class CGuildManager : public singleton +{ + public: + CGuildManager(); + virtual ~CGuildManager(); + + void Initialize(); + + void Load(DWORD dwGuildID); + + TGuild & TouchGuild(DWORD GID); + + void Update(); + + void OnSetup(CPeer * peer); + void StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve = NULL); + + void UpdateScore(DWORD guild_gain_point, DWORD guild_opponent, int iScore, int iBetScore); + + void AddDeclare(BYTE bType, DWORD guild_from, DWORD guild_to); + void RemoveDeclare(DWORD guild_from, DWORD guild_to); + + bool TakeBetPrice(DWORD dwGuildTo, DWORD dwGuildFrom, long lWarPrice); + + bool WaitStart(TPacketGuildWar * p); + + void RecvWarEnd(DWORD GID1, DWORD GID2); + void RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bDraw, long lWarPrice); + + void ChangeLadderPoint(DWORD GID, int change); + + void UseSkill(DWORD dwGuild, DWORD dwSkillVnum, DWORD dwCooltime); + + INT GetGuildGold(DWORD dwGuild); + void DepositMoney(DWORD dwGuild, INT lGold); + void WithdrawMoney(CPeer* peer, DWORD dwGuild, INT lGold); + void WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT lGold); + + void MoneyChange(DWORD dwGuild, DWORD dwGold); + + void QueryRanking(); + void ResultRanking(MYSQL_RES * pRes); + int GetRanking(DWORD dwGID); + + // Reserve War + + void BootReserveWar(); + bool ReserveWar(TPacketGuildWar * p); + void ProcessReserveWar(); + bool Bet(DWORD dwID, const char * c_pszLogin, DWORD dwGold, DWORD dwGuild); + + void CancelWar(DWORD GID1, DWORD GID2); + + bool ChangeMaster(DWORD dwGID, DWORD dwFrom, DWORD dwTo); + + private: + void ParseResult(SQLResult * pRes); + + void RemoveWar(DWORD GID1, DWORD GID2); // erase war from m_WarMap and set end on priority queue + + void WarEnd(DWORD GID1, DWORD GID2, bool bDraw = false); + + int GetLadderPoint(DWORD GID); + + void GuildWarWin(DWORD GID); + void GuildWarDraw(DWORD GID); + void GuildWarLose(DWORD GID); + + void ProcessDraw(DWORD dwGuildID1, DWORD dwGuildID2); + void ProcessWinLose(DWORD dwGuildWinner, DWORD dwGuildLoser); + + bool IsHalfWinLadderPoint(DWORD dwGuildWinner, DWORD dwGuildLoser); + + std::map m_map_kGuild; + std::map > m_mapGuildWarEndTime; + + std::set m_DeclareMap; + std::map > m_WarMap; + + typedef std::pair stPairGuildWar; + typedef std::pair stPairSkillUsed; + typedef std::pair stPairWaitStart; + + std::priority_queue, std::greater > + m_pqOnWar; + std::priority_queue, std::greater > + m_pqWaitStart; + std::priority_queue, std::greater > + m_pqSkill; + + std::map m_map_kWarReserve; + CPoly polyPower; + CPoly polyHandicap; + + // GID Ranking + std::map map_kLadderPointRankingByGID; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/HB.cpp b/source-server/Srcs/Server/db/src/HB.cpp new file mode 100644 index 000000000..b79e69238 --- /dev/null +++ b/source-server/Srcs/Server/db/src/HB.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "HB.h" +#include "Main.h" +#include "DBManager.h" + +#include + +PlayerHB::PlayerHB() +{ + m_iExpireTime = 3600; // 1 hour hotbackup default. +} + +PlayerHB::~PlayerHB() +{ +} + +bool PlayerHB::Initialize() +{ + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "SHOW CREATE TABLE player%s", GetTablePostfix()); + + auto pMsg(CDBManager::instance().DirectQuery(szQuery)); + if (pMsg->Get()->uiNumRows == 0) + return false; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + m_stCreateTableQuery = row[1]; + return true; +} + +// + +// +void PlayerHB::Put(DWORD id) +{ + itertype(m_map_data) it = m_map_data.find(id); + + if (it == m_map_data.end()) + { + Query(id); + m_map_data.emplace(id, get_dword_time()); + return; + } + + if (time(0) - it->second > m_iExpireTime) + Query(id); +} + +// +// +bool PlayerHB::Query(DWORD id) +{ + time_t ct = time(0); + struct tm curr_tm = *localtime(&ct); + char szTableName[64]; + snprintf(szTableName, sizeof(szTableName), "hb_%02d%02d%02d%02d_player%s", + curr_tm.tm_year - 100, curr_tm.tm_mon + 1, curr_tm.tm_mday, curr_tm.tm_hour, GetTablePostfix()); + + char szQuery[4096]; + + if (m_stTableName.compare(szTableName)) + { + char szFind[32]; + snprintf(szFind, sizeof(szFind), "CREATE TABLE `player%s`", GetTablePostfix()); + int pos = m_stCreateTableQuery.find(szFind); + + if (pos < 0) + { + sys_err("cannot find %s ", szFind); + return false; + } + + snprintf(szQuery, sizeof(szQuery), "CREATE TABLE IF NOT EXISTS %s%s", szTableName, m_stCreateTableQuery.c_str() + strlen(szFind)); + auto pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_HOTBACKUP)); + m_stTableName = szTableName; + } + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO %s SELECT * FROM %splayer%s WHERE id=%u", m_stTableName.c_str(), GetPlayerDBName(), GetTablePostfix(), id); + CDBManager::instance().AsyncQuery(szQuery, SQL_HOTBACKUP); + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/HB.h b/source-server/Srcs/Server/db/src/HB.h new file mode 100644 index 000000000..1e7b8fcc4 --- /dev/null +++ b/source-server/Srcs/Server/db/src/HB.h @@ -0,0 +1,25 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN_II_PLAYERHB_H__ +#define __INC_METIN_II_PLAYERHB_H__ + +class PlayerHB : public singleton +{ + public: + PlayerHB(); + virtual ~PlayerHB(); + + bool Initialize(); + + void Put(DWORD id); + + private: + bool Query(DWORD id); + + std::map m_map_data; + std::string m_stCreateTableQuery; + std::string m_stTableName; + int m_iExpireTime; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ItemAwardManager.cpp b/source-server/Srcs/Server/db/src/ItemAwardManager.cpp new file mode 100644 index 000000000..c4fcba591 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ItemAwardManager.cpp @@ -0,0 +1,509 @@ +#include "stdafx.h" +#include "QID.h" +#include "DBManager.h" +#include "ItemAwardManager.h" +#include "Peer.h" +#include "ClientManager.h" + +#ifdef ENABLE_EXTEND_ITEM_AWARD +#include +#include +#include +#include + +inline double uniform_random(const double a, const double b) +{ + return msl::random_real(a, b); +} + +inline float gauss_random(const float fAverage, const float sigma) +{ + static bool bHasNextGaussian = false; + static float fNextGaussian = 0.0f; + + if (bHasNextGaussian) + { + bHasNextGaussian = false; + return (fNextGaussian * sigma) + fAverage; + } + else + { + double v1(0), v2(0), s(0); + do + { + v1 = uniform_random(-1.f, 1.f); + v2 = uniform_random(-1.f, 1.f); + s = (v1 * v1) + (v2 * v2); + } while (s >= 1.f || fabs(s) < FLT_EPSILON); + + const double multiplier = sqrtf(-2 * logf(s) / s); + fNextGaussian = v2 * multiplier; + bHasNextGaussian = true; + return (v1 * multiplier) * sigma + fAverage; + } +} +#endif + +DWORD g_dwLastCachedItemAwardID = 0; +ItemAwardManager::ItemAwardManager() +{ +} + +ItemAwardManager::~ItemAwardManager() +{ +} + +void ItemAwardManager::RequestLoad() +{ + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT id,login,vnum,count,socket0,socket1,socket2," + #ifdef ENABLE_EXTEND_ITEM_AWARD + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6, " + #endif + "mall,why FROM item_award WHERE taken_time IS NULL and id > %d", g_dwLastCachedItemAwardID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_LOAD, 0, NULL); +} + +void ItemAwardManager::Load(SQLMsg * pMsg) +{ + MYSQL_RES * pRes = pMsg->Get()->pSQLResult; + + for (uint i = 0; i < pMsg->Get()->uiNumRows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(pRes); + int col = 0; + + DWORD dwID = 0; + str_to_number(dwID, row[col++]); + + if (m_map_award.find(dwID) != m_map_award.end()) + continue; + + TItemAward * kData = new TItemAward{}; + + kData->dwID = dwID; + trim_and_lower(row[col++], kData->szLogin, sizeof(kData->szLogin)); + str_to_number(kData->dwVnum, row[col++]); + str_to_number(kData->dwCount, row[col++]); + str_to_number(kData->dwSocket0, row[col++]); + str_to_number(kData->dwSocket1, row[col++]); + str_to_number(kData->dwSocket2, row[col++]); + #ifdef ENABLE_EXTEND_ITEM_AWARD + for (size_t j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) + { + str_to_number(kData->aAttr[j].bType, row[col++]); + str_to_number(kData->aAttr[j].sValue, row[col++]); + } + #endif + str_to_number(kData->bMall, row[col++]); + + if (row[col]) + { + strlcpy(kData->szWhy, row[col], sizeof(kData->szWhy)); + + char* whyStr = kData->szWhy; + char cmdStr[100] = ""; + strcpy(cmdStr,whyStr); + char command[20] = ""; + // @fixme203 directly GetCommand instead of strcpy + CClientManager::instance().GetCommand(cmdStr, command); + //sys_err("%d, %s",pItemAward->dwID,command); + if( !(strcmp(command,"GIFT") )) + { + TPacketItemAwardInfromer giftData; + strcpy(giftData.login, kData->szLogin); + strcpy(giftData.command, command); + giftData.vnum = kData->dwVnum; + CClientManager::instance().ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer)); + } + } + + m_map_award.emplace(dwID, kData); + + printf("ITEM_AWARD load id %u bMall %d \n", kData->dwID, kData->bMall); + sys_log(0, "ITEM_AWARD: load id %lu login %s vnum %lu count %u socket %lu", kData->dwID, kData->szLogin, kData->dwVnum, kData->dwCount, kData->dwSocket0); + std::set & kSet = m_map_kSetAwardByLogin[kData->szLogin]; + kSet.emplace(kData); + + if (dwID > g_dwLastCachedItemAwardID) + g_dwLastCachedItemAwardID = dwID; + } +} + +std::set * ItemAwardManager::GetByLogin(const char * c_pszLogin) +{ + itertype(m_map_kSetAwardByLogin) it = m_map_kSetAwardByLogin.find(c_pszLogin); + + if (it == m_map_kSetAwardByLogin.end()) + return NULL; + + return &it->second; +} + +void ItemAwardManager::Taken(DWORD dwAwardID, DWORD dwItemID) +{ + itertype(m_map_award) it = m_map_award.find(dwAwardID); + + if (it == m_map_award.end()) + { + sys_log(0, "ITEM_AWARD: Taken ID not exist %lu", dwAwardID); + return; + } + + TItemAward * k = it->second; + k->bTaken = true; + + // Update taken_time in database to prevent not to give him again. + + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "UPDATE item_award SET taken_time=NOW(),item_id=%u WHERE id=%u AND taken_time IS NULL", + dwItemID, dwAwardID); + + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_TAKEN, 0, NULL); +} + +std::map& ItemAwardManager::GetMapAward() +{ + return m_map_award; +} + +std::map >& ItemAwardManager::GetMapkSetAwardByLogin() +{ + return m_map_kSetAwardByLogin; +} + +#ifdef USE_ITEM_AWARD_CHECK_ATTRIBUTES +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| Attribute set index, return a specific index by item type & item sub type. +||| List initialization (since C++11) +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +int8_t ItemAwardManager::GetItemAttributeSetIndex(const uint8_t bItemType, const uint8_t bItemSubType) const +{ + using TAttributeMapT = std::map>; + static const TAttributeMapT mapAttrSetFields = + { + { ITEM_WEAPON, + { + {WEAPON_ARROW, ITEM_ATTRIBUTE_NONE}, + #ifdef ENABLE_QUIVER_SYSTEM + {WEAPON_QUIVER, ITEM_ATTRIBUTE_NONE}, + #endif + } + }, + + { ITEM_ARMOR, + { + {ARMOR_BODY, ATTRIBUTE_SET_BODY}, + {ARMOR_WRIST, ATTRIBUTE_SET_WRIST}, + {ARMOR_FOOTS, ATTRIBUTE_SET_FOOTS}, + {ARMOR_NECK, ATTRIBUTE_SET_NECK}, + {ARMOR_HEAD, ATTRIBUTE_SET_HEAD}, + {ARMOR_SHIELD, ATTRIBUTE_SET_SHIELD}, + {ARMOR_EAR, ATTRIBUTE_SET_EAR}, + #ifdef ENABLE_PENDANT_SYSTEM + {ARMOR_PENDANT, ATTRIBUTE_SET_PENDANT}, + #endif + #ifdef ENABLE_GLOVE_SYSTEM + {ARMOR_GLOVE, ATTRIBUTE_SET_GLOVE}, + #endif + } + }, + + { ITEM_COSTUME, + { + {COSTUME_BODY, ATTRIBUTE_SET_BODY}, + {COSTUME_HAIR, ATTRIBUTE_SET_HEAD}, + #ifdef ENABLE_MOUNT_COSTUME_SYSTEM + {COSTUME_MOUNT, ITEM_ATTRIBUTE_NONE}, + #endif + #ifdef ENABLE_WEAPON_COSTUME_SYSTEM + {COSTUME_WEAPON, ATTRIBUTE_SET_WEAPON}, + #endif + } + }, + }; + + const auto c_iter_type = mapAttrSetFields.find(bItemType); + if (c_iter_type != mapAttrSetFields.end()) + { + const auto c_iter_sub_type = c_iter_type->second.find(bItemSubType); + if (c_iter_sub_type != c_iter_type->second.end()) + return c_iter_sub_type->second; + else if (c_iter_type->first == ITEM_WEAPON) + return ATTRIBUTE_SET_WEAPON; + } + + return ITEM_ATTRIBUTE_NONE; +} + +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| 15.04.2018 - Added a check for attr types and values min - max. +||| You can't insert wrong bonuses into a specific item. +||| Eg. Add 2000M MAX_HP on your Sword+9, was possible, now not. +||| Eg. Add +500 INT to your shield, now there's a check for min-max value of player.item_attr Lv.1 - Lv.5 +||| and your 500 INT value will be replaced with max value from lvl5 of bonus, like 12 (lv5), that happen with all the bonuses, +||| same thing with the values lower than lvl1, like 5 HP_REGEN on your neck, when the minimum (lv1) is 10, the value will be replaced with 10. +||| If the bonus type can't be added into a specific item, the bonus will be ignored > deleted. (example: critical pct to armor) +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemAttributes(TItemAward & rkItemAward, const TItemTable & rkItemTable, const std::vector & vec_itemAttrTable, const std::vector & vec_itemRareTable) +{ + const auto bItemType = rkItemTable.bType; + const auto bItemSubType = rkItemTable.bSubType; + const auto iAttributeSet = GetItemAttributeSetIndex(bItemType, bItemSubType); + if (iAttributeSet != ITEM_ATTRIBUTE_NONE) + { + // remove duplicates + std::unordered_set duplicates; + for (size_t i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + { + // clear up duplicates after reached the rare ones + if (i == ITEM_ATTRIBUTE_RARE_START) + duplicates.clear(); + + auto bApplyType = rkItemAward.aAttr[i].bType; + // check if already present + if (duplicates.find(bApplyType) != duplicates.end()) + { + rkItemAward.aAttr[i].bType = 0; + rkItemAward.aAttr[i].sValue = 0; + continue; + } + duplicates.emplace(bApplyType); + } + + for (size_t i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + { + const auto bApplyType = rkItemAward.aAttr[i].bType; + const TItemAttrTable * pkAttrTable{nullptr}; + + // skip special bonus + if (bApplyType == APPLY_SKILL_DAMAGE_BONUS || bApplyType == APPLY_NORMAL_HIT_DAMAGE_BONUS) + continue; + + // check normal bonus with item_attr + if (i < ITEM_ATTRIBUTE_NORM_END) + { + for (size_t j = 0; j < vec_itemAttrTable.size() && !pkAttrTable; ++j) + { + const TItemAttrTable & rkAttrTable = vec_itemAttrTable.at(j); + if (rkAttrTable.dwApplyIndex == bApplyType) + { + const auto bAttrLevel = rkAttrTable.bMaxLevelBySet[iAttributeSet]; + if (bAttrLevel > 0) + pkAttrTable = &rkAttrTable; + } + } + } + // check rare bonus with item_attr_rare + else if (i >= ITEM_ATTRIBUTE_RARE_START) + { + for (size_t j = 0; j < vec_itemRareTable.size() && !pkAttrTable; ++j) + { + const TItemAttrTable & rkAttrTable = vec_itemRareTable.at(j); + if (rkAttrTable.dwApplyIndex == bApplyType) + { + const auto bAttrLevel = rkAttrTable.bMaxLevelBySet[iAttributeSet]; + if (bAttrLevel > 0) + pkAttrTable = &rkAttrTable; + } + } + } + + if (pkAttrTable) + { + const auto sMinValue = static_cast(pkAttrTable->lValues[0]); + const auto sMaxValue = static_cast(pkAttrTable->lValues[(sizeof(pkAttrTable->lValues) / sizeof(pkAttrTable->lValues[0])) - 1]); + rkItemAward.aAttr[i].sValue = MINMAX(sMinValue, rkItemAward.aAttr[i].sValue, sMaxValue); + } + else + { + rkItemAward.aAttr[i].bType = 0; + rkItemAward.aAttr[i].sValue = 0; + } + } + } +} +#endif + +#ifdef ENABLE_EXTEND_ITEM_AWARD +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| 02.04.2018 - Fixed unknown average/skill damage bonus value. +||| player.item_proto.addon_type = -1 (Eg. 189, 199, 299, 1139, 1179, 2159, 2179, 3169, 3219, 5119, 5129, 6019, 6069, 6079, 7169)[+0 - +9] +||| That's for the items which have addon type (-1) and you added them in item shop without bonuses like skill damage or hit damage, +||| value x, y as default, so they'll will be without bonuses and get 'bugged'. +||| Now when the item will be inserted there'll be a check if item doesn't have those bonuses (from query) add a random average/skill damage bonus value. +||| INSERT INTO player.item_award(`login`, `vnum`, `count`, `mall`) VALUES ('account', 189, 1, 1); +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemAddonType(TItemAward & rkItemAward, const TItemTable & rkItemTable) +{ + const bool bIsAddonTypeItem = (rkItemTable.sAddonType == -1); + if (!bIsAddonTypeItem) + return; + + bool bHasBonus = false; + for (size_t i = 0; i < ITEM_ATTRIBUTE_MAX_NUM && !bHasBonus; ++i) + { + const auto bType = rkItemAward.aAttr[i].bType; + const auto sValue = rkItemAward.aAttr[i].sValue; + + if ((bType == APPLY_SKILL_DAMAGE_BONUS || bType == APPLY_NORMAL_HIT_DAMAGE_BONUS) && sValue) + bHasBonus = true; + } + + if (!bHasBonus) + { + const auto sApplySkillDamageValue = MINMAX(-30, static_cast((gauss_random(0, 5) + 0.5f)), 30); + const auto sApplyNormalHitValue = std::abs(sApplySkillDamageValue) <= 20 ? + (-2 * sApplySkillDamageValue) + std::abs(number(-8, 8) + number(-8, 8)) + number(1, 4) : + (-2 * sApplySkillDamageValue) + number(1, 5); + + rkItemAward.aAttr[0].bType = APPLY_NORMAL_HIT_DAMAGE_BONUS; + rkItemAward.aAttr[0].sValue = sApplyNormalHitValue; + rkItemAward.aAttr[1].bType = APPLY_SKILL_DAMAGE_BONUS; + rkItemAward.aAttr[1].sValue = sApplySkillDamageValue; + + // clear up possible duplicates + for (size_t i = 2; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + { + const auto bType = rkItemAward.aAttr[i].bType; + if (bType == APPLY_SKILL_DAMAGE_BONUS || bType == APPLY_NORMAL_HIT_DAMAGE_BONUS) + { + rkItemAward.aAttr[i].bType = 0; + rkItemAward.aAttr[i].sValue = 0; + } + } + } +} + +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| 12.04.2019 - Added support for books. +||| Check skill types, unknown skill, skill vnum need to be saved into socket0, +||| (4=Aura of the Sword < player.skill_proto), if the skill vnum is unknown, there will be a random book based on pc races, +||| excluded skills PASSIVE, GUILD, SUPPORT. +||| INSERT INTO player.item_award(`login`, `vnum`, `count`, `mall`) VALUES ('account', 50300, 1, 1); # Random book +||| INSERT INTO player.item_award(`login`, `vnum`, `count`, `socket0`, `mall`) VALUES ('account', 50300, 1, 4, 1); # Specific book by skill vnum +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemSkillBook(TItemAward & rkItemAward, const std::vector vec_skillTable) +{ + const bool bIsBookItem = (rkItemAward.dwVnum == ITEM_SKILL_VNUM || rkItemAward.dwVnum == ITEM_SKILLFORGET_VNUM); + if (!bIsBookItem) + return; + + auto dwSocket0SkillVnum = rkItemAward.dwSocket0; + bool bHasSkill = false; + for (size_t i = 0; i < vec_skillTable.size(); ++i) + { + if (vec_skillTable[i].dwVnum == dwSocket0SkillVnum) + { + bHasSkill = true; + break; + } + } + + if (!bHasSkill) + { + do + { + const TSkillTable * pkSkillTable = &vec_skillTable.at(number(0, vec_skillTable.size() - 1)); + if (!pkSkillTable) + continue; + + const auto dwSkillVnum = pkSkillTable->dwVnum; + const auto bSkillType = pkSkillTable->bType; + + if ((JOB_WARRIOR + 1 <= bSkillType && bSkillType <= JOB_SHAMAN + 1) + #ifdef ENABLE_WOLFMAN_CHARACTER + || bSkillType == 7 + #endif + ) + continue; + + dwSocket0SkillVnum = dwSkillVnum; + break; + } + while (true); + + rkItemAward.dwSocket0 = dwSocket0SkillVnum; + } +} + +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| 11.04.2019 - Fixed unstackable items. +||| Check if item count overflow occured, then set it to maximum. +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemCount(TItemAward & rkItemAward, const TItemTable & rkItemTable) +{ + const bool bIsStackableItem = (rkItemTable.dwFlags & ITEM_FLAG_STACKABLE); + if (rkItemAward.dwCount > 1 && !bIsStackableItem) + rkItemAward.dwCount = 1; + else + rkItemAward.dwCount = MINMAX(1, rkItemAward.dwCount, ITEM_MAX_COUNT); +} + +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| 20.08.2021 - Fixed not slottable items. +||| New created equipments had no available slots. +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemSocket(TItemAward & rkItemAward, const TItemTable & rkItemTable) +{ + // check for limited time items + for (const auto & limit : rkItemTable.aLimits) + { + if (LIMIT_REAL_TIME == limit.bType || LIMIT_REAL_TIME_START_FIRST_USE == limit.bType) + return; + } + + // check slottable sockets for non limited time items + const auto maxSockets = std::min(rkItemTable.bGainSocketPct, ITEM_SOCKET_MAX_NUM); + if (maxSockets <= 0) + return; + + if (maxSockets >= 1) + rkItemAward.dwSocket0 = 1; + if (maxSockets >= 2) + rkItemAward.dwSocket1 = 1; + if (maxSockets >= 3) + rkItemAward.dwSocket2 = 1; +} + +/*******************************************************************\ +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +||| TODO-UNFINISHED: Check if apply_type exists in bonuses. +||| Check if apply_value, apply_duration is equal with grades (1/2/3/4/5) from settings, blend.txt +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +\*******************************************************************/ +void ItemAwardManager::CheckItemBlend(TItemAward & rkItemAward, const TItemTable & rkItemTable) +{ + const bool bIsBlendItem = (rkItemTable.bType == ITEM_BLEND); + if (!bIsBlendItem) + return; + + const auto bApplyType = rkItemAward.dwSocket0; + const auto bApplyValue = rkItemAward.dwSocket1; + const auto dwApplyDuration = rkItemAward.dwSocket2; + + if (bApplyType == 0 || bApplyValue == 0 || dwApplyDuration == 0) + sys_err("ItemAwardManager: Unknown sockets for ITEM_BLEND."); +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ItemAwardManager.h b/source-server/Srcs/Server/db/src/ItemAwardManager.h new file mode 100644 index 000000000..57d22a375 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ItemAwardManager.h @@ -0,0 +1,69 @@ +// vim:ts=8 sw=4 +#ifndef __INC_ITEM_AWARD_H +#define __INC_ITEM_AWARD_H +#include +#include +#include "Peer.h" + +#ifdef ENABLE_EXTEND_ITEM_AWARD +enum +{ + ITEM_ATTRIBUTE_NONE = -1, + ITEM_SKILL_VNUM = 50300, + ITEM_SKILLFORGET_VNUM = 70037, +}; +#endif + +typedef struct SItemAward +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN+1]; + DWORD dwVnum; + DWORD dwCount; + DWORD dwSocket0; + DWORD dwSocket1; + DWORD dwSocket2; + char szWhy[ITEM_AWARD_WHY_MAX_LEN+1]; + bool bTaken; + bool bMall; + #ifdef ENABLE_EXTEND_ITEM_AWARD + TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM]; + #endif +} TItemAward; + +class ItemAwardManager : public singleton +{ + public: + ItemAwardManager(); + virtual ~ItemAwardManager(); + + void RequestLoad(); + void Load(SQLMsg * pMsg); + std::set * GetByLogin(const char * c_pszLogin); + + void Taken(DWORD dwAwardID, DWORD dwItemID); + + #ifdef ENABLE_EXTEND_ITEM_AWARD + void CheckItemAddonType(TItemAward & pkItemAward, const TItemTable & pkItemTable); + void CheckItemCount(TItemAward & pkItemAward, const TItemTable & pkItemTable); + void CheckItemSocket(TItemAward & pkItemAward, const TItemTable & pkItemTable); + void CheckItemBlend(TItemAward & pkItemAward, const TItemTable & pkItemTable); + void CheckItemSkillBook(TItemAward & pkItemAward, const std::vector vec_skillTable); + #endif + #ifdef USE_ITEM_AWARD_CHECK_ATTRIBUTES + void CheckItemAttributes(TItemAward & pkItemAward, const TItemTable & pkItemTable, const std::vector & vec_itemAttrTable, const std::vector & vec_itemRareTable); + int8_t GetItemAttributeSetIndex(const uint8_t bItemType, const uint8_t bItemSubType) const; + #endif + + // gift notify + std::map& GetMapAward(); + std::map >& GetMapkSetAwardByLogin(); + private: + // ID, ItemAward pair + std::map m_map_award; + // PID, ItemAward pair + std::map > m_map_kSetAwardByLogin; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ItemIDRangeManager.cpp b/source-server/Srcs/Server/db/src/ItemIDRangeManager.cpp new file mode 100644 index 000000000..2f57d8ae7 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ItemIDRangeManager.cpp @@ -0,0 +1,160 @@ +#include "stdafx.h" +#include "ItemIDRangeManager.h" +#include "Main.h" +#include "DBManager.h" +#include "ClientManager.h" +#include "Peer.h" + +CItemIDRangeManager::CItemIDRangeManager() +{ + m_listData.clear(); +} + +void CItemIDRangeManager::Build() +{ + DWORD dwMin = 0; + DWORD dwMax = 0; + TItemIDRangeTable range; + + for (int i = 0; ; ++i) + { + dwMin = cs_dwMinimumRange * (i + 1) + 1; + dwMax = cs_dwMinimumRange * (i + 2); + + if (dwMax == cs_dwMaxItemID) + break; + + if (CClientManager::instance().GetItemRange().dwMin <= dwMin && + CClientManager::instance().GetItemRange().dwMax >= dwMax) + { + continue; + } + + if (BuildRange(dwMin, dwMax, range) == true) + { + m_listData.emplace_back(range); + } + } +} + +struct FCheckCollision +{ + bool hasCollision; + TItemIDRangeTable range; + + FCheckCollision(TItemIDRangeTable data) + { + hasCollision = false; + range = data; + } + + void operator() (CPeer* peer) + { + if (hasCollision == false) + { + hasCollision = peer->CheckItemIDRangeCollision(range); + } + } +}; + +TItemIDRangeTable CItemIDRangeManager::GetRange() +{ + TItemIDRangeTable ret; + ret.dwMin = 0; + ret.dwMax = 0; + ret.dwUsableItemIDMin = 0; + + if (m_listData.size() > 0) + { + while (m_listData.size() > 0) + { + ret = m_listData.front(); + m_listData.pop_front(); + + FCheckCollision f(ret); + CClientManager::instance().for_each_peer(f); + + if (f.hasCollision == false) return ret; + } + } + + for (int i = 0; i < 10; ++i) + sys_err("ItemIDRange: NO MORE ITEM ID RANGE"); + + return ret; +} + +bool CItemIDRangeManager::BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range) +{ + char szQuery[1024]; + DWORD dwItemMaxID = 0; + MYSQL_ROW row; + + snprintf(szQuery, sizeof(szQuery), "SELECT MAX(id) FROM item%s WHERE id >= %u and id <= %u", GetTablePostfix(), dwMin, dwMax); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery); + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + row = mysql_fetch_row(pMsg->Get()->pSQLResult); + str_to_number(dwItemMaxID, row[0]); + } + } + + if (dwItemMaxID == 0) + dwItemMaxID = dwMin; + else + dwItemMaxID++; + + if ((dwMax < dwItemMaxID) || (dwMax - dwItemMaxID < cs_dwMinimumRemainCount)) + { + sys_log(0, "ItemIDRange: Build %u ~ %u start: %u\tNOT USE remain count is below %u", + dwMin, dwMax, dwItemMaxID, cs_dwMinimumRemainCount); + } + else + { + range.dwMin = dwMin; + range.dwMax = dwMax; + range.dwUsableItemIDMin = dwItemMaxID; + + snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM item%s WHERE id >= %u AND id <= %u", + GetTablePostfix(), range.dwUsableItemIDMin, range.dwMax); + + pMsg = CDBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + DWORD count = 0; + row = mysql_fetch_row(pMsg->Get()->pSQLResult); + str_to_number(count, row[0]); + + if (count > 0) + { + sys_err("ItemIDRange: Build: %u ~ %u\thave a item", range.dwUsableItemIDMin, range.dwMax); + return false; + } + else + { + sys_log(0, "ItemIDRange: Build: %u ~ %u start:%u", range.dwMin, range.dwMax, range.dwUsableItemIDMin); + return true; + } + } + } + } + + return false; +} + +void CItemIDRangeManager::UpdateRange(DWORD dwMin, DWORD dwMax) +{ + TItemIDRangeTable range; + + if (BuildRange(dwMin, dwMax, range) == true) + { + m_listData.emplace_back(range); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ItemIDRangeManager.h b/source-server/Srcs/Server/db/src/ItemIDRangeManager.h new file mode 100644 index 000000000..ef46537c2 --- /dev/null +++ b/source-server/Srcs/Server/db/src/ItemIDRangeManager.h @@ -0,0 +1,25 @@ +// vim:ts=4 sw=4 +#ifndef __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ +#define __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ + +class CItemIDRangeManager : public singleton +{ + private : + const static DWORD cs_dwMaxItemID = 4290000000UL; + const static DWORD cs_dwMinimumRange = 10000000UL; + const static DWORD cs_dwMinimumRemainCount = 10000UL; + + std::list m_listData; + + public : + CItemIDRangeManager(); + + void Build(); + bool BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range); + void UpdateRange(DWORD dwMin, DWORD dwMax); + + TItemIDRangeTable GetRange(); +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Lock.cpp b/source-server/Srcs/Server/db/src/Lock.cpp new file mode 100644 index 000000000..610d6269c --- /dev/null +++ b/source-server/Srcs/Server/db/src/Lock.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "Lock.h" + +CLock::CLock() +{ +} + +CLock::~CLock() +{ +} + +void CLock::Initialize() +{ + m_bLocked = false; +#ifndef __WIN32__ + pthread_mutex_init(&m_lock, NULL); +#else + ::InitializeCriticalSection(&m_lock); +#endif +} + +void CLock::Destroy() +{ + assert(!m_bLocked && "lock didn't released"); +#ifndef __WIN32__ + pthread_mutex_destroy(&m_lock); +#else + ::DeleteCriticalSection(&m_lock); +#endif +} + +int CLock::Trylock() +{ +#ifndef __WIN32__ + return pthread_mutex_trylock(&m_lock); +#else + return ::TryEnterCriticalSection(&m_lock); +#endif +} + +void CLock::Lock() +{ +#ifndef __WIN32__ + pthread_mutex_lock(&m_lock); +#else + ::EnterCriticalSection(&m_lock); +#endif + m_bLocked = true; +} + +void CLock::Unlock() +{ + assert(m_bLocked && "lock didn't issued"); + m_bLocked = false; +#ifndef __WIN32__ + pthread_mutex_unlock(&m_lock); +#else + ::LeaveCriticalSection(&m_lock); +#endif +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Lock.h b/source-server/Srcs/Server/db/src/Lock.h new file mode 100644 index 000000000..eaeba0a7a --- /dev/null +++ b/source-server/Srcs/Server/db/src/Lock.h @@ -0,0 +1,28 @@ +// vim:ts=8 sw=4 +#ifndef __INC_LOCK_H__ +#define __INC_LOCK_H__ + +#ifdef __WIN32__ +typedef CRITICAL_SECTION lock_t; +#else +typedef pthread_mutex_t lock_t; +#endif + +class CLock +{ + public: + CLock(); + ~CLock(); + + void Initialize(); + void Destroy(); + int Trylock(); + void Lock(); + void Unlock(); + + private: + lock_t m_lock; + bool m_bLocked; +}; +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/LoginData.cpp b/source-server/Srcs/Server/db/src/LoginData.cpp new file mode 100644 index 000000000..9bb78b5d9 --- /dev/null +++ b/source-server/Srcs/Server/db/src/LoginData.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" +#include "LoginData.h" +#include "ClientManager.h" + +CLoginData::CLoginData() +{ + m_dwKey = 0; + memset(m_adwClientKey, 0, sizeof(m_adwClientKey)); + m_dwConnectedPeerHandle = 0; + m_dwLogonTime = 0; + memset(m_szIP, 0, sizeof(m_szIP)); + m_bPlay = false; + m_bDeleted = false; + m_bBillType = 0; + m_dwBillID = 0; + m_lastPlayTime = 0; + m_dwLastPlayerID = 0; + + memset(&m_data, 0, sizeof(TAccountTable)); +} + +TAccountTable & CLoginData::GetAccountRef() +{ + return m_data; +} + +void CLoginData::SetClientKey(const DWORD * c_pdwClientKey) +{ + thecore_memcpy(&m_adwClientKey, c_pdwClientKey, sizeof(DWORD) * 4); +} + +const DWORD * CLoginData::GetClientKey() +{ + return &m_adwClientKey[0]; +} + +void CLoginData::SetKey(DWORD dwKey) +{ + m_dwKey = dwKey; +} + +DWORD CLoginData::GetKey() +{ + return m_dwKey; +} + +void CLoginData::SetConnectedPeerHandle(DWORD dwHandle) +{ + m_dwConnectedPeerHandle = dwHandle; +} + +DWORD CLoginData::GetConnectedPeerHandle() +{ + return m_dwConnectedPeerHandle; +} + +void CLoginData::SetLogonTime() +{ + m_dwLogonTime = get_dword_time(); +} + +DWORD CLoginData::GetLogonTime() +{ + return m_dwLogonTime; +} + +void CLoginData::SetIP(const char * c_pszIP) +{ + strlcpy(m_szIP, c_pszIP, sizeof(m_szIP)); +} + +const char * CLoginData::GetIP() +{ + return m_szIP; +} + +void CLoginData::SetPlay(bool bOn) +{ + if (bOn) + { + sys_log(0, "SetPlay on %lu %s", GetKey(), m_data.login); + SetLogonTime(); + } + else + sys_log(0, "SetPlay off %lu %s", GetKey(), m_data.login); + + m_bPlay = bOn; + m_lastPlayTime = CClientManager::instance().GetCurrentTime(); +} + +bool CLoginData::IsPlay() +{ + return m_bPlay; +} + +void CLoginData::SetDeleted(bool bSet) +{ + m_bDeleted = bSet; +} + +bool CLoginData::IsDeleted() +{ + return m_bDeleted; +} + +void CLoginData::SetPremium(int * paiPremiumTimes) +{ + thecore_memcpy(m_aiPremiumTimes, paiPremiumTimes, sizeof(m_aiPremiumTimes)); +} + +int CLoginData::GetPremium(BYTE type) +{ + if (type >= PREMIUM_MAX_NUM) + return 0; + + return m_aiPremiumTimes[type]; +} + +int * CLoginData::GetPremiumPtr() +{ + return &m_aiPremiumTimes[0]; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/LoginData.h b/source-server/Srcs/Server/db/src/LoginData.h new file mode 100644 index 000000000..d340f58b3 --- /dev/null +++ b/source-server/Srcs/Server/db/src/LoginData.h @@ -0,0 +1,67 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN_II_DB_LOGINDATA_H__ +#define __INC_METIN_II_DB_LOGINDATA_H__ + +class CLoginData +{ + public: + CLoginData(); + + TAccountTable & GetAccountRef(); + void SetClientKey(const DWORD * c_pdwClientKey); + + const DWORD * GetClientKey(); + void SetKey(DWORD dwKey); + DWORD GetKey(); + + void SetConnectedPeerHandle(DWORD dwHandle); + DWORD GetConnectedPeerHandle(); + + void SetLogonTime(); + DWORD GetLogonTime(); + + void SetIP(const char * c_pszIP); + const char * GetIP(); + + void SetPlay(bool bOn); + bool IsPlay(); + + void SetDeleted(bool bSet); + bool IsDeleted(); + + void SetBillID(DWORD id) { m_dwBillID = id; } + DWORD GetBillID() { return m_dwBillID; } + + void SetBillType(BYTE type) { m_bBillType = type; } + BYTE GetBillType() { return m_bBillType; } + + time_t GetLastPlayTime() { return m_lastPlayTime; } + + void SetPremium(int * paiPremiumTimes); + int GetPremium(BYTE type); + int * GetPremiumPtr(); + + DWORD GetLastPlayerID() const { return m_dwLastPlayerID; } + void SetLastPlayerID(DWORD id) { m_dwLastPlayerID = id; } + + private: + DWORD m_dwKey; + DWORD m_adwClientKey[4]; + DWORD m_dwConnectedPeerHandle; + DWORD m_dwLogonTime; + char m_szIP[MAX_HOST_LENGTH+1]; + bool m_bPlay; + bool m_bDeleted; + + BYTE m_bBillType; + DWORD m_dwBillID; + time_t m_lastPlayTime; + int m_aiPremiumTimes[PREMIUM_MAX_NUM]; + + DWORD m_dwLastPlayerID; + + TAccountTable m_data; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Main.cpp b/source-server/Srcs/Server/db/src/Main.cpp new file mode 100644 index 000000000..77c3d0337 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Main.cpp @@ -0,0 +1,443 @@ +#include "stdafx.h" +#include "Config.h" +#include "Peer.h" +#include "DBManager.h" +#include "ClientManager.h" +#include "GuildManager.h" +#include "ItemAwardManager.h" +#include "HB.h" +#include "PrivManager.h" +#include "MoneyLog.h" +#include "Marriage.h" +#include "Monarch.h" +#include "ItemIDRangeManager.h" +#include + +void SetPlayerDBName(const char* c_pszPlayerDBName); +void SetTablePostfix(const char* c_pszTablePostfix); +int Start(); + +std::string g_stTablePostfix; +std::string g_stLocaleNameColumn = "name"; +std::string g_stLocale = "latin1"; // default: euckr +std::string g_stPlayerDBName = ""; + +bool g_bHotBackup = false; // default: true +BOOL g_test_server = false; + +int g_iPlayerCacheFlushSeconds = 60*7; +int g_iItemCacheFlushSeconds = 60*5; + +int g_iLogoutSeconds = 60*10; + +int g_log = 1; + +// MYSHOP_PRICE_LIST +int g_iItemPriceListTableCacheFlushSeconds = 540; +// END_OF_MYSHOP_PRICE_LIST + +#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version<1000000 +extern const char * _malloc_options; +#endif + +extern void WriteVersion(); + +void emergency_sig(int sig) +{ + if (sig == SIGSEGV) + sys_log(0, "SIGNAL: SIGSEGV"); + else if (sig == SIGUSR1) + sys_log(0, "SIGNAL: SIGUSR1"); + + if (sig == SIGSEGV) + abort(); +} + +int main() +{ + WriteVersion(); + +#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version<1000000 + _malloc_options = "A"; +#endif + + CConfig Config; + CNetPoller poller; + CDBManager DBManager; + CClientManager ClientManager; + PlayerHB player_hb; + CGuildManager GuildManager; + CPrivManager PrivManager; + CMoneyLog MoneyLog; + ItemAwardManager ItemAwardManager; + marriage::CManager MarriageManager; + CMonarch Monarch; + CItemIDRangeManager ItemIDRangeManager; + if (!Start()) + return 1; + + GuildManager.Initialize(); + MarriageManager.Initialize(); + ItemIDRangeManager.Build(); + sys_log(0, "Metin2DBCacheServer Start\n"); + + CClientManager::instance().MainLoop(); + + signal_timer_disable(); + + DBManager.Quit(); + int iCount; + + while (1) + { + iCount = 0; + + iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER); + iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER); + + if (iCount == 0) + break; + + usleep(1000); + sys_log(0, "WAITING_QUERY_COUNT %d", iCount); + } + + return 1; +} + +void emptybeat(LPHEART heart, int pulse) +{ + if (!(pulse % heart->passes_per_sec)) + { + } +} + +// + +// +int Start() +{ + if (!CConfig::instance().LoadFile("conf.txt")) + { + fprintf(stderr, "Loading conf.txt failed.\n"); + return false; + } + + if (!CConfig::instance().GetValue("TEST_SERVER", &g_test_server)) + { + fprintf(stderr, "Real Server\n"); + } + else + fprintf(stderr, "Test Server\n"); + + if (!CConfig::instance().GetValue("LOG", &g_log)) + { + fprintf(stderr, "Log Off"); + g_log= 0; + } + else + { + g_log = 1; + fprintf(stderr, "Log On"); + } + + int tmpValue; + + int heart_beat = 50; + if (!CConfig::instance().GetValue("CLIENT_HEART_FPS", &heart_beat)) + { + fprintf(stderr, "Cannot find CLIENT_HEART_FPS configuration.\n"); + return false; + } + + log_set_expiration_days(3); + + if (CConfig::instance().GetValue("LOG_KEEP_DAYS", &tmpValue)) + { + tmpValue = MINMAX(3, tmpValue, 30); + log_set_expiration_days(tmpValue); + fprintf(stderr, "Setting log keeping days to %d\n", tmpValue); + } + + thecore_init(heart_beat, emptybeat); + signal_timer_enable(60); + + char szBuf[256+1]; + + if (CConfig::instance().GetValue("LOCALE", szBuf, 256)) + { + g_stLocale = szBuf; + sys_log(0, "LOCALE set to %s", g_stLocale.c_str()); + + // CHINA_DISABLE_HOTBACKUP + if ("gb2312" == g_stLocale) + { + sys_log(0, "CIBN_LOCALE: DISABLE_HOTBACKUP"); + g_bHotBackup = false; + } + // END_OF_CHINA_DISABLE_HOTBACKUP + } + + int iDisableHotBackup; + if (CConfig::instance().GetValue("DISABLE_HOTBACKUP", &iDisableHotBackup)) + { + if (iDisableHotBackup) + { + sys_log(0, "CONFIG: DISABLE_HOTBACKUP"); + g_bHotBackup = false; + } + } + + if (!CConfig::instance().GetValue("TABLE_POSTFIX", szBuf, 256)) + { + sys_log(0, "TABLE_POSTFIX not configured use default"); // @warme012 + szBuf[0] = '\0'; + } + + SetTablePostfix(szBuf); + + if (CConfig::instance().GetValue("PLAYER_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iPlayerCacheFlushSeconds, szBuf); + sys_log(0, "PLAYER_CACHE_FLUSH_SECONDS: %d", g_iPlayerCacheFlushSeconds); + } + + if (CConfig::instance().GetValue("ITEM_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iItemCacheFlushSeconds, szBuf); + sys_log(0, "ITEM_CACHE_FLUSH_SECONDS: %d", g_iItemCacheFlushSeconds); + } + + // MYSHOP_PRICE_LIST + if (CConfig::instance().GetValue("ITEM_PRICELIST_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iItemPriceListTableCacheFlushSeconds, szBuf); + sys_log(0, "ITEM_PRICELIST_CACHE_FLUSH_SECONDS: %d", g_iItemPriceListTableCacheFlushSeconds); + } + // END_OF_MYSHOP_PRICE_LIST + + if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256)) + { + DWORD dwVal = 0; str_to_number(dwVal, szBuf); + CClientManager::instance().SetCacheFlushCountLimit(dwVal); + } + + int iIDStart; + if (!CConfig::instance().GetValue("PLAYER_ID_START", &iIDStart)) + { + sys_err("PLAYER_ID_START not configured"); + return false; + } + + CClientManager::instance().SetPlayerIDStart(iIDStart); + + if (CConfig::instance().GetValue("NAME_COLUMN", szBuf, 256)) + { + fprintf(stderr, "%s %s", g_stLocaleNameColumn.c_str(), szBuf); + g_stLocaleNameColumn = szBuf; + } + + char szAddr[64], szDB[64], szUser[64], szPassword[64]; + int iPort; + char line[256+1]; + + if (CConfig::instance().GetValue("SQL_PLAYER", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (player)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_PLAYER, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success PLAYER\n"); + SetPlayerDBName(szDB); + } + else + { + sys_err("SQL_PLAYER not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_ACCOUNT", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (account)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_ACCOUNT, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success ACCOUNT\n"); + } + else + { + sys_err("SQL_ACCOUNT not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_COMMON", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (common)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success COMMON\n"); + } + else + { + sys_err("SQL_COMMON not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_HOTBACKUP", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (hotbackup)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_HOTBACKUP, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } + while (iRetry--); + + fprintf(stderr, "Success HOTBACKUP\n"); + } + else + { + sys_err("SQL_HOTBACKUP not configured"); + return false; + } + + +#ifdef ENABLE_DB_SQL_LOG + if (CConfig::instance().GetValue("SQL_LOG", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (log)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_LOG, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + + fprintf(stderr, "Success LOG\n"); + } + else + { + sys_err("SQL_LOG not configured"); + return false; + } +#endif + + if (!CNetPoller::instance().Create()) + { + sys_err("Cannot create network poller"); + return false; + } + + sys_log(0, "ClientManager initialization.. "); + + if (!CClientManager::instance().Initialize()) + { + sys_log(0, " failed"); + return false; + } + + sys_log(0, " OK"); + + if (!PlayerHB::instance().Initialize()) + { + sys_err("cannot initialize player hotbackup"); + return false; + } + +#ifndef __WIN32__ + signal(SIGUSR1, emergency_sig); +#endif + signal(SIGSEGV, emergency_sig); + return true; +} + +void SetTablePostfix(const char* c_pszTablePostfix) +{ + if (!c_pszTablePostfix || !*c_pszTablePostfix) + g_stTablePostfix = ""; + else + g_stTablePostfix = c_pszTablePostfix; +} + +const char * GetTablePostfix() +{ + return g_stTablePostfix.c_str(); +} + +void SetPlayerDBName(const char* c_pszPlayerDBName) +{ + if (! c_pszPlayerDBName || ! *c_pszPlayerDBName) + g_stPlayerDBName = ""; + else + { + g_stPlayerDBName = c_pszPlayerDBName; + g_stPlayerDBName += "."; + } +} + +const char * GetPlayerDBName() +{ + return g_stPlayerDBName.c_str(); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Main.h b/source-server/Srcs/Server/db/src/Main.h new file mode 100644 index 000000000..00b79ffbd --- /dev/null +++ b/source-server/Srcs/Server/db/src/Main.h @@ -0,0 +1,10 @@ +#ifndef __INC_MAIN_H__ +#define __INC_MAIN_H__ + +int Start(); +void End(); +const char * GetTablePostfix(); +const char * GetPlayerDBName(); + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Makefile b/source-server/Srcs/Server/db/src/Makefile new file mode 100644 index 000000000..22ddad7dc --- /dev/null +++ b/source-server/Srcs/Server/db/src/Makefile @@ -0,0 +1,136 @@ +CC = clang +CXX = clang++ + +DB_VERSION = $(shell cat ../../__REVISION__) +LBITS := $(shell getconf LONG_BIT) + +INCDIR = +LIBDIR = +BINDIR = .. +OBJDIR = .obj +$(shell if [ ! -d $(OBJDIR) ]; then mkdir $(OBJDIR); fi) + +# LIST_OF_CONSTANTS BEGIN +ENABLE_GCC_AUTODEPEND = 1 +ENABLE_STATIC = 0 +# LIST_OF_CONSTANTS END + +# Depend Path File +ifneq ($(ENABLE_GCC_AUTODEPEND), 1) +DEPFILE = Depend +endif + +# Project Flags +CFLAGS = -m32 -g -Wall -O2 -pipe -fno-rtti -fno-exceptions -Wno-long-long -pthread -D_THREAD_SAFE +CFLAGS += -Wno-deprecated-declarations -Wno-nonnull-compare -Wno-deprecated-declarations -Wno-array-bounds -Wno-address +CFLAGS += -Wno-int-in-bool-context -Wno-format-truncation -Wno-deprecated-enum-enum-conversion +CXXFLAGS = -std=c++20 + +# for clang +ifneq '' '$(findstring clang++,$(CXX))' +CFLAGS += -Wno-invalid-source-encoding -Wno-deprecated-anon-enum-enum-conversion -Wno-unknown-warning-option +# for gcc +else ifneq '' '$(findstring g++,$(CXX))' +# for 32bit on 64bit +ifeq ($(LBITS),64) +CFLAGS += -L/usr/local/lib32/gcc12 +CFLAGS += -Wl,-rpath=/usr/local/lib32/gcc12 +else +# for 32bit on 32bit +CXXFLAGS += -Wl,-rpath=/usr/local/lib/gcc12 +endif +endif + +# MySQL +INCDIR += -I/usr/local/include/mysql +ifeq ($(LBITS),64) +LIBS += ../../../Extern/lib/libmysqlclient.a -lz +else +LIBS += /usr/local/lib/mysql/libmysqlclient.a /usr/lib/libz.a +endif + +ifeq ($(ENABLE_STATIC), 1) +CFLAGS += -static +endif + +# Version defines +CFLAGS += -D__USER__=\"$(USER)\" -D__HOSTNAME__=\"$(HOSTNAME)\" -D__PWD__=\"$(PWD)\" -D__DB_VERSION__=\"$(DB_VERSION)\" + +# Boost +INCDIR += -I../../../Extern/include/boost + +# Project Libraries +INCDIR += -I/usr/local/include +INCDIR += -I../../../Extern/include +LIBDIR += -I../../../Extern/lib + +LIBDIR += -L../../libthecore/lib -L../../libsql -L../../libpoly -L../../libgame/lib +LIBS += -lthecore -lsql -lpoly -lgame -lm + +# OpenSSL +ifeq ($(LBITS),64) +LIBS += /usr/lib32/libcrypto.a /usr/lib32/libssl.a +else +LIBS += -lssl -lcrypto +endif + +# PROJECT_SRC_FILES BEGIN +CPPFILE = Config.cpp NetBase.cpp Peer.cpp PeerBase.cpp Main.cpp Lock.cpp DBManager.cpp \ + Cache.cpp LoginData.cpp ClientManager.cpp ClientManagerPlayer.cpp ClientManagerLogin.cpp \ + ClientManagerBoot.cpp ClientManagerParty.cpp ClientManagerGuild.cpp GuildManager.cpp HB.cpp \ + PrivManager.cpp MoneyLog.cpp ItemAwardManager.cpp ClientManagerEventFlag.cpp Marriage.cpp \ + Monarch.cpp ItemIDRangeManager.cpp ClientManagerHorseName.cpp version.cpp \ + ProtoReader.cpp CsvReader.cpp +# PROJECT_SRC_FILES END + +# PROJECT_OBJ_FILES BEGIN +CPPOBJS = $(CPPFILE:%.cpp=$(OBJDIR)/%.o) +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) +CPPDEPS = $(CPPOBJS:%.o=%.d) +endif +# PROJECT_OBJ_FILES END + +# Target Paths +MAIN_TARGET = $(BINDIR)/db_r$(DB_VERSION) + +default: $(MAIN_TARGET) + +$(MAIN_TARGET): $(CPPOBJS) + @echo linking $(MAIN_TARGET) + @$(CXX) $(CFLAGS) $(CXXFLAGS) $(LIBDIR) $(CPPOBJS) $(LIBS) -o $(MAIN_TARGET) + +$(OBJDIR)/%.o: %.cpp + @echo compiling $< + @$(CXX) $(CFLAGS) $(CXXFLAGS) $(INCDIR) -c $< -o $@ +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @$(CXX) -MM -MG -MP $(CFLAGS) $(CXXFLAGS) $(INCDIR) -c $< -o $(OBJDIR)/$*.d + @sed -i '' -e's/$*.o:/$(OBJDIR)\/$*.o:/g' $(OBJDIR)/$*.d +endif + +$(OBJDIR): + @mkdir $(OBJDIR) + +symlink: + @ln -fs db_r$(DB_VERSION) $(BINDIR)/db_symlink + +strip: + @cp $(BINDIR)/db_r$(DB_VERSION) $(BINDIR)/db_r + @strip $(BINDIR)/db_r + +clean: + @rm -f $(CPPOBJS) $(BINDIR)/db_r* + +dep: +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @echo "Note: gcc autodepend is autodetected, so target dep skipped" +else + makedepend -f $(DEPFILE) $(INCDIR) -I/usr/include/c++/3.3 -I/usr/include/c++/4.2 -p$(OBJDIR)/ $(CPPFILE) 2> /dev/null > $(DEPFILE) +endif + +# AUTO_DEPEND_CHECK BEGIN +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) +sinclude $(CPPDEPS) +else +sinclude $(DEPFILE) +endif +# AUTO_DEPEND_CHECK END diff --git a/source-server/Srcs/Server/db/src/Marriage.cpp b/source-server/Srcs/Server/db/src/Marriage.cpp new file mode 100644 index 000000000..c2590e999 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Marriage.cpp @@ -0,0 +1,377 @@ +#include "stdafx.h" +#include "Marriage.h" +#include "Main.h" +#include "DBManager.h" +#include "ClientManager.h" + +namespace marriage +{ + const DWORD WEDDING_LENGTH = 60 * 60; // sec + bool operator < (const TWedding& lhs, const TWedding& rhs) + { + return lhs.dwTime < rhs.dwTime; + } + + bool operator > (const TWedding& lhs, const TWedding& rhs) + { + return lhs.dwTime > rhs.dwTime; + } + + bool operator > (const TWeddingInfo &lhs, const TWeddingInfo& rhs) + { + return lhs.dwTime > rhs.dwTime; + } + + using namespace std; + + CManager::CManager() + { + } + + CManager::~CManager() + { + } + + bool CManager::Initialize() + { + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, love_point, time, is_married, p1.name, p2.name FROM marriage, player%s as p1, player%s as p2 WHERE p1.id = pid1 AND p2.id = pid2", + GetTablePostfix(), GetTablePostfix()); + + auto pmsg_delete(CDBManager::instance().DirectQuery("DELETE FROM marriage WHERE is_married = 0")); + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult * pRes = pmsg->Get(); + sys_log(0, "MarriageList(size=%lu)", pRes->uiNumRows); + + if (pRes->uiNumRows > 0) + { + for (uint uiRow = 0; uiRow != pRes->uiNumRows; ++uiRow) + { + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + + DWORD pid1 = 0; str_to_number(pid1, row[0]); + DWORD pid2 = 0; str_to_number(pid2, row[1]); + int love_point = 0; str_to_number(love_point, row[2]); + DWORD time = 0; str_to_number(time, row[3]); + BYTE is_married = 0; str_to_number(is_married, row[4]); + const char* name1 = row[5]; + const char* name2 = row[6]; + + TMarriage* pMarriage = new TMarriage(pid1, pid2, love_point, time, is_married, name1, name2); + m_Marriages.emplace(pMarriage); + m_MarriageByPID.emplace(pid1, pMarriage); + m_MarriageByPID.emplace(pid2, pMarriage); + + sys_log(0, "Marriage %lu: LP:%d TM:%u ST:%d %10lu:%16s %10lu:%16s ", uiRow, love_point, time, is_married, pid1, name1, pid2, name2); + } + } + return true; + } + + TMarriage* CManager::Get(DWORD dwPlayerID) + { + itertype(m_MarriageByPID) it = m_MarriageByPID.find(dwPlayerID); + + if (it != m_MarriageByPID.end()) + return it->second; + + return NULL; + } + + void Align(DWORD& rPID1, DWORD& rPID2) + { + if (rPID1 > rPID2) + std::swap(rPID1, rPID2); + } + + void CManager::Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2) + { + DWORD now = CClientManager::instance().GetCurrentTime(); + if (IsMarried(dwPID1) || IsMarried(dwPID2)) + { + sys_err("cannot marry already married character. %d - %d", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "INSERT INTO marriage(pid1, pid2, love_point, time) VALUES (%u, %u, 0, %u)", dwPID1, dwPID2, now); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot insert marriage"); + return; + } + + sys_log(0, "MARRIAGE ADD %u %u", dwPID1, dwPID2); + + TMarriage* pMarriage = new TMarriage(dwPID1, dwPID2, 0, now, 0, szName1, szName2); + m_Marriages.emplace(pMarriage); + m_MarriageByPID.emplace(dwPID1, pMarriage); + m_MarriageByPID.emplace(dwPID2, pMarriage); + + TPacketMarriageAdd p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.tMarryTime = now; + strlcpy(p.szName1, szName1, sizeof(p.szName1)); + strlcpy(p.szName2, szName2, sizeof(p.szName2)); + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_ADD, &p, sizeof(p)); + } + + void CManager::Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried) + { + TMarriage* pMarriage = Get(dwPID1); + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + sys_err("not under marriage : %u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET love_point = %d, is_married = %d WHERE pid1 = %u AND pid2 = %u", + iLovePoint, byMarried, pMarriage->pid1, pMarriage->pid2); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "cannot update marriage : PID:%u %u", dwPID1, dwPID2); // @warme012 + return; + } + + sys_log(0, "MARRIAGE UPDATE PID:%u %u LP:%u ST:%d", dwPID1, dwPID2, iLovePoint, byMarried); + pMarriage->love_point = iLovePoint; + pMarriage->is_married = byMarried; + + TPacketMarriageUpdate p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p)); + } + + void CManager::Remove(DWORD dwPID1, DWORD dwPID2) + { + TMarriage* pMarriage = Get(dwPID1); + + if (pMarriage) + { + sys_log(0, "Break Marriage pid1 %d pid2 %d Other %d", dwPID1, dwPID2, pMarriage->GetOther(dwPID1)); + } + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + itertype(m_MarriageByPID) it = m_MarriageByPID.begin(); + + for (; it != m_MarriageByPID.end(); ++it) + { + sys_log(0, "Marriage List pid1 %d pid2 %d", it->second->pid1, it->second->pid2); + } + sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM marriage WHERE pid1 = %u AND pid2 = %u", dwPID1, dwPID2); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot delete marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + sys_log(0, "MARRIAGE REMOVE PID:%u %u", dwPID1, dwPID2); + + m_Marriages.erase(pMarriage); + m_MarriageByPID.erase(dwPID1); + m_MarriageByPID.erase(dwPID2); + + TPacketMarriageRemove p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_REMOVE, &p, sizeof(p)); + + delete pMarriage; + } + + void CManager::EngageToMarriage(DWORD dwPID1, DWORD dwPID2) + { + TMarriage* pMarriage = Get(dwPID1); + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + if (pMarriage->is_married) + { + sys_err("already married, cannot change engage to marry : PID:%u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET is_married = 1 WHERE pid1 = %u AND pid2 = %u", + pMarriage->pid1, pMarriage->pid2); + + auto pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot change engage to marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + sys_log(0, "MARRIAGE ENGAGE->MARRIAGE PID:%u %u", dwPID1, dwPID2); + pMarriage->is_married = 1; + + TPacketMarriageUpdate p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p)); + } + + void CManager::OnSetup(CPeer* peer) + { + for (itertype(m_Marriages) it = m_Marriages.begin(); it != m_Marriages.end(); ++it) + { + TMarriage* pMarriage = *it; + + { + TPacketMarriageAdd p; + p.dwPID1 = pMarriage->pid1; + p.dwPID2 = pMarriage->pid2; + p.tMarryTime = pMarriage->time; + strlcpy(p.szName1, pMarriage->name1.c_str(), sizeof(p.szName1)); + strlcpy(p.szName2, pMarriage->name2.c_str(), sizeof(p.szName2)); + peer->EncodeHeader(HEADER_DG_MARRIAGE_ADD, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + + { + TPacketMarriageUpdate p; + p.dwPID1 = pMarriage->pid1; + p.dwPID2 = pMarriage->pid2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + peer->EncodeHeader(HEADER_DG_MARRIAGE_UPDATE, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + } + + for (itertype(m_mapRunningWedding) it = m_mapRunningWedding.begin(); it != m_mapRunningWedding.end(); ++it) + { + const TWedding& t = it->second; + + TPacketWeddingReady p; + p.dwPID1 = t.dwPID1; + p.dwPID2 = t.dwPID2; + p.dwMapIndex = t.dwMapIndex; + + peer->EncodeHeader(HEADER_DG_WEDDING_READY, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + + TPacketWeddingStart p2; + p2.dwPID1 = t.dwPID1; + p2.dwPID2 = t.dwPID2; + + peer->EncodeHeader(HEADER_DG_WEDDING_START, 0, sizeof(p2)); + peer->Encode(&p2, sizeof(p2)); + } + } + + void CManager::ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2) + { + DWORD dwStartTime = CClientManager::instance().GetCurrentTime(); + m_pqWeddingStart.push(TWedding(dwStartTime + 5, dwMapIndex, dwPID1, dwPID2)); + } + + void CManager::EndWedding(DWORD dwPID1, DWORD dwPID2) + { + itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(dwPID1, dwPID2)); + if (it == m_mapRunningWedding.end()) + { + sys_err("try to end wedding %u %u", dwPID1, dwPID2); + return; + } + + TWedding& w = it->second; + + TPacketWeddingEnd p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p)); + m_mapRunningWedding.erase(it); + } + + void CManager::Update() + { + DWORD now = CClientManager::instance().GetCurrentTime(); + + if (!m_pqWeddingEnd.empty()) + { + while (!m_pqWeddingEnd.empty() && m_pqWeddingEnd.top().dwTime <= now) + { + TWeddingInfo wi = m_pqWeddingEnd.top(); + m_pqWeddingEnd.pop(); + + itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(wi.dwPID1, wi.dwPID2)); + if (it == m_mapRunningWedding.end()) + continue; + + TWedding& w = it->second; + + TPacketWeddingEnd p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p)); + m_mapRunningWedding.erase(it); + + itertype(m_MarriageByPID) it_marriage = m_MarriageByPID.find(w.dwPID1); + + if (it_marriage != m_MarriageByPID.end()) + { + TMarriage* pMarriage = it_marriage->second; + if (!pMarriage->is_married) + { + Remove(pMarriage->pid1, pMarriage->pid2); + } + } + } + } + if (!m_pqWeddingStart.empty()) + { + while (!m_pqWeddingStart.empty() && m_pqWeddingStart.top().dwTime <= now) + { + TWedding w = m_pqWeddingStart.top(); + m_pqWeddingStart.pop(); + + TPacketWeddingStart p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_START, &p, sizeof(p)); + + w.dwTime += WEDDING_LENGTH; + m_pqWeddingEnd.push(TWeddingInfo(w.dwTime, w.dwPID1, w.dwPID2)); + m_mapRunningWedding.emplace(make_pair(w.dwPID1, w.dwPID2), w); + } + } + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Marriage.h b/source-server/Srcs/Server/db/src/Marriage.h new file mode 100644 index 000000000..8cfc568e4 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Marriage.h @@ -0,0 +1,114 @@ +// vim: ts=4 sw=4 +#ifndef __MARRIAGE_H +#define __MARRIAGE_H + +#include +#include +#include + +#include "Peer.h" + +namespace marriage +{ + struct TWeddingInfo + { + DWORD dwTime; + DWORD dwPID1; + DWORD dwPID2; + TWeddingInfo(DWORD time, DWORD pid1, DWORD pid2) + : dwTime(time), + dwPID1(pid1), + dwPID2(pid2) + { + } + }; + + struct TWedding + { + DWORD dwTime; + DWORD dwMapIndex; + DWORD dwPID1; + DWORD dwPID2; + + TWedding(DWORD time, DWORD dwMapIndex, DWORD pid1, DWORD pid2) + : dwTime(time), + dwMapIndex(dwMapIndex), + dwPID1(pid1), + dwPID2(pid2) + { + } + }; + + extern bool operator < (const TWedding& lhs, const TWedding& rhs); + extern bool operator > (const TWedding& lhs, const TWedding& rhs); + extern bool operator > (const TWeddingInfo& lhs, const TWeddingInfo& rhs); + + struct TMarriage + { + DWORD pid1; + DWORD pid2; + int love_point; + DWORD time; + BYTE is_married; + std::string name1; + std::string name2; + + TMarriage(DWORD _pid1, DWORD _pid2, int _love_point, DWORD _time, BYTE _is_married, const char* name1, const char* name2) + : pid1(_pid1), pid2(_pid2), love_point(_love_point), time(_time), is_married(_is_married), name1(name1), name2(name2) + { + } + + DWORD GetOther(DWORD PID) + { + if (pid1 == PID) + return pid2; + + if (pid2 == PID) + return pid1; + + return 0; + } + }; + + class CManager : public singleton + { + public: + CManager(); + virtual ~CManager(); + + bool Initialize(); + + TMarriage* Get(DWORD dwPlayerID); + bool IsMarried(DWORD dwPlayerID) + { + return Get(dwPlayerID) != NULL; + } + + //void Reserve(DWORD dwPID1, DWORD dwPID2); + void Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2); + void Remove(DWORD dwPID1, DWORD dwPID2); + void Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried); + + void EngageToMarriage(DWORD dwPID1, DWORD dwPID2); + + void ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2); + void EndWedding(DWORD dwPID1, DWORD dwPID2); + + void OnSetup(CPeer* peer); + + void Update(); + + private: + std::set m_Marriages; + std::map m_MarriageByPID; + + std::priority_queue, std::greater > m_pqWeddingStart; + + std::priority_queue, std::greater > m_pqWeddingEnd; + + std::map, TWedding> m_mapRunningWedding; + }; +} + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Monarch.cpp b/source-server/Srcs/Server/db/src/Monarch.cpp new file mode 100644 index 000000000..2820fcf13 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Monarch.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "Monarch.h" +#include "../../common/utils.h" +#include "Main.h" +#include "ClientManager.h" + +extern int g_test_server; + +CMonarch::CMonarch() +{ + memset(&m_MonarchInfo, 0, sizeof(MonarchInfo)); +} + +CMonarch::~CMonarch() +{ +} + +bool CMonarch::VoteMonarch(DWORD pid, DWORD selectdpid) +{ + MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid); + + if (it == m_map_MonarchElection.end()) + { + MonarchElectionInfo * p = new MonarchElectionInfo; + p->pid = pid; + p->selectedpid= selectdpid; + m_map_MonarchElection.emplace(pid, p); + + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO monarch_election(pid, selectedpid, electiondata) VALUES(%d, %d, now())", pid, selectdpid); + + CDBManager::instance().AsyncQuery(szQuery); + return 1; + } + + return 0; +} + +void CMonarch::ElectMonarch() +{ + int size = GetVecMonarchCandidacy().size(); + + int * s = new int[size]; + + itertype(m_map_MonarchElection) it = m_map_MonarchElection.begin(); + + int idx = 0; + + for (; it != m_map_MonarchElection.end(); ++it) + { + if ((idx = GetCandidacyIndex(it->second->pid)) < 0) + continue; + + ++s[idx]; + + if (g_test_server) + sys_log (0, "[MONARCH_VOTE] pid(%d) come to vote candidacy pid(%d)", it->second->pid, m_vec_MonarchCandidacy[idx].pid); + } + + delete [] s; +} + +bool CMonarch::IsCandidacy(DWORD pid) +{ + VEC_MONARCHCANDIDACY::iterator it = m_vec_MonarchCandidacy.begin(); + + for (; it != m_vec_MonarchCandidacy.end(); ++it) + { + if (it->pid == pid) + return false; + } + + return true; +} + +bool CMonarch::AddCandidacy(DWORD pid, const char * name) +{ + if (IsCandidacy(pid) == false) + return false; + + MonarchCandidacy info; + + info.pid = pid; + strlcpy(info.name, name, sizeof(info.name)); + m_vec_MonarchCandidacy.emplace_back(info); + + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO monarch_candidacy(pid, date) VALUES(%d, now())", pid); + + CDBManager::instance().AsyncQuery(szQuery); + return true; +} + +bool CMonarch::DelCandidacy(const char * name) +{ + itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin(); + for (; it != m_vec_MonarchCandidacy.end(); ++it) + { + if (0 == strncmp(it->name, name, sizeof(it->name))) + { + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "DELETE FROM monarch_candidacy WHERE pid=%d ", it->pid); + CDBManager::instance().AsyncQuery(szQuery); + + m_vec_MonarchCandidacy.erase (it); + return true; + } + } + return false; +} + +bool CMonarch::IsMonarch(int Empire, DWORD pid) +{ + if (m_MonarchInfo.pid[Empire] != pid) + return false; + return true; +} + +bool CMonarch::AddMoney(int Empire, int64_t Money) +{ + if (m_MonarchInfo.money[Empire] + Money > 2000000000) + return true; + + m_MonarchInfo.money[Empire] += Money; + + int64_t Money64 = m_MonarchInfo.money[Empire]; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch SET money=%lld WHERE empire=%d", Money64, Empire); + + CDBManager::instance().AsyncQuery(szQuery); + + return true; +} + +bool CMonarch::DecMoney(int Empire, int64_t Money) +{ + if (m_MonarchInfo.money[Empire] - Money < 0) + return false; + m_MonarchInfo.money[Empire] -= Money; + + int64_t Money64 = m_MonarchInfo.money[Empire]; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch SET money=%lld WHERE empire=%d", Money64, Empire); + + CDBManager::instance().AsyncQuery(szQuery); + return true; +} + +bool CMonarch::TakeMoney(int Empire, DWORD pid, int64_t Money) +{ + if (IsMonarch(Empire, pid) == false) + return false; + + if (m_MonarchInfo.money[Empire] < Money) + return false; + + m_MonarchInfo.money[Empire] -= Money; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "UPDATE monarch SET money=%lld WHERE empire=%d", m_MonarchInfo.money[Empire], Empire); + + CDBManager::instance().AsyncQuery(szQuery); + + if (g_test_server) + sys_log(0, "[MONARCH] Take money empire(%d) money(%lld)", Empire, m_MonarchInfo.money[Empire]); + return true; +} + +bool CMonarch::LoadMonarch() +{ + MonarchInfo * p = &m_MonarchInfo; + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT a.empire, a.pid, b.name, a.money, a.windate FROM monarch a, player%s b WHERE a.pid=b.id", GetTablePostfix()); + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows == 0) + return false; + + MYSQL_ROW row; + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != nullptr) + { + int idx = 0; + int Empire = 0; str_to_number(Empire, row[idx++]); + + str_to_number(p->pid[Empire], row[idx++]); + strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); + + str_to_number(p->money[Empire], row[idx++]); + strlcpy(p->date[Empire], row[idx++], sizeof(p->date[Empire])); + + if (g_test_server) + sys_log(0, "[LOAD_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); + } + + return true; +} + +bool CMonarch::SetMonarch(const char * name) +{ + MonarchInfo * p = &m_MonarchInfo; + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT player_index.empire, player.id, player.name, player.gold FROM player JOIN player_index ON player_index.id = player.account_id WHERE player.name = '%s'", name); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + if (pMsg->Get()->uiNumRows == 0) + return false; + + MYSQL_ROW row; + int Empire = 0; + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != nullptr) + { + int idx = 0; + str_to_number(Empire, row[idx++]); + + str_to_number(p->pid[Empire], row[idx++]); + strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); + p->money[Empire] = atoll(row[idx++]); + + if (g_test_server) + sys_log(0, "[Set_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); + } + + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO monarch (empire, name, windate, money) VALUES(%d, %d, now(), %lld)", Empire, p->pid[Empire], p->money[Empire]); + + CDBManager::instance().AsyncQuery(szQuery, SQL_PLAYER); + return true; +} + +bool CMonarch::DelMonarch(int Empire) +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM monarch WHERE empire=%d", Empire); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + if (pMsg->Get()->uiNumRows == 0) + return false; + + memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire])); + m_MonarchInfo.money[Empire] = 0; + m_MonarchInfo.pid[Empire] = 0; + return true; +} + +bool CMonarch::DelMonarch(const char * name) +{ + for (int n = 1; n < 4; ++n) + { + if (0 == strncmp(m_MonarchInfo.name[n], name, sizeof(m_MonarchInfo.name[n]))) + { + char szQuery[256]; + + int Empire = n; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM monarch WHERE empire=%d", Empire); + + auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + if (pMsg->Get()->uiNumRows == 0) + { + sys_err(" DirectQuery failed(%s)", szQuery); + return false; + } + + memset(m_MonarchInfo.name[Empire], 0, 32); + m_MonarchInfo.money[Empire] = 0; + m_MonarchInfo.pid[Empire] = 0; + return true; + } + } + + return false; +} + +int CMonarch::GetCandidacyIndex(DWORD pid) +{ + itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin(); + + for (int n = 0; it != m_vec_MonarchCandidacy.end(); ++it, ++n) + { + if (it->pid == pid) + return n; + } + + return -1; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Monarch.h b/source-server/Srcs/Server/db/src/Monarch.h new file mode 100644 index 000000000..3da6f700c --- /dev/null +++ b/source-server/Srcs/Server/db/src/Monarch.h @@ -0,0 +1,71 @@ +// vim: ts=4 sw=4 +#ifndef METIN2_MONARCH_H +#define METIN2_MONARCH_H + +#include "../../libthecore/include/stdafx.h" +#include +#include +#include "../../common/singleton.h" +#include "../../common/tables.h" + +class CMonarch : public singleton +{ + public: + typedef std::map MAP_MONARCHELECTION; + typedef std::vector VEC_MONARCHCANDIDACY; + + CMonarch(); + virtual ~CMonarch(); + + bool VoteMonarch(DWORD pid, DWORD selectedpid); + void ElectMonarch(); + + bool IsCandidacy(DWORD pid); + bool AddCandidacy(DWORD pid, const char * name); + bool DelCandidacy(const char * name); + + bool LoadMonarch(); + bool SetMonarch(const char * name); + bool DelMonarch(int Empire); + bool DelMonarch(const char * name); + + bool IsMonarch(int Empire, DWORD pid); + bool AddMoney(int Empire, int64_t Money); + bool DecMoney(int Empire, int64_t Money); + bool TakeMoney(int Empire, DWORD pid, int64_t Money); + + TMonarchInfo* GetMonarch() + { + return &m_MonarchInfo; + } + + VEC_MONARCHCANDIDACY& GetVecMonarchCandidacy() + { + return m_vec_MonarchCandidacy; + } + + size_t MonarchCandidacySize() + { + return m_vec_MonarchCandidacy.size(); + } + + private: + int GetCandidacyIndex(DWORD pid); + + MAP_MONARCHELECTION m_map_MonarchElection; + VEC_MONARCHCANDIDACY m_vec_MonarchCandidacy; + TMonarchInfo m_MonarchInfo; + + MonarchElectionInfo* GetMonarchElection(DWORD pid) + { + MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid); + + if (it != m_map_MonarchElection.end()) + return it->second; + + return NULL; + } +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/MoneyLog.cpp b/source-server/Srcs/Server/db/src/MoneyLog.cpp new file mode 100644 index 000000000..5247ed5ba --- /dev/null +++ b/source-server/Srcs/Server/db/src/MoneyLog.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "MoneyLog.h" +#include "ClientManager.h" +#include "Peer.h" + +CMoneyLog::CMoneyLog() +{ +} + +CMoneyLog::~CMoneyLog() +{ +} + +void CMoneyLog::Save() +{ + CPeer* peer = CClientManager::instance().GetAnyPeer(); + if (!peer) + return; + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType ++) + { + typeof(m_MoneyLogContainer[bType].begin()) it; + for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it) + { + //bType; + TPacketMoneyLog p; + p.type = bType; + p.vnum = it->first; + p.gold = it->second; + peer->EncodeHeader(HEADER_DG_MONEY_LOG, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + m_MoneyLogContainer[bType].clear(); + } + /* + CPeer* peer = GetPeer(); + + peer-> + + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++) + { + //"INSERT INTO money_log%s VALUES('%s', %d, %d, %d)", CClientManager::instance().GetTablePostfix(), + typeof(m_MoneyLogContainer[bType].begin()) it; + for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it) + { + typeof(it->second.begin()) + } + } + + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++) + m_MoneyLogContainer[bType].clear() + */ +} + +void CMoneyLog::AddLog(BYTE bType, DWORD dwVnum, int iGold) +{ + m_MoneyLogContainer[bType][dwVnum] += iGold; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/MoneyLog.h b/source-server/Srcs/Server/db/src/MoneyLog.h new file mode 100644 index 000000000..9f44a768b --- /dev/null +++ b/source-server/Srcs/Server/db/src/MoneyLog.h @@ -0,0 +1,21 @@ +// vim: ts=8 sw=4 +#ifndef __INC_MONEY_LOG +#define __INC_MONEY_LOG + +#include + +class CMoneyLog : public singleton +{ + public: + CMoneyLog(); + virtual ~CMoneyLog(); + + void Save(); + void AddLog(BYTE bType, DWORD dwVnum, int iGold); + + private: + std::map m_MoneyLogContainer[MONEY_LOG_TYPE_MAX_NUM]; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/NetBase.cpp b/source-server/Srcs/Server/db/src/NetBase.cpp new file mode 100644 index 000000000..281bda0e8 --- /dev/null +++ b/source-server/Srcs/Server/db/src/NetBase.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "NetBase.h" +#include "Config.h" +#include "ClientManager.h" + +LPFDWATCH CNetBase::m_fdWatcher = NULL; + +CNetBase::CNetBase() +{ +} + +CNetBase::~CNetBase() +{ +} + +CNetPoller::CNetPoller() +{ +} + +CNetPoller::~CNetPoller() +{ + Destroy(); +} + +bool CNetPoller::Create() +{ + sys_log(1, "NetPoller::Create()"); + + if (m_fdWatcher) + return true; + + m_fdWatcher = fdwatch_new(512); + + if (!m_fdWatcher) + { + Destroy(); + return false; + } + + return true; +} + +void CNetPoller::Destroy() +{ + sys_log(1, "NetPoller::Destroy()"); + + if (m_fdWatcher) + { + fdwatch_delete(m_fdWatcher); + m_fdWatcher = NULL; + } + + thecore_destroy(); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/NetBase.h b/source-server/Srcs/Server/db/src/NetBase.h new file mode 100644 index 000000000..309d45931 --- /dev/null +++ b/source-server/Srcs/Server/db/src/NetBase.h @@ -0,0 +1,26 @@ +// vim: ts=8 sw=4 +#ifndef __INC_NETWORKBASE_H__ +#define __INC_NETWORKBASE_H__ + +class CNetBase +{ + public: + CNetBase(); + virtual ~CNetBase(); + + protected: + static LPFDWATCH m_fdWatcher; +}; + +class CNetPoller : public CNetBase, public singleton +{ + public: + CNetPoller(); + virtual ~CNetPoller(); + + bool Create(); + void Destroy(); +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Peer.cpp b/source-server/Srcs/Server/db/src/Peer.cpp new file mode 100644 index 000000000..d54f0e218 --- /dev/null +++ b/source-server/Srcs/Server/db/src/Peer.cpp @@ -0,0 +1,196 @@ +#include "stdafx.h" +#include "Peer.h" +#include "ItemIDRangeManager.h" + +CPeer::CPeer() +{ + m_state = 0; + m_bChannel = 0; + m_dwHandle = 0; + m_dwUserCount = 0; + m_wListenPort = 0; + m_wP2PPort = 0; + + memset(m_alMaps, 0, sizeof(m_alMaps)); + + m_itemRange.dwMin = m_itemRange.dwMax = m_itemRange.dwUsableItemIDMin = 0; + m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0; +} + +CPeer::~CPeer() +{ + Close(); +} + +void CPeer::OnAccept() +{ + m_state = STATE_PLAYING; + + static DWORD current_handle = 0; + m_dwHandle = ++current_handle; + + sys_log(0, "Connection accepted. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd); +} + +void CPeer::OnConnect() +{ + sys_log(0, "Connection established. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd); + m_state = STATE_PLAYING; +} + +void CPeer::OnClose() +{ + m_state = STATE_CLOSE; + + sys_log(0, "Connection closed. (host: %s)", m_host); + sys_log(0, "ItemIDRange: returned. %u ~ %u", m_itemRange.dwMin, m_itemRange.dwMax); + + CItemIDRangeManager::instance().UpdateRange(m_itemRange.dwMin, m_itemRange.dwMax); + + m_itemRange.dwMin = 0; + m_itemRange.dwMax = 0; + m_itemRange.dwUsableItemIDMin = 0; +} + +DWORD CPeer::GetHandle() +{ + return m_dwHandle; +} + +DWORD CPeer::GetUserCount() +{ + return m_dwUserCount; +} + +void CPeer::SetUserCount(DWORD dwCount) +{ + m_dwUserCount = dwCount; +} + +bool CPeer::PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data) +{ + if (GetRecvLength() < iBytesProceed + 9) + return false; + + const char * buf = (const char *) GetRecvBuffer(); + buf += iBytesProceed; + + header = *(buf++); + + dwHandle = *((DWORD *) buf); + buf += sizeof(DWORD); + + dwLength = *((DWORD *) buf); + buf += sizeof(DWORD); + + //sys_log(0, "%d header %d handle %u length %u", GetRecvLength(), header, dwHandle, dwLength); + if (iBytesProceed + dwLength + 9 > (DWORD) GetRecvLength()) + { + sys_log(0, "PeekPacket: not enough buffer size: len %u, recv %d", + 9+dwLength, GetRecvLength()-iBytesProceed); + return false; + } + + *data = buf; + iBytesProceed += dwLength + 9; + return true; +} + +void CPeer::EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize) +{ + HEADER h; + + sys_log(1, "EncodeHeader %u handle %u size %u", header, dwHandle, dwSize); + + h.bHeader = header; + h.dwHandle = dwHandle; + h.dwSize = dwSize; + + Encode(&h, sizeof(HEADER)); +} + +void CPeer::EncodeReturn(BYTE header, DWORD dwHandle) +{ + EncodeHeader(header, dwHandle, 0); +} + +int CPeer::Send() +{ + if (m_state == STATE_CLOSE) + return -1; + + return (CPeerBase::Send()); +} + +void CPeer::SetP2PPort(WORD wPort) +{ + m_wP2PPort = wPort; +} + +void CPeer::SetMaps(long * pl) +{ + thecore_memcpy(m_alMaps, pl, sizeof(m_alMaps)); +} + +void CPeer::SendSpareItemIDRange() +{ + if (m_itemSpareRange.dwMin == 0 || m_itemSpareRange.dwMax == 0 || m_itemSpareRange.dwUsableItemIDMin == 0) + { + EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable)); + Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable)); + } + else + { + SetItemIDRange(m_itemSpareRange); + + if (SetSpareItemIDRange(CItemIDRangeManager::instance().GetRange()) == false) + { + sys_log(0, "ItemIDRange: spare range set error"); + m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0; + } + + EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable)); + Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable)); + } +} + +bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange) +{ + if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; + + m_itemRange = itemRange; + sys_log(0, "ItemIDRange: SET %s %u ~ %u start: %u", GetPublicIP(), m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin); + + return true; +} + +bool CPeer::SetSpareItemIDRange(TItemIDRangeTable itemRange) +{ + if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; + + m_itemSpareRange = itemRange; + sys_log(0, "ItemIDRange: SPARE SET %s %u ~ %u start: %u", GetPublicIP(), m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, + m_itemSpareRange.dwUsableItemIDMin); + + return true; +} + +bool CPeer::CheckItemIDRangeCollision(TItemIDRangeTable itemRange) +{ + if (m_itemRange.dwMin < itemRange.dwMax && m_itemRange.dwMax > itemRange.dwMin) + { + sys_err("ItemIDRange: Collision!! this %u ~ %u check %u ~ %u", + m_itemRange.dwMin, m_itemRange.dwMax, itemRange.dwMin, itemRange.dwMax); + return false; + } + + if (m_itemSpareRange.dwMin < itemRange.dwMax && m_itemSpareRange.dwMax > itemRange.dwMin) + { + sys_err("ItemIDRange: Collision with spare range this %u ~ %u check %u ~ %u", + m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, itemRange.dwMin, itemRange.dwMax); + return false; + } + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/Peer.h b/source-server/Srcs/Server/db/src/Peer.h new file mode 100644 index 000000000..53f20132c --- /dev/null +++ b/source-server/Srcs/Server/db/src/Peer.h @@ -0,0 +1,81 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PEER_H__ +#define __INC_PEER_H__ + +#include "PeerBase.h" +#include "../../common/CommonDefines.h" + +class CPeer : public CPeerBase +{ + protected: + virtual void OnAccept(); + virtual void OnClose(); + virtual void OnConnect(); + + public: +#pragma pack(1) + typedef struct _header + { + BYTE bHeader; + DWORD dwHandle; + DWORD dwSize; + } HEADER; +#pragma pack() + enum EState + { + STATE_CLOSE = 0, + STATE_PLAYING = 1 + }; + + CPeer(); + virtual ~CPeer(); + + void EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize); + bool PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data); + void EncodeReturn(BYTE header, DWORD dwHandle); + + void ProcessInput(); + int Send(); + + DWORD GetHandle(); + DWORD GetUserCount(); + void SetUserCount(DWORD dwCount); + + void SetPublicIP(const char * ip) { m_stPublicIP = ip; } + const char * GetPublicIP() { return m_stPublicIP.c_str(); } + + void SetChannel(BYTE bChannel) { m_bChannel = bChannel; } + BYTE GetChannel() { return m_bChannel; } + + void SetListenPort(WORD wPort) { m_wListenPort = wPort; } + WORD GetListenPort() { return m_wListenPort; } + + void SetP2PPort(WORD wPort); + WORD GetP2PPort() { return m_wP2PPort; } + + void SetMaps(long* pl); + long * GetMaps() { return &m_alMaps[0]; } + + bool SetItemIDRange(TItemIDRangeTable itemRange); + bool SetSpareItemIDRange(TItemIDRangeTable itemRange); + bool CheckItemIDRangeCollision(TItemIDRangeTable itemRange); + void SendSpareItemIDRange(); + + private: + int m_state; + + BYTE m_bChannel; + DWORD m_dwHandle; + DWORD m_dwUserCount; + WORD m_wListenPort; + WORD m_wP2PPort; + long m_alMaps[MAP_ALLOW_LIMIT]; + + TItemIDRangeTable m_itemRange; + TItemIDRangeTable m_itemSpareRange; + + std::string m_stPublicIP; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/PeerBase.cpp b/source-server/Srcs/Server/db/src/PeerBase.cpp new file mode 100644 index 000000000..1895b3dd9 --- /dev/null +++ b/source-server/Srcs/Server/db/src/PeerBase.cpp @@ -0,0 +1,230 @@ +#include "stdafx.h" +#include "PeerBase.h" +#include "../../common/CommonDefines.h" + +CPeerBase::CPeerBase() : m_fd(INVALID_SOCKET), m_BytesRemain(0), m_outBuffer(NULL), m_inBuffer(NULL) +{ +} + +CPeerBase::~CPeerBase() +{ + Destroy(); +} + +void CPeerBase::Disconnect() +{ + if (m_fd != INVALID_SOCKET) + { + fdwatch_del_fd(m_fdWatcher, m_fd); + + socket_close(m_fd); + m_fd = INVALID_SOCKET; + } +} + +void CPeerBase::Destroy() +{ + Disconnect(); + + if (m_outBuffer) + { + buffer_delete(m_outBuffer); + m_outBuffer = NULL; + } + + if (m_inBuffer) + { + buffer_delete(m_inBuffer); + m_inBuffer = NULL; + } +} + +bool CPeerBase::Accept(socket_t fd_accept) +{ + struct sockaddr_in peer; + + if ((m_fd = socket_accept(fd_accept, &peer)) == INVALID_SOCKET) + { + Destroy(); + return false; + } + +#ifdef ENABLE_PORT_SECURITY + // refuse if remote host != localhost (only the same machine must be able to connect in here) + std::string targetIP = inet_ntoa(peer.sin_addr); + if (targetIP.rfind("127.0.0.1", 0) && targetIP.rfind("192.168.", 0) && targetIP.rfind("10.", 0)) + { + sys_log(0, "BLOCK CONNECTION FROM %s", inet_ntoa(peer.sin_addr)); + Destroy(); + return false; + } +#endif + + //socket_block(m_fd); + socket_sndbuf(m_fd, 233016); + socket_rcvbuf(m_fd, 233016); + + strlcpy(m_host, inet_ntoa(peer.sin_addr), sizeof(m_host)); + m_wPort = peer.sin_port; + + m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE); + m_inBuffer = buffer_new(MAX_INPUT_LEN); + + if (!m_outBuffer || !m_inBuffer) + { + Destroy(); + return false; + } + + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false); + + OnAccept(); + sys_log(0, "ACCEPT FROM %s", inet_ntoa(peer.sin_addr)); + return true; +} + +bool CPeerBase::Connect(const char* host, WORD port) +{ + strlcpy(m_host, host, sizeof(m_host)); + m_wPort = port; + + if ((m_fd = socket_connect(host, port)) == INVALID_SOCKET) + return false; + + m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE); + + if (!m_outBuffer) + { + Destroy(); + return false; + } + + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false); + + OnConnect(); + return true; +} + +void CPeerBase::Close() +{ + OnClose(); +} + +void CPeerBase::EncodeBYTE(BYTE b) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &b, 1); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::EncodeWORD(WORD w) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &w, 2); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::EncodeDWORD(DWORD dw) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &dw, 4); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::Encode(const void* data, DWORD size) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, data, size); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +int CPeerBase::Recv() +{ + if (!m_inBuffer) + { + sys_err("input buffer nil"); + return -1; + } + + buffer_adjust_size(m_inBuffer, MAX_INPUT_LEN >> 2); + int bytes_to_read = buffer_has_space(m_inBuffer); + ssize_t bytes_read = socket_read(m_fd, (char *) buffer_write_peek(m_inBuffer), bytes_to_read); + + if (bytes_read < 0) + { + sys_err("socket_read failed %s", strerror(errno)); + return -1; + } + else if (bytes_read == 0) + return 0; + + buffer_write_proceed(m_inBuffer, bytes_read); + m_BytesRemain = buffer_size(m_inBuffer); + return 1; +} + +void CPeerBase::RecvEnd(int proceed_bytes) +{ + buffer_read_proceed(m_inBuffer, proceed_bytes); + m_BytesRemain = buffer_size(m_inBuffer); +} + +int CPeerBase::GetRecvLength() +{ + return m_BytesRemain; +} + +const void * CPeerBase::GetRecvBuffer() +{ + return buffer_read_peek(m_inBuffer); +} + +int CPeerBase::GetSendLength() +{ + return buffer_size(m_outBuffer); +} + +int CPeerBase::Send() +{ + if (buffer_size(m_outBuffer) <= 0) + return 0; + + int iBufferLeft = fdwatch_get_buffer_size(m_fdWatcher, m_fd); + int iBytesToWrite = MIN(iBufferLeft, buffer_size(m_outBuffer)); + + if (iBytesToWrite == 0) + return 0; + + int result = socket_write(m_fd, (const char *) buffer_read_peek(m_outBuffer), iBytesToWrite); + + if (result == 0) + { + buffer_read_proceed(m_outBuffer, iBytesToWrite); + + if (buffer_size(m_outBuffer) != 0) + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); + } + + return (result); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/PeerBase.h b/source-server/Srcs/Server/db/src/PeerBase.h new file mode 100644 index 000000000..17e471ccc --- /dev/null +++ b/source-server/Srcs/Server/db/src/PeerBase.h @@ -0,0 +1,73 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PEERBASE_H__ +#define __INC_PEERBASE_H__ + +#include "NetBase.h" + +class CPeerBase : public CNetBase +{ + public: + enum + { + MAX_HOST_LENGTH = 30, + MAX_INPUT_LEN = 1024 * 1024 * 2, + DEFAULT_PACKET_BUFFER_SIZE = 1024 * 1024 * 2 + }; + + protected: + virtual void OnAccept() = 0; + virtual void OnConnect() = 0; + virtual void OnClose() = 0; + + public: + bool Accept(socket_t accept_fd); + bool Connect(const char* host, WORD port); + void Close(); + + public: + CPeerBase(); + virtual ~CPeerBase(); + + void Disconnect(); + void Destroy(); + + socket_t GetFd() { return m_fd; } + + void EncodeBYTE(BYTE b); + void EncodeWORD(WORD w); + void EncodeDWORD(DWORD dw); + void Encode(const void* data, DWORD size); + int Send(); + + template>* = nullptr> + void Encode(const T& c_pvData) { + Encode(&c_pvData, sizeof(T)); + } + template>* = nullptr> + void Encode(const C& v) { + Encode(v.data(), v.size() * sizeof(typename C::value_type)); + } + + int Recv(); + void RecvEnd(int proceed_bytes); + int GetRecvLength(); + const void * GetRecvBuffer(); + + int GetSendLength(); + + const char * GetHost() { return m_host; } + WORD GetPort() { return m_wPort; } + + protected: + char m_host[MAX_HOST_LENGTH + 1]; + socket_t m_fd; + WORD m_wPort; + + private: + int m_BytesRemain; + LPBUFFER m_outBuffer; + LPBUFFER m_inBuffer; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/PrivManager.cpp b/source-server/Srcs/Server/db/src/PrivManager.cpp new file mode 100644 index 000000000..4b4594a88 --- /dev/null +++ b/source-server/Srcs/Server/db/src/PrivManager.cpp @@ -0,0 +1,283 @@ +#include "stdafx.h" +#include "PrivManager.h" +#include "ClientManager.h" + +const int CHARACTER_GOOD_PRIV_DURATION = 2*60*60; +const int CHARACTER_BAD_PRIV_DURATION = 60*60; + +CPrivManager::CPrivManager() +{ + for (int type = 0; type < MAX_PRIV_NUM; ++type) + { + for (int empire = 0; empire < EMPIRE_MAX_NUM; ++empire) + m_aaPrivEmpire[type][empire] = 0; + } +} + +CPrivManager::~CPrivManager() +{ +} + +// + +// +void CPrivManager::Update() +{ + time_t now = CClientManager::instance().GetCurrentTime(); + + while (!m_pqPrivGuild.empty() && m_pqPrivGuild.top().first <= now) + { + TPrivGuildData* p = m_pqPrivGuild.top().second; + m_pqPrivGuild.pop(); + + if (p->value != 0 && !p->bRemoved) + { + typeof(m_aPrivGuild[p->type].begin()) it = m_aPrivGuild[p->type].find(p->guild_id); + + // ADD_GUILD_PRIV_TIME + + if (it != m_aPrivGuild[p->type].end() && it->second == p) { + m_aPrivGuild[p->type].erase(it); + SendChangeGuildPriv(p->guild_id, p->type, 0, 0); + // END_OF_ADD_GUILD_PRIV_TIME + } + } + + delete p; + } + + while (!m_pqPrivEmpire.empty() && m_pqPrivEmpire.top().first <= now) + { + TPrivEmpireData* p = (m_pqPrivEmpire.top().second); + m_pqPrivEmpire.pop(); + + if (p->value != 0 && !p->bRemoved) + { + SendChangeEmpirePriv(p->empire, p->type, 0, 0); + m_aaPrivEmpire[p->type][p->empire] = 0; + } + + delete p; + } + + while (!m_pqPrivChar.empty() && m_pqPrivChar.top().first <= now) + { + TPrivCharData* p = (m_pqPrivChar.top().second); + m_pqPrivChar.pop(); + + if (!p->bRemoved) + { + // TODO send packet + SendChangeCharPriv(p->pid, p->type, 0); + typeof(m_aPrivChar[p->type].begin()) it = m_aPrivChar[p->type].find(p->pid); + if (it != m_aPrivChar[p->type].end()) + m_aPrivChar[p->type].erase(it); + } + delete p; + } +} + +void CPrivManager::AddCharPriv(DWORD pid, BYTE type, int value) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddCharPriv: wrong char priv type(%u) recved", type); + return; + } + + typeof(m_aPrivChar[type].begin()) it = m_aPrivChar[type].find(pid); + + if (it != m_aPrivChar[type].end()) + return; + + if (!value) + return; + + time_t now = CClientManager::instance().GetCurrentTime(); + TPrivCharData* p = new TPrivCharData(type, value, pid); + + int iDuration = CHARACTER_BAD_PRIV_DURATION; + + if (value > 0) + iDuration = CHARACTER_GOOD_PRIV_DURATION; + + m_pqPrivChar.push(std::make_pair(now+iDuration, p)); + m_aPrivChar[type].emplace(pid, p); + + // TODO send packet + sys_log(0, "AddCharPriv %d %d %d", pid, type, value); + SendChangeCharPriv(pid, type, value); +} + +// + +// +void CPrivManager::AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t duration_sec) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddGuildPriv: wrong guild priv type(%u) recved", type); + return; + } + + typeof(m_aPrivGuild[type].begin()) it = m_aPrivGuild[type].find(guild_id); + + time_t now = CClientManager::instance().GetCurrentTime(); + time_t end = now + duration_sec; + TPrivGuildData * p = new TPrivGuildData(type, value, guild_id, end); + m_pqPrivGuild.push(std::make_pair(end, p)); + + // ADD_GUILD_PRIV_TIME + + if (it != m_aPrivGuild[type].end()) + it->second = p; + else + m_aPrivGuild[type].emplace(guild_id, p); + + SendChangeGuildPriv(guild_id, type, value, end); + // END_OF_ADD_GUILD_PRIV_TIME + + sys_log(0, "Guild Priv guild(%d) type(%d) value(%d) duration_sec(%d)", guild_id, type, value, duration_sec); +} + +void CPrivManager::AddEmpirePriv(BYTE empire, BYTE type, int value, time_t duration_sec) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddEmpirePriv: wrong empire priv type(%u) recved", type); + return; + } + + if (duration_sec < 0) + duration_sec = 0; + + time_t now = CClientManager::instance().GetCurrentTime(); + time_t end = now+duration_sec; + + { + if (m_aaPrivEmpire[type][empire]) + m_aaPrivEmpire[type][empire]->bRemoved = true; + } + + TPrivEmpireData * p = new TPrivEmpireData(type, value, empire, end); + m_pqPrivEmpire.push(std::make_pair(end, p)); + m_aaPrivEmpire[type][empire] = p; + + // ADD_EMPIRE_PRIV_TIME + SendChangeEmpirePriv(empire, type, value, end); + // END_OF_ADD_EMPIRE_PRIV_TIME + + sys_log(0, "Empire Priv empire(%d) type(%d) value(%d) duration_sec(%d)", empire, type, value, duration_sec); +} + +struct FSendChangeGuildPriv +{ + FSendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec) + { + p.guild_id = guild_id; + p.type = type; + p.value = value; + p.bLog = 1; + // ADD_GUILD_PRIV_TIME + p.end_time_sec = end_time_sec; + // END_OF_ADD_GUILD_PRIV_TIME + } + + void operator() (CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_GUILD_PRIV, 0, sizeof(TPacketDGChangeGuildPriv)); + peer->Encode(&p, sizeof(TPacketDGChangeGuildPriv)); + p.bLog = 0; + } + + TPacketDGChangeGuildPriv p; +}; + +struct FSendChangeEmpirePriv +{ + FSendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec) + { + p.empire = empire; + p.type = type; + p.value = value; + p.bLog = 1; + // ADD_EMPIRE_PRIV_TIME + p.end_time_sec = end_time_sec; + // END_OF_ADD_EMPIRE_PRIV_TIME + } + + void operator ()(CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_EMPIRE_PRIV, 0, sizeof(TPacketDGChangeEmpirePriv)); + peer->Encode(&p, sizeof(TPacketDGChangeEmpirePriv)); + p.bLog = 0; + } + + TPacketDGChangeEmpirePriv p; +}; + +struct FSendChangeCharPriv +{ + FSendChangeCharPriv(DWORD pid, BYTE type, int value) + { + p.pid = pid; + p.type = type; + p.value = value; + p.bLog = 1; + } + void operator()(CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_CHARACTER_PRIV, 0, sizeof(TPacketDGChangeCharacterPriv)); + peer->Encode(&p, sizeof(TPacketDGChangeCharacterPriv)); + p.bLog = 0; + } + TPacketDGChangeCharacterPriv p; +}; + +// ADD_GUILD_PRIV_TIME +void CPrivManager::SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec) +{ + CClientManager::instance().for_each_peer(FSendChangeGuildPriv(guild_id, type, value, end_time_sec)); +} +// END_OF_ADD_GUILD_PRIV_TIME + +// ADD_EMPIRE_PRIV_TIME +void CPrivManager::SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec) +{ + CClientManager::instance().for_each_peer(FSendChangeEmpirePriv(empire, type, value, end_time_sec)); +} +// END_OF_ADD_EMPIRE_PRIV_TIME + +void CPrivManager::SendChangeCharPriv(DWORD pid, BYTE type, int value) +{ + CClientManager::instance().for_each_peer(FSendChangeCharPriv(pid, type, value)); +} + +void CPrivManager::SendPrivOnSetup(CPeer* peer) +{ + for (int i = 1; i < MAX_PRIV_NUM; ++i) + { + for (int e = 0; e < EMPIRE_MAX_NUM; ++e) + { + // ADD_EMPIRE_PRIV_TIME + TPrivEmpireData* pPrivEmpireData = m_aaPrivEmpire[i][e]; + if (pPrivEmpireData) + { + FSendChangeEmpirePriv(e, i, pPrivEmpireData->value, pPrivEmpireData->end_time_sec)(peer); + } + // END_OF_ADD_EMPIRE_PRIV_TIME + } + + for (typeof(m_aPrivGuild[i].begin()) it = m_aPrivGuild[i].begin(); it != m_aPrivGuild[i].end();++it) + { + // ADD_GUILD_PRIV_TIME + FSendChangeGuildPriv(it->first, i, it->second->value, it->second->end_time_sec)(peer); + // END_OF_ADD_GUILD_PRIV_TIME + } + for (typeof(m_aPrivChar[i].begin()) it = m_aPrivChar[i].begin(); it != m_aPrivChar[i].end(); ++it) + { + FSendChangeCharPriv(it->first, i, it->second->value)(peer); + } + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/PrivManager.h b/source-server/Srcs/Server/db/src/PrivManager.h new file mode 100644 index 000000000..bcf5f56ff --- /dev/null +++ b/source-server/Srcs/Server/db/src/PrivManager.h @@ -0,0 +1,101 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PRIV_MANAGER_H +#define __INC_PRIV_MANAGER_H + +#include "Peer.h" +#include +#include + +struct TPrivEmpireData +{ + BYTE type; + int value; + bool bRemoved; + BYTE empire; + + // ADD_EMPIRE_PRIV_TIME + time_t end_time_sec; + + TPrivEmpireData(BYTE type, int value, BYTE empire, time_t end_time_sec) + : type(type), value(value), bRemoved(false), empire(empire), end_time_sec(end_time_sec) + {} + // END_OF_ADD_EMPIRE_PRIV_TIME +}; + +struct TPrivGuildData +{ + BYTE type; + int value; + bool bRemoved; + DWORD guild_id; + + // ADD_GUILD_PRIV_TIME + time_t end_time_sec; + + TPrivGuildData(BYTE type, int value, DWORD guild_id, time_t _end_time_sec) + : type(type), value(value), bRemoved(false), guild_id(guild_id), end_time_sec(_end_time_sec ) + {} + // END_OF_ADD_GUILD_PRIV_TIME +}; + +struct TPrivCharData +{ + BYTE type; + int value; + bool bRemoved; + DWORD pid; + TPrivCharData(BYTE type, int value, DWORD pid) + : type(type), value(value), bRemoved(false), pid(pid) + {} +}; + +class CPrivManager : public singleton +{ + public: + CPrivManager(); + virtual ~CPrivManager(); + + // ADD_GUILD_PRIV_TIME + void AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t time_sec); + // END_OF_ADD_GUILD_PRIV_TIME + + // ADD_EMPIRE_PRIV_TIME + void AddEmpirePriv(BYTE empire, BYTE type, int value, time_t time_sec); + // END_OF_ADD_EMPIRE_PRIV_TIME + + void AddCharPriv(DWORD pid, BYTE type, int value); + + void Update(); + + void SendPrivOnSetup(CPeer* peer); + + private: + + // ADD_GUILD_PRIV_TIME + void SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec); + // END_OF_ADD_GUILD_PRIV_TIME + + // ADD_EMPIRE_PRIV_TIME + void SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec); + // END_OF_ADD_EMPIRE_PRIV_TIME + + void SendChangeCharPriv(DWORD pid, BYTE type, int value); + + typedef std::pair stPairChar; + typedef std::pair stPairGuild; + typedef std::pair stPairEmpire; + + std::priority_queue, std::greater > + m_pqPrivChar; + std::priority_queue, std::greater > + m_pqPrivGuild; + std::priority_queue, std::greater > + m_pqPrivEmpire; + + TPrivEmpireData* m_aaPrivEmpire[MAX_PRIV_NUM][EMPIRE_MAX_NUM]; + std::map m_aPrivGuild[MAX_PRIV_NUM]; + std::map m_aPrivChar[MAX_PRIV_NUM]; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ProtoReader.cpp b/source-server/Srcs/Server/db/src/ProtoReader.cpp new file mode 100644 index 000000000..b95a49c1c --- /dev/null +++ b/source-server/Srcs/Server/db/src/ProtoReader.cpp @@ -0,0 +1,825 @@ +#include "stdafx.h" + +#include +#include "ProtoReader.h" + +#include "CsvReader.h" + +#include +#include +#include +#include + +using namespace std; + +#define ENABLE_NUMERIC_FIELD +#ifdef ENABLE_NUMERIC_FIELD +bool _IsNumericString(const std::string& trimInputString) +{ + if (trimInputString.empty()) + return false; + bool isNumber = true; + for (auto& c : trimInputString) + { + if (!std::isdigit(c) && c != '-' && c != '+') + { + isNumber = false; + break; + } + } + return isNumber; +} +#endif + +inline string trim_left(const string& str) +{ + string::size_type n = str.find_first_not_of(" \t\v\n\r"); + return n == string::npos ? str : str.substr(n, str.length()); +} + +inline string trim_right(const string& str) +{ + string::size_type n = str.find_last_not_of(" \t\v\n\r"); + return n == string::npos ? str : str.substr(0, n + 1); +} + +string trim(const string& str){return trim_left(trim_right(str));} + +static string* StringSplit(string strOrigin, string strTok) +{ + size_t cutAt; + int index = 0; + string* strResult = new string[30]; + + while ((cutAt = strOrigin.find_first_of(strTok)) != strOrigin.npos) + { + if (cutAt > 0) + { + strResult[index++] = strOrigin.substr(0, cutAt); + } + strOrigin = strOrigin.substr(cutAt+1); + } + + if(strOrigin.length() > 0) + { + strResult[index++] = strOrigin.substr(0, cutAt); + } + + for( int i=0;i arItemType = { + "ITEM_NONE", "ITEM_WEAPON", + "ITEM_ARMOR", "ITEM_USE", + "ITEM_AUTOUSE", "ITEM_MATERIAL", + "ITEM_SPECIAL", "ITEM_TOOL", + "ITEM_LOTTERY", "ITEM_ELK", + + "ITEM_METIN", "ITEM_CONTAINER", + "ITEM_FISH", "ITEM_ROD", + "ITEM_RESOURCE", "ITEM_CAMPFIRE", + "ITEM_UNIQUE", "ITEM_SKILLBOOK", + "ITEM_QUEST", "ITEM_POLYMORPH", + + "ITEM_TREASURE_BOX", "ITEM_TREASURE_KEY", + "ITEM_SKILLFORGET", "ITEM_GIFTBOX", + "ITEM_PICK", "ITEM_HAIR", + "ITEM_TOTEM", "ITEM_BLEND", + "ITEM_COSTUME", "ITEM_DS", + + "ITEM_SPECIAL_DS", "ITEM_EXTRACT", + "ITEM_SECONDARY_COIN", + + "ITEM_RING", + "ITEM_BELT", + "ITEM_PET", "ITEM_MEDIUM", // 35,36 + "ITEM_GACHA", "ITEM_SOUL" // 37,38 + "ITEM_PASSIVE" // 39 +}; + +int get_Item_Type_Value(std::string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + + auto it = std::find(std::begin(arItemType), std::end(arItemType), inputString); + if (it == std::end(arItemType)) + return -1; + return std::distance(std::begin(arItemType), it); +} + +static const std::unordered_map> mapItemSubType { + {1, { "WEAPON_SWORD", "WEAPON_DAGGER", "WEAPON_BOW", "WEAPON_TWO_HANDED", + "WEAPON_BELL", "WEAPON_FAN", "WEAPON_ARROW", "WEAPON_MOUNT_SPEAR", "WEAPON_CLAW", "WEAPON_QUIVER", "WEAPON_BOUQUET"}}, + {2, { "ARMOR_BODY", "ARMOR_HEAD", "ARMOR_SHIELD", "ARMOR_WRIST", "ARMOR_FOOTS", + "ARMOR_NECK", "ARMOR_EAR", "ARMOR_PENDANT", "ARMOR_GLOVE", "ARMOR_NUM_TYPES"}}, + {3, { "USE_POTION", "USE_TALISMAN", "USE_TUNING", "USE_MOVE", "USE_TREASURE_BOX", "USE_MONEYBAG", "USE_BAIT", + "USE_ABILITY_UP", "USE_AFFECT", "USE_CREATE_STONE", "USE_SPECIAL", "USE_POTION_NODELAY", "USE_CLEAR", + "USE_INVISIBILITY", "USE_DETACHMENT", "USE_BUCKET", "USE_POTION_CONTINUE", "USE_CLEAN_SOCKET", + "USE_CHANGE_ATTRIBUTE", "USE_ADD_ATTRIBUTE", "USE_ADD_ACCESSORY_SOCKET", "USE_PUT_INTO_ACCESSORY_SOCKET", + "USE_ADD_ATTRIBUTE2", "USE_RECIPE", "USE_CHANGE_ATTRIBUTE2", "USE_BIND", "USE_UNBIND", "USE_TIME_CHARGE_PER", "USE_TIME_CHARGE_FIX", "USE_PUT_INTO_BELT_SOCKET", "USE_PUT_INTO_RING_SOCKET", + "USE_CHANGE_COSTUME_ATTR", "USE_RESET_COSTUME_ATTR", "USE_UNK33", "USE_CHANGE_ATTRIBUTE_PLUS"}}, + {4, { "AUTOUSE_POTION", "AUTOUSE_ABILITY_UP", "AUTOUSE_BOMB", "AUTOUSE_GOLD", "AUTOUSE_MONEYBAG", "AUTOUSE_TREASURE_BOX"}}, + {5, { "MATERIAL_LEATHER", "MATERIAL_BLOOD", "MATERIAL_ROOT", "MATERIAL_NEEDLE", "MATERIAL_JEWEL", + "MATERIAL_DS_REFINE_NORMAL", "MATERIAL_DS_REFINE_BLESSED", "MATERIAL_DS_REFINE_HOLLY"}}, + {6, { "SPECIAL_MAP", "SPECIAL_KEY", "SPECIAL_DOC", "SPECIAL_SPIRIT"}}, + {7, { "TOOL_FISHING_ROD"}}, + {8, { "LOTTERY_TICKET", "LOTTERY_INSTANT"}}, + {10, { "METIN_NORMAL", "METIN_GOLD"}}, + {12, { "FISH_ALIVE", "FISH_DEAD"}}, + {14, { "RESOURCE_FISHBONE", "RESOURCE_WATERSTONEPIECE", "RESOURCE_WATERSTONE", "RESOURCE_BLOOD_PEARL", + "RESOURCE_BLUE_PEARL", "RESOURCE_WHITE_PEARL", "RESOURCE_BUCKET", "RESOURCE_CRYSTAL", "RESOURCE_GEM", + "RESOURCE_STONE", "RESOURCE_METIN", "RESOURCE_ORE"}}, + {16, { "UNIQUE_NONE", "UNIQUE_BOOK", "UNIQUE_SPECIAL_RIDE", "UNIQUE_3", "UNIQUE_4", "UNIQUE_5", + "UNIQUE_6", "UNIQUE_7", "UNIQUE_8", "UNIQUE_9", "USE_SPECIAL"}}, + {28, { "COSTUME_BODY", "COSTUME_HAIR", "COSTUME_MOUNT", "COSTUME_ACCE", "COSTUME_WEAPON"}}, + {29, { "DS_SLOT1", "DS_SLOT2", "DS_SLOT3", "DS_SLOT4", "DS_SLOT5", "DS_SLOT6"}}, + {31, { "EXTRACT_DRAGON_SOUL", "EXTRACT_DRAGON_HEART"}}, + {35, { "PET_EGG", "PET_UPBRINGING", "PET_BAG", "PET_FEEDSTUFF", "PET_SKILL", + "PET_SKILL_DEL_BOOK", "PET_NAME_CHANGE", "PET_EXPFOOD", "PET_SKILL_ALL_DEL_BOOK", "PET_EXPFOOD_PER", + "PET_ATTR_DETERMINE", "PET_ATTR_CHANGE", "PET_PAY", "PET_PRIMIUM_FEEDSTUFF"}}, +}; + +int get_Item_SubType_Value(int type_value, std::string inputString) +{ + inputString = trim(inputString); + +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + + auto itType = mapItemSubType.find(type_value); + if (itType == mapItemSubType.end()) + { + sys_err("Type Out of range! (type_value: %d)", type_value); + return -1; + } + + auto vecSubtype = itType->second; + auto itSubtype = std::find(vecSubtype.begin(), vecSubtype.end(), inputString); + if (itSubtype == vecSubtype.end()) + { + sys_err("Subtype Out of range! (type_value: %d)", type_value); + return -1; + } + + return std::distance(vecSubtype.begin(), itSubtype); +} + +int get_Item_AntiFlag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arAntiFlag[] = {"ANTI_FEMALE", "ANTI_MALE", "ANTI_MUSA", "ANTI_ASSASSIN", "ANTI_SURA", "ANTI_MUDANG", + "ANTI_GET", "ANTI_DROP", "ANTI_SELL", "ANTI_EMPIRE_A", "ANTI_EMPIRE_B", "ANTI_EMPIRE_C", + "ANTI_SAVE", "ANTI_GIVE", "ANTI_PKDROP", "ANTI_STACK", "ANTI_MYSHOP", "ANTI_SAFEBOX", "ANTI_WOLFMAN", + "ANTI_PET20", "ANTI_PET21"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); + for(size_t i=0;i<_countof(arAntiFlag);i++) { + string tempString = arAntiFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "AntiFlag : " << antiFlagStr << " -> " << retValue << endl; + + return retValue; +} + +int get_Item_Flag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arFlag[] = {"ITEM_TUNABLE", "ITEM_SAVE", "ITEM_STACKABLE", "COUNT_PER_1GOLD", "ITEM_SLOW_QUERY", "ITEM_UNIQUE", + "ITEM_MAKECOUNT", "ITEM_IRREMOVABLE", "CONFIRM_WHEN_USE", "QUEST_USE", "QUEST_USE_MULTIPLE", + "QUEST_GIVE", "ITEM_QUEST", "LOG", "STACKABLE", "SLOW_QUERY", "REFINEABLE", "IRREMOVABLE", "ITEM_APPLICABLE"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); + for(size_t i=0;i<_countof(arFlag);i++) { + string tempString = arFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "Flag : " << flagStr << " -> " << retValue << endl; + + return retValue; +} + +int get_Item_WearFlag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arWearrFlag[] = {"WEAR_BODY", "WEAR_HEAD", "WEAR_FOOTS", "WEAR_WRIST", "WEAR_WEAPON", "WEAR_NECK", "WEAR_EAR", "WEAR_SHIELD", "WEAR_UNIQUE", + "WEAR_ARROW", "WEAR_HAIR", "WEAR_ABILITY", "WEAR_PENDANT", "WEAR_GLOVE" + }; + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); + for(size_t i=0;i<_countof(arWearrFlag);i++) { + string tempString = arWearrFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "WearFlag : " << wearFlagStr << " -> " << retValue << endl; + + return retValue; +} + +int get_Item_Immune_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arImmune[] = {"PARA","CURSE","STUN","SLEEP","SLOW","POISON","TERROR"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); + for(size_t i=0;i<_countof(arImmune);i++) { + string tempString = arImmune[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "Immune : " << immuneStr << " -> " << retValue << endl; + + return retValue; +} + +int get_Item_LimitType_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arLimitType[] = {"LIMIT_NONE", "LEVEL", "STR", "DEX", "INT", "CON", "PC_BANG", "REAL_TIME", "REAL_TIME_FIRST_USE", "TIMER_BASED_ON_WEAR"}; + + int retInt = -1; + //cout << "LimitType : " << limitTypeStr << " -> "; + for (unsigned int j=0;j<_countof(arLimitType);j++) { + string tempString = arLimitType[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Item_ApplyType_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arApplyType[] = {"APPLY_NONE", "APPLY_MAX_HP", "APPLY_MAX_SP", "APPLY_CON", "APPLY_INT", "APPLY_STR", "APPLY_DEX", "APPLY_ATT_SPEED", + "APPLY_MOV_SPEED", "APPLY_CAST_SPEED", "APPLY_HP_REGEN", "APPLY_SP_REGEN", "APPLY_POISON_PCT", "APPLY_STUN_PCT", + "APPLY_SLOW_PCT", "APPLY_CRITICAL_PCT", "APPLY_PENETRATE_PCT", "APPLY_ATTBONUS_HUMAN", "APPLY_ATTBONUS_ANIMAL", + "APPLY_ATTBONUS_ORC", "APPLY_ATTBONUS_MILGYO", "APPLY_ATTBONUS_UNDEAD", "APPLY_ATTBONUS_DEVIL", "APPLY_STEAL_HP", + "APPLY_STEAL_SP", "APPLY_MANA_BURN_PCT", "APPLY_DAMAGE_SP_RECOVER", "APPLY_BLOCK", "APPLY_DODGE", "APPLY_RESIST_SWORD", + "APPLY_RESIST_TWOHAND", "APPLY_RESIST_DAGGER", "APPLY_RESIST_BELL", "APPLY_RESIST_FAN", "APPLY_RESIST_BOW", "APPLY_RESIST_FIRE", + "APPLY_RESIST_ELEC", "APPLY_RESIST_MAGIC", "APPLY_RESIST_WIND", "APPLY_REFLECT_MELEE", "APPLY_REFLECT_CURSE", "APPLY_POISON_REDUCE", + "APPLY_KILL_SP_RECOVER", "APPLY_EXP_DOUBLE_BONUS", "APPLY_GOLD_DOUBLE_BONUS", "APPLY_ITEM_DROP_BONUS", "APPLY_POTION_BONUS", + "APPLY_KILL_HP_RECOVER", "APPLY_IMMUNE_STUN", "APPLY_IMMUNE_SLOW", "APPLY_IMMUNE_FALL", "APPLY_SKILL", "APPLY_BOW_DISTANCE", + "APPLY_ATT_GRADE_BONUS", "APPLY_DEF_GRADE_BONUS", "APPLY_MAGIC_ATT_GRADE", "APPLY_MAGIC_DEF_GRADE", "APPLY_CURSE_PCT", + "APPLY_MAX_STAMINA", "APPLY_ATTBONUS_WARRIOR", "APPLY_ATTBONUS_ASSASSIN", "APPLY_ATTBONUS_SURA", "APPLY_ATTBONUS_SHAMAN", + "APPLY_ATTBONUS_MONSTER", "APPLY_MALL_ATTBONUS", "APPLY_MALL_DEFBONUS", "APPLY_MALL_EXPBONUS", "APPLY_MALL_ITEMBONUS", + "APPLY_MALL_GOLDBONUS", "APPLY_MAX_HP_PCT", "APPLY_MAX_SP_PCT", "APPLY_SKILL_DAMAGE_BONUS", "APPLY_NORMAL_HIT_DAMAGE_BONUS", + "APPLY_SKILL_DEFEND_BONUS", "APPLY_NORMAL_HIT_DEFEND_BONUS", "APPLY_PC_BANG_EXP_BONUS", "APPLY_PC_BANG_DROP_BONUS", + "APPLY_EXTRACT_HP_PCT", "APPLY_RESIST_WARRIOR", "APPLY_RESIST_ASSASSIN", "APPLY_RESIST_SURA", "APPLY_RESIST_SHAMAN", + "APPLY_ENERGY", "APPLY_DEF_GRADE", "APPLY_COSTUME_ATTR_BONUS", "APPLY_MAGIC_ATTBONUS_PER", "APPLY_MELEE_MAGIC_ATTBONUS_PER", + "APPLY_RESIST_ICE", "APPLY_RESIST_EARTH", "APPLY_RESIST_DARK", "APPLY_ANTI_CRITICAL_PCT", "APPLY_ANTI_PENETRATE_PCT", + "APPLY_BLEEDING_REDUCE", "APPLY_BLEEDING_PCT", "APPLY_ATTBONUS_WOLFMAN", "APPLY_RESIST_WOLFMAN", "APPLY_RESIST_CLAW", + "APPLY_ACCEDRAIN_RATE", "APPLY_RESIST_MAGIC_REDUCTION", // 97,98 + "APPLY_ENCHANT_ELECT", "APPLY_ENCHANT_FIRE", "APPLY_ENCHANT_ICE", "APPLY_ENCHANT_WIND", "APPLY_ENCHANT_EARTH", "APPLY_ENCHANT_DARK", // 99-104 + "APPLY_ATTBONUS_CZ","APPLY_ATTBONUS_INSECT","APPLY_ATTBONUS_DESERT","APPLY_ATTBONUS_SWORD","APPLY_ATTBONUS_TWOHAND", // 105,109 + "APPLY_ATTBONUS_DAGGER","APPLY_ATTBONUS_BELL","APPLY_ATTBONUS_FAN","APPLY_ATTBONUS_BOW","APPLY_ATTBONUS_CLAW", "APPLY_RESIST_HUMAN", // 110,115 + "APPLY_RESIST_MOUNT_FALL", "APPLY_UNK117", "APPLY_MOUNT" // 116-118 + }; + + int retInt = -1; + //cout << "ApplyType : " << applyTypeStr << " -> "; + for (size_t j=0;j<_countof(arApplyType);j++) { + string tempString = arApplyType[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Mob_Rank_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arRank[] = {"PAWN", "S_PAWN", "KNIGHT", "S_KNIGHT", "BOSS", "KING"}; + + int retInt = -1; + //cout << "Rank : " << rankStr << " -> "; + for (size_t j=0;j<_countof(arRank);j++) { + string tempString = arRank[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Mob_Type_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arType[] = { "MONSTER", "NPC", "STONE", "WARP", "DOOR", "BUILDING", "PC", "POLYMORPH_PC", "HORSE", "GOTO"}; + + int retInt = -1; + //cout << "Type : " << typeStr << " -> "; + for (size_t j=0;j<_countof(arType);j++) { + string tempString = arType[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Mob_BattleType_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arBattleType[] = { "MELEE", "RANGE", "MAGIC", "SPECIAL", "POWER", "TANKER", "SUPER_POWER", "SUPER_TANKER"}; + + int retInt = -1; + //cout << "Battle Type : " << battleTypeStr << " -> "; + for (size_t j=0;j<_countof(arBattleType);j++) { + string tempString = arBattleType[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Mob_Size_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arSize[] = { "SMALL", "MEDIUM", "BIG"}; //@fixme201 SAMLL to SMALL + + int retInt = 0; + //cout << "Size : " << sizeStr << " -> "; + for (size_t j=0;j<_countof(arSize);j++) { + string tempString = arSize[j]; + string tempInputString = trim(inputString); + if (tempInputString.compare(tempString)==0) + { + //cout << j << " "; + retInt = j + 1; + break; + } + } + //cout << endl; + + return retInt; +} + +int get_Mob_AIFlag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arAIFlag[] = {"AGGR","NOMOVE","COWARD","NOATTSHINSU","NOATTCHUNJO","NOATTJINNO","ATTMOB","BERSERK","STONESKIN","GODSPEED","DEATHBLOW","REVIVE", + }; + + int retValue = 0; + string* arInputString = StringSplit(inputString, ","); + for (size_t i =0;i<_countof(arAIFlag);i++) { + string tempString = arAIFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "AIFlag : " << aiFlagStr << " -> " << retValue << endl; + + return retValue; +} +int get_Mob_RaceFlag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arRaceFlag[] = {"ANIMAL","UNDEAD","DEVIL","HUMAN","ORC","MILGYO","INSECT","FIRE","ICE","DESERT","TREE", + "ATT_ELEC","ATT_FIRE","ATT_ICE","ATT_WIND","ATT_EARTH","ATT_DARK","ZODIAC"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, ","); + for(size_t i =0;i<_countof(arRaceFlag);i++) { + string tempString = arRaceFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "Race Flag : " << raceFlagStr << " -> " << retValue << endl; + + return retValue; +} +int get_Mob_ImmuneFlag_Value(string inputString) +{ +#ifdef ENABLE_NUMERIC_FIELD + if (_IsNumericString(inputString)) + return std::stoi(inputString); +#endif + string arImmuneFlag[] = {"STUN","SLOW","FALL","CURSE","POISON","TERROR", "REFLECT"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, ","); + for(size_t i =0;i<_countof(arImmuneFlag);i++) { + string tempString = arImmuneFlag[i]; + for (size_t j=0; j<30 ; j++) + { + string tempString2 = arInputString[j]; + if (tempString2.compare(tempString)==0) { + retValue = retValue + pow((float)2,(float)i); + } + + if(tempString2.compare("") == 0) + break; + } + } + delete []arInputString; + //cout << "Immune Flag : " << immuneFlagStr << " -> " << retValue << endl; + + return retValue; +} + +#ifndef __DUMP_PROTO__ + +bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable,std::map &nameMap) +{ + int col = 0; + str_to_number(mobTable->dwVnum, csvTable.AsStringByIndex(col++)); + strlcpy(mobTable->szName, csvTable.AsStringByIndex(col++), sizeof(mobTable->szName)); + + auto it = nameMap.find(mobTable->dwVnum); + if (it != nameMap.end()) { + const char * localeName = it->second; + strlcpy(mobTable->szLocaleName, localeName, sizeof(mobTable->szLocaleName)); + } else { + strlcpy(mobTable->szLocaleName, mobTable->szName, sizeof(mobTable->szLocaleName)); + } + + //RANK + int rankValue = get_Mob_Rank_Value(csvTable.AsStringByIndex(col++)); + mobTable->bRank = rankValue; + //TYPE + int typeValue = get_Mob_Type_Value(csvTable.AsStringByIndex(col++)); + mobTable->bType = typeValue; + //BATTLE_TYPE + int battleTypeValue = get_Mob_BattleType_Value(csvTable.AsStringByIndex(col++)); + mobTable->bBattleType = battleTypeValue; + + str_to_number(mobTable->bLevel, csvTable.AsStringByIndex(col++)); + //SIZE + int sizeValue = get_Mob_Size_Value(csvTable.AsStringByIndex(col++)); + mobTable->bSize = sizeValue; + //AI_FLAG + int aiFlagValue = get_Mob_AIFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwAIFlag = aiFlagValue; + //mount_capacity; + col++; + //RACE_FLAG + int raceFlagValue = get_Mob_RaceFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwRaceFlag = raceFlagValue; + //IMMUNE_FLAG + int immuneFlagValue = get_Mob_ImmuneFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwImmuneFlag = immuneFlagValue; + + str_to_number(mobTable->bEmpire, csvTable.AsStringByIndex(col++)); //col = 11 + + strlcpy(mobTable->szFolder, csvTable.AsStringByIndex(col++), sizeof(mobTable->szFolder)); + + str_to_number(mobTable->bOnClickType, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->bStr, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bDex, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bCon, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bInt, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDamageRange[0], csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDamageRange[1], csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwMaxHP, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRegenCycle, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRegenPercent, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwGoldMin, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwGoldMax, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwExp, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wDef, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->sAttackSpeed, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->sMovingSpeed, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bAggresiveHPPct, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wAggressiveSight, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wAttackRange, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->dwDropItemVnum, csvTable.AsStringByIndex(col++)); //32 + str_to_number(mobTable->dwResurrectionVnum, csvTable.AsStringByIndex(col++)); + for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i) + str_to_number(mobTable->cEnchants[i], csvTable.AsStringByIndex(col++)); + + for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i) + str_to_number(mobTable->cResists[i], csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->fDamMultiply, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwSummonVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDrainSP, csvTable.AsStringByIndex(col++)); + + //Mob_Color + ++col; + + str_to_number(mobTable->dwPolymorphItemVnum, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->Skills[0].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[0].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[1].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[1].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[2].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[2].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[3].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[3].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[4].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[4].dwVnum, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->bBerserkPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bStoneSkinPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bGodSpeedPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bDeathBlowPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRevivePoint, csvTable.AsStringByIndex(col++)); + + sys_log(0, "MOB #%-5d %-24s level: %-3u rank: %u empire: %d", mobTable->dwVnum, mobTable->szLocaleName, mobTable->bLevel, mobTable->bRank, mobTable->bEmpire); + + return true; +} + +bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map &nameMap) +{ + int col = 0; + + int dataArray[33]; + for (size_t i=0; i<_countof(dataArray);i++) { + int validCheck = 0; + if (i==2) { + dataArray[i] = get_Item_Type_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==3) { + dataArray[i] = get_Item_SubType_Value(dataArray[i-1], csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==5) { + dataArray[i] = get_Item_AntiFlag_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==6) { + dataArray[i] = get_Item_Flag_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==7) { + dataArray[i] = get_Item_WearFlag_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==8) { + dataArray[i] = get_Item_Immune_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==14) { + dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==16) { + dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==18) { + dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==20) { + dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else if (i==22) { + dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); + validCheck = dataArray[i]; + } else { + str_to_number(dataArray[i], csvTable.AsStringByIndex(col)); + } + + if (validCheck == -1) + { + std::ostringstream dataStream; + + for (size_t j = 0; j < i; ++j) + dataStream << dataArray[j] << ","; + + //fprintf(stderr, "ItemProto Reading Failed : Invalid value.\n"); + sys_err("ItemProto Reading Failed : Invalid value. (index: %d, col: %d, value: %s)", i, col, csvTable.AsStringByIndex(col)); + sys_err("\t%d ~ %d Values: %s", 0, i, dataStream.str().c_str()); + + exit(0); + } + + col = col + 1; + } + + { + std::string s(csvTable.AsStringByIndex(0)); + size_t pos = s.find("~"); + if (std::string::npos == pos) + { + itemTable->dwVnum = dataArray[0]; + itemTable->dwVnumRange = 0; + } + else + { + std::string s_start_vnum (s.substr(0, pos)); + std::string s_end_vnum (s.substr(pos +1 )); + + int start_vnum = atoi(s_start_vnum.c_str()); + int end_vnum = atoi(s_end_vnum.c_str()); + if (0 == start_vnum || (0 != end_vnum && end_vnum < start_vnum)) + { + sys_err ("INVALID VNUM %s", s.c_str()); + return false; + } + itemTable->dwVnum = start_vnum; + itemTable->dwVnumRange = end_vnum - start_vnum; + } + } + + strlcpy(itemTable->szName, csvTable.AsStringByIndex(1), sizeof(itemTable->szName)); + auto it = nameMap.find(itemTable->dwVnum); + if (it != nameMap.end()) { + const char * localeName = it->second; + strlcpy(itemTable->szLocaleName, localeName, sizeof(itemTable->szLocaleName)); + } else { + strlcpy(itemTable->szLocaleName, itemTable->szName, sizeof(itemTable->szLocaleName)); + } + itemTable->bType = dataArray[2]; + itemTable->bSubType = dataArray[3]; + itemTable->bSize = MINMAX(1, dataArray[4], 3); // @fixme179 + itemTable->dwAntiFlags = dataArray[5]; + itemTable->dwFlags = dataArray[6]; + itemTable->dwWearFlags = dataArray[7]; + itemTable->dwImmuneFlag = dataArray[8]; + itemTable->dwGold = dataArray[9]; + itemTable->dwShopBuyPrice = dataArray[10]; + itemTable->dwRefinedVnum = dataArray[11]; + itemTable->wRefineSet = dataArray[12]; + itemTable->bAlterToMagicItemPct = dataArray[13]; + itemTable->cLimitRealTimeFirstUseIndex = -1; + itemTable->cLimitTimerBasedOnWearIndex = -1; + + int i; + + for (i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + itemTable->aLimits[i].bType = dataArray[14+i*2]; + itemTable->aLimits[i].lValue = dataArray[15+i*2]; + + if (LIMIT_REAL_TIME_START_FIRST_USE == itemTable->aLimits[i].bType) + itemTable->cLimitRealTimeFirstUseIndex = (char)i; + + if (LIMIT_TIMER_BASED_ON_WEAR == itemTable->aLimits[i].bType) + itemTable->cLimitTimerBasedOnWearIndex = (char)i; + + } + + for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i) + { + itemTable->aApplies[i].bType = dataArray[18+i*2]; + itemTable->aApplies[i].lValue = dataArray[19+i*2]; + } + + for (i = 0; i < ITEM_VALUES_MAX_NUM; ++i) + itemTable->alValues[i] = dataArray[24+i]; + + itemTable->bGainSocketPct = dataArray[31]; + itemTable->sAddonType = dataArray[32]; + + str_to_number(itemTable->bWeight, "0"); + +#ifdef ENABLE_CHECK_SELL_PRICE + auto dwPrice = itemTable->dwShopBuyPrice; + #ifndef ENABLE_NO_SELL_PRICE_DIVIDED_BY_5 + dwPrice /= 5; + #endif + if (dwPrice > itemTable->dwGold) + { + sys_err("ITEM: #%-5lu %-24s SELL_OVERFLOW dwGold: %u < dwShopBuyPrice %u", + itemTable->dwVnum, + itemTable->szLocaleName, + itemTable->dwGold, + dwPrice + ); + itemTable->dwGold = dwPrice; + } +#endif + + return true; +} + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/ProtoReader.h b/source-server/Srcs/Server/db/src/ProtoReader.h new file mode 100644 index 000000000..73cc191ae --- /dev/null +++ b/source-server/Srcs/Server/db/src/ProtoReader.h @@ -0,0 +1,34 @@ +#ifndef __Item_CSV_READER_H__ +#define __Item_CSV_READER_H__ + +#include +#include + +#include "CsvReader.h" + +void putItemIntoTable(); + +int get_Item_Type_Value(std::string inputString); +int get_Item_SubType_Value(int type_value, std::string inputString); +int get_Item_AntiFlag_Value(std::string inputString); +int get_Item_Flag_Value(std::string inputString); +int get_Item_WearFlag_Value(std::string inputString); +int get_Item_Immune_Value(std::string inputString); +int get_Item_LimitType_Value(std::string inputString); +int get_Item_ApplyType_Value(std::string inputString); + +int get_Mob_Rank_Value(std::string inputString); +int get_Mob_Type_Value(std::string inputString); +int get_Mob_BattleType_Value(std::string inputString); + +int get_Mob_Size_Value(std::string inputString); +int get_Mob_AIFlag_Value(std::string inputString); +int get_Mob_RaceFlag_Value(std::string inputString); +int get_Mob_ImmuneFlag_Value(std::string inputString); + +// +bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable, std::map &nameMap); +bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map &nameMap); + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/QID.h b/source-server/Srcs/Server/db/src/QID.h new file mode 100644 index 000000000..512909dd2 --- /dev/null +++ b/source-server/Srcs/Server/db/src/QID.h @@ -0,0 +1,36 @@ +#ifndef __INC_METIN_II_DB_QID_H__ +#define __INC_METIN_II_DB_QID_H__ + +enum QID +{ + QID_PLAYER, // 0 + QID_ITEM, // 1 + QID_QUEST, // 2 + QID_AFFECT, // 3 + QID_LOGIN, // 4 + QID_SAFEBOX_LOAD, // 5 + QID_SAFEBOX_CHANGE_SIZE, // 6 + QID_SAFEBOX_CHANGE_PASSWORD, // 7 + QID_SAFEBOX_CHANGE_PASSWORD_SECOND, // 8 + QID_SAFEBOX_SAVE, // 9 + QID_ITEM_SAVE, // 10 + QID_ITEM_DESTROY, // 11 + QID_QUEST_SAVE, // 12 + QID_PLAYER_SAVE, // 13 + QID_PLAYER_DELETE, // 15 + QID_LOGIN_BY_KEY, // 16 + QID_PLAYER_INDEX_CREATE, // 17 + QID_ITEM_AWARD_LOAD, // 18 + QID_ITEM_AWARD_TAKEN, // 19 + QID_GUILD_RANKING, // 20 + + // MYSHOP_PRICE_LIST + QID_ITEMPRICE_SAVE, + QID_ITEMPRICE_DESTROY, + QID_ITEMPRICE_LOAD_FOR_UPDATE, + QID_ITEMPRICE_LOAD, + // END_OF_MYSHOP_PRICE_LIST +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/db.vcxproj b/source-server/Srcs/Server/db/src/db.vcxproj new file mode 100644 index 000000000..8227febd8 --- /dev/null +++ b/source-server/Srcs/Server/db/src/db.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + db + {ADC2E26A-C39B-4817-A24C-A99827B74EB2} + db + + + + + + + + + Win32Proj + 10.0 + + + + Application + MultiByte + true + v143 + + + Application + MultiByte + v143 + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\db\ + $(Configuration)\ + true + ../bin/release\ + $(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + $(ProjectDir)../../libmysql/6.0.2/win32;$(ProjectDir)../../../Extern/include;%(AdditionalIncludeDirectories) + WIN32;NOMINMAX;_DEBUG;_CONSOLE;__WIN32__;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WIN32;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + $(OutDir)$(TargetName).pdb + Level3 + EditAndContinue + stdcpp20 + + + libmysql.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName)_d.exe + $(ProjectDir)../../libmysql/6.0.2/win32/lib;$(ProjectDir)../../../Extern/lib;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + LIBCMT;libcpmt;%(IgnoreSpecificDefaultLibraries) + + + + + MaxSpeed + true + $(ProjectDir)../../libmysql/6.0.2/win32;$(ProjectDir)../../../Extern/include;%(AdditionalIncludeDirectories) + WIN32;NOMINMAX;NDEBUG;_CONSOLE;__WIN32__;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WIN32;%(PreprocessorDefinitions) + MultiThreaded + true + + + $(OutDir)$(TargetName).pdb + Level3 + ProgramDatabase + stdcpp20 + + + libmysql.lib;ws2_32.lib;%(AdditionalDependencies) + $(ProjectDir)../../libmysql/6.0.2/win32/lib;$(ProjectDir)../../../Extern/lib;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2ab4a5a4-3eaa-4486-b93c-38e8d39bf218} + false + + + {bd8e86dd-07ba-49b0-bf04-3282da8377eb} + false + + + {3967853d-4e19-4548-ac3a-f6012b78e384} + false + + + {5c8620c9-5d1f-419d-8267-48d2863c3d13} + false + + + + + + \ No newline at end of file diff --git a/source-server/Srcs/Server/db/src/db.vcxproj.filters b/source-server/Srcs/Server/db/src/db.vcxproj.filters new file mode 100644 index 000000000..b2db8cf07 --- /dev/null +++ b/source-server/Srcs/Server/db/src/db.vcxproj.filters @@ -0,0 +1,163 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/source-server/Srcs/Server/db/src/db.vcxproj.user b/source-server/Srcs/Server/db/src/db.vcxproj.user new file mode 100644 index 000000000..ace9a86ac --- /dev/null +++ b/source-server/Srcs/Server/db/src/db.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/source-server/Srcs/Server/db/src/stdafx.h b/source-server/Srcs/Server/db/src/stdafx.h new file mode 100644 index 000000000..100afb1a1 --- /dev/null +++ b/source-server/Srcs/Server/db/src/stdafx.h @@ -0,0 +1,21 @@ +#ifndef __INC_METiN_II_DBSERV_STDAFX_H__ +#define __INC_METiN_II_DBSERV_STDAFX_H__ + +#include "../../libthecore/include/stdafx.h" + +#ifndef __WIN32__ +#include +#else +#define isdigit iswdigit +#define isspace iswspace +#endif + +#include "../../common/length.h" +#include "../../common/tables.h" +#include "../../common/singleton.h" +#include "../../common/utils.h" +#include "../../common/stl.h" +#include "../../common/service.h" + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/db/src/version.cpp b/source-server/Srcs/Server/db/src/version.cpp new file mode 100644 index 000000000..5d6ffd7d8 --- /dev/null +++ b/source-server/Srcs/Server/db/src/version.cpp @@ -0,0 +1,22 @@ +#include +#include + +void WriteVersion() +{ +#ifndef __WIN32__ + FILE* fp(fopen("VERSION.txt", "w")); + + if (NULL != fp) + { + fprintf(fp, "__DB_VERSION__: %s\n", __DB_VERSION__); + fprintf(fp, "%s@%s:%s\n", "cHVjaGF0eQ==", __HOSTNAME__, __PWD__); + fclose(fp); + } + else + { + fprintf(stderr, "cannot open VERSION.txt\n"); + exit(0); + } +#endif +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/BlueDragon.cpp b/source-server/Srcs/Server/game/src/BlueDragon.cpp new file mode 100644 index 000000000..9d22ed8a7 --- /dev/null +++ b/source-server/Srcs/Server/game/src/BlueDragon.cpp @@ -0,0 +1,194 @@ +#include "stdafx.h" +#include "config.h" + +#include "BlueDragon.h" + +#include "vector.h" +#include "utils.h" +#include "char.h" +#include "mob_manager.h" +#include "sectree_manager.h" +#include "battle.h" +#include "affect.h" +#include "BlueDragon_Binder.h" +#include "BlueDragon_Skill.h" +#include "packet.h" +#include "motion.h" + +time_t UseBlueDragonSkill(LPCHARACTER pChar, unsigned int idx) +{ + LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap( pChar->GetMapIndex() ); + + if (NULL == pSecMap) + return 0; + + int nextUsingTime = 0; + + switch (idx) + { + case 0: + { + sys_log(0, "BlueDragon: Using Skill Breath"); + + FSkillBreath f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill0", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "period", "max")); + } + break; + + case 1: + { + sys_log(0, "BlueDragon: Using Skill Weak Breath"); + + FSkillWeakBreath f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill1", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "period", "max")); + } + break; + + case 2: + { + sys_log(0, "BlueDragon: Using Skill EarthQuake"); + + FSkillEarthQuake f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill2", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "period", "max")); + + if (NULL != f.pFarthestChar) + { + pChar->BeginFight( f.pFarthestChar ); + } + } + break; + + default: + sys_err("BlueDragon: Wrong Skill Index: %d", idx); + return 0; + } + + int addPct = BlueDragon_GetRangeFactor("hp_period", pChar->GetHPPct()); + + nextUsingTime += (nextUsingTime * addPct) / 100; + + return nextUsingTime; +} + +int BlueDragon_StateBattle(LPCHARACTER pChar) +{ + if (pChar->GetHPPct() > 98) + return PASSES_PER_SEC(1); + + const int SkillCount = 3; + int SkillPriority[SkillCount]; + static time_t timeSkillCanUseTime[SkillCount]; + + if (pChar->GetHPPct() > 76) + { + SkillPriority[0] = 1; + SkillPriority[1] = 0; + SkillPriority[2] = 2; + } + else if (pChar->GetHPPct() > 31) + { + SkillPriority[0] = 0; + SkillPriority[1] = 1; + SkillPriority[2] = 2; + } + else + { + SkillPriority[0] = 0; + SkillPriority[1] = 2; + SkillPriority[2] = 1; + } + + time_t timeNow = static_cast(get_dword_time()); + + for (int i=0 ; i < SkillCount ; ++i) + { + const int SkillIndex = SkillPriority[i]; + + if (timeSkillCanUseTime[SkillIndex] < timeNow) + { + int SkillUsingDuration = + static_cast(CMotionManager::instance().GetMotionDuration( pChar->GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + SkillIndex) )); + + timeSkillCanUseTime[SkillIndex] = timeNow + (UseBlueDragonSkill( pChar, SkillIndex ) * 1000) + SkillUsingDuration + 3000; + + pChar->SendMovePacket(FUNC_MOB_SKILL, SkillIndex, pChar->GetX(), pChar->GetY(), 0, timeNow); + + return 0 == SkillUsingDuration ? PASSES_PER_SEC(1) : PASSES_PER_SEC(SkillUsingDuration); + } + } + + return PASSES_PER_SEC(1); +} + +int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam) +{ + if (NULL == me || NULL == pAttacker) + return dam; + + if (true == pAttacker->IsMonster() && 2493 == pAttacker->GetMobTable().dwVnum) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (ATK_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( pAttacker->GetMapIndex(), dwDragonStoneID ); + + dam += (dam * (val*cnt))/100; + + break; + } + } + } + + if (true == me->IsMonster() && 2493 == me->GetMobTable().dwVnum) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (DEF_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( me->GetMapIndex(), dwDragonStoneID ); + + dam -= (dam * (val*cnt))/100; + + if (dam <= 0) + dam = 1; + + break; + } + } + } + + if (true == me->IsStone() && 0 != pAttacker->GetMountVnum()) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (me->GetMobTable().dwVnum == BlueDragon_GetIndexFactor("DragonStone", i, "vnum")) + { + if (pAttacker->GetMountVnum() == BlueDragon_GetIndexFactor("DragonStone", i, "enemy")) + { + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "enemy_val"); + + dam *= val; + + break; + } + } + } + } + + return dam; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/BlueDragon.h b/source-server/Srcs/Server/game/src/BlueDragon.h new file mode 100644 index 000000000..a53dd7a66 --- /dev/null +++ b/source-server/Srcs/Server/game/src/BlueDragon.h @@ -0,0 +1,4 @@ +extern int BlueDragon_StateBattle (LPCHARACTER); +extern time_t UseBlueDragonSkill (LPCHARACTER, unsigned int); +extern int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER attacker, int dam); +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/BlueDragon_Binder.cpp b/source-server/Srcs/Server/game/src/BlueDragon_Binder.cpp new file mode 100644 index 000000000..300e53bcd --- /dev/null +++ b/source-server/Srcs/Server/game/src/BlueDragon_Binder.cpp @@ -0,0 +1,217 @@ +#include "stdafx.h" + +#include "BlueDragon_Binder.h" + +#include "questmanager.h" + +unsigned int BlueDragon_GetSkillFactor(const size_t cnt, ...) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + va_list vl; + + va_start(vl, cnt); + + for( size_t i=0 ; i < cnt ; ++i ) + { + const char* key = va_arg(vl, const char*); + + if (NULL == key) + { + va_end(vl); + lua_settop( L, stack_top ); + sys_err("BlueDragon: wrong key list"); + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1) && i != cnt-1) + { + va_end(vl); + lua_settop( L, stack_top ); + sys_err("BlueDragon: wrong key table %s", key); + return 0; + } + } + + va_end(vl); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + sys_err("BlueDragon: Last key is not a number"); + return 0; + } + + int val = static_cast(lua_tonumber( L, -1 )); + + lua_settop( L, stack_top ); + + return val; +} + +unsigned int BlueDragon_GetRangeFactor(const char* key, const int val) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no required table %s", key); + return 0; + } + + const size_t cnt = static_cast(luaL_getn(L, -1)); + + for( size_t i=1 ; i <= cnt ; ++i ) + { + lua_rawgeti( L, -1, i ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: wrong table index %s %d", key, i); + return 0; + } + + lua_pushstring( L, "min" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no min value set %s", key); + return 0; + } + + const int min = static_cast(lua_tonumber(L, -1)); + + lua_pop(L, 1); + + lua_pushstring( L, "max" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no max value set %s", key); + return 0; + } + + const int max = static_cast(lua_tonumber(L, -1)); + + lua_pop(L, 1); + + if (min <= val && val <= max) + { + lua_pushstring( L, "pct" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no pct value set %s", key); + return 0; + } + + const int pct = static_cast(lua_tonumber(L, -1)); + + lua_settop( L, stack_top ); + + return pct; + } + + lua_pop(L, 1); + } + + lua_settop( L, stack_top ); + + return 0; +} + +unsigned int BlueDragon_GetIndexFactor(const char* container, const size_t idx, const char* key) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + lua_pushstring( L, container ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no required table %s", key); + return 0; + } + + lua_rawgeti( L, -1, idx ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: wrong table index %s %d", key, idx); + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no min value set %s", key); + return 0; + } + + const unsigned int ret = static_cast(lua_tonumber(L, -1)); + + lua_settop( L, stack_top ); + + return ret; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/BlueDragon_Binder.h b/source-server/Srcs/Server/game/src/BlueDragon_Binder.h new file mode 100644 index 000000000..c19ae9d47 --- /dev/null +++ b/source-server/Srcs/Server/game/src/BlueDragon_Binder.h @@ -0,0 +1,12 @@ +enum BLUEDRAGON_STONE_EFFECT +{ + DEF_BONUS = 1, + ATK_BONUS = 2, + REGEN_TIME_BONUS = 3, + REGEN_PECT_BONUS = 4, +}; + +extern unsigned int BlueDragon_GetRangeFactor (const char* key, const int val); +extern unsigned int BlueDragon_GetSkillFactor (const size_t cnt, ...); +extern unsigned int BlueDragon_GetIndexFactor (const char* container, const size_t idx, const char* key); +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/BlueDragon_Skill.h b/source-server/Srcs/Server/game/src/BlueDragon_Skill.h new file mode 100644 index 000000000..e8c1d889f --- /dev/null +++ b/source-server/Srcs/Server/game/src/BlueDragon_Skill.h @@ -0,0 +1,355 @@ +#ifndef _INC_BLUE_DRAGON_SKILL_H__INC_ +#define _INC_BLUE_DRAGON_SKILL_H__INC_ +#include "../../common/CommonDefines.h" +struct FSkillBreath +{ + EJobs Set1; + EJobs Set2; + ESex gender; + LPCHARACTER pAttacker; + + FSkillBreath(LPCHARACTER p) + { + pAttacker = p; + + Set1 = static_cast(number(0,3)); + Set2 = static_cast(number(0,3)); + gender = static_cast(number(0,2)); + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill0", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int overlapDamageCount = 0; + + int pct = 0; + if (ch->GetJob() == Set1) + { + const char* ptr = NULL; + + switch ( Set1 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: ptr = "wolf"; break; +#endif + + default: + case JOB_MAX_NUM: return; + } + + int firstDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max")); + pct += firstDamagePercent; + + if (firstDamagePercent > 0) + overlapDamageCount++; + } + + if (ch->GetJob() == Set2) + { + const char* ptr = NULL; + + switch ( Set2 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: ptr = "wolf"; break; +#endif + + default: + case JOB_MAX_NUM: return; + } + + int secondDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max")); + pct += secondDamagePercent; + + if (secondDamagePercent > 0) + overlapDamageCount++; + } + + if (GET_SEX(ch) == gender) + { + const char* ptr = NULL; + + switch (gender) + { + case SEX_MALE: ptr = "male"; break; + case SEX_FEMALE: ptr = "female"; break; + default: return; + } + + int thirdDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "max")); + pct += thirdDamagePercent; + + if (thirdDamagePercent > 0) + overlapDamageCount++; + } + + switch (overlapDamageCount) + { + case 1: + ch->EffectPacket(SE_PERCENT_DAMAGE1); + break; + case 2: + ch->EffectPacket(SE_PERCENT_DAMAGE2); + break; + case 3: + ch->EffectPacket(SE_PERCENT_DAMAGE3); + break; + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + pct += addPct; + + int dam = number(BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "max")); + + dam += (dam * addPct) / 100; + dam += (ch->GetMaxHP() * pct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE ); + + sys_log(0, "BlueDragon: Breath to %s pct(%d) dam(%d) overlap(%d)", ch->GetName(), pct, dam, overlapDamageCount); + } + } + } + } +}; + +struct FSkillWeakBreath +{ + LPCHARACTER pAttacker; + + FSkillWeakBreath(LPCHARACTER p) + { + pAttacker = p; + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill1", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + int dam = number( BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "max") ); + dam += (dam * addPct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE ); + + sys_log(0, "BlueDragon: WeakBreath to %s addPct(%d) dam(%d)", ch->GetName(), addPct, dam); + } + } + } + } +}; + +struct FSkillEarthQuake +{ + EJobs Set1; + EJobs Set2; + ESex gender; + long MaxDistance; + LPCHARACTER pAttacker; + LPCHARACTER pFarthestChar; + + FSkillEarthQuake(LPCHARACTER p) + { + pAttacker = p; + + MaxDistance = 0; + pFarthestChar = NULL; + + Set1 = static_cast(number(0,3)); + Set2 = static_cast(number(0,3)); + gender = static_cast(number(0,2)); + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill2", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int sec = number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "max")); + + if (ch->GetJob() == Set1) + { + const char* ptr = NULL; + + switch ( Set1 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: ptr = "wolf"; break; +#endif + + default: + case JOB_MAX_NUM: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max")); + } + + if (ch->GetJob() == Set2) + { + const char* ptr = NULL; + + switch ( Set2 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: ptr = "wolf"; break; +#endif + + default: + case JOB_MAX_NUM: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max")); + } + + if (GET_SEX(ch) == gender) + { + const char* ptr = NULL; + + switch (gender) + { + case SEX_MALE: ptr = "male"; break; + case SEX_FEMALE: ptr = "female"; break; + default: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "max")); + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + int dam = number( BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "max") ); + dam += (dam * addPct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE); + + SkillAttackAffect( ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" ); + + sys_log(0, "BlueDragon: EarthQuake to %s addPct(%d) dam(%d) sec(%d)", ch->GetName(), addPct, dam, sec); + + VECTOR vec; + vec.x = static_cast(pAttacker->GetX() - ch->GetX()); + vec.y = static_cast(pAttacker->GetY() - ch->GetY()); + vec.z = 0.0f; + + Normalize( &vec, &vec ); + + const int nFlyDistance = 1000; + + long tx = ch->GetX() + vec.x * nFlyDistance; + long ty = ch->GetY() + vec.y * nFlyDistance; + + for (int i=0 ; i < 5 ; ++i) + { + if (true == SECTREE_MANAGER::instance().IsMovablePosition( ch->GetMapIndex(), tx, ty )) + { + break; + } + + switch( i ) + { + case 0: + tx = ch->GetX() + vec.x * nFlyDistance * -1; + ty = ch->GetY() + vec.y * nFlyDistance * -1; + break; + case 1: + tx = ch->GetX() + vec.x * nFlyDistance * -1; + ty = ch->GetY() + vec.y * nFlyDistance; + break; + case 2: + tx = ch->GetX() + vec.x * nFlyDistance; + ty = ch->GetY() + vec.y * nFlyDistance * -1; + break; + case 3: + tx = ch->GetX() + vec.x * number(1,100); + ty = ch->GetY() + vec.y * number(1,100); + break; + case 4: + tx = ch->GetX() + vec.x * number(1,10); + ty = ch->GetY() + vec.y * number(1,10); + break; + } + } + + ch->Sync( tx , ty ); + ch->Goto( tx , ty ); + ch->CalculateMoveDuration(); + + ch->SyncPacket(); + + long dist = DISTANCE_APPROX( pAttacker->GetX() - ch->GetX(), pAttacker->GetY() - ch->GetY() ); + + if (dist > MaxDistance) + { + MaxDistance = dist; + pFarthestChar = ch; + } + } + } + } + } +}; +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.cpp b/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.cpp new file mode 100644 index 000000000..27779d603 --- /dev/null +++ b/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.cpp @@ -0,0 +1,222 @@ +#include "stdafx.h" +#include "ClientPackageCryptInfo.h" +#include "../../common/stl.h" + +#ifndef __FreeBSD__ +#include "../../libthecore/include/xdirent.h" +#endif + +CClientPackageCryptInfo::CClientPackageCryptInfo() : m_nCryptKeyPackageCnt(0), m_pSerializedCryptKeyStream(NULL) +{ +} + +CClientPackageCryptInfo::~CClientPackageCryptInfo() +{ + m_vecPackageCryptKeys.clear(); + m_mapPackageSDB.clear(); + if( m_pSerializedCryptKeyStream ) + { + delete[] m_pSerializedCryptKeyStream; + m_pSerializedCryptKeyStream = NULL; + } +} + +bool CClientPackageCryptInfo::LoadPackageCryptFile( const char* pCryptFile ) +{ + FILE * fp = fopen(pCryptFile, "rb"); + + if (!fp) + return false; + + int iSDBDataOffset; + fread(&iSDBDataOffset, sizeof(int), 1, fp); + + int iPackageCnt; + fread( &iPackageCnt, sizeof(int), 1, fp ); + m_nCryptKeyPackageCnt += iPackageCnt; + + int iCryptKeySize = iSDBDataOffset - 2*sizeof(int); + + { + if (0 == iCryptKeySize) + { + sys_log(0, "[PackageCryptInfo] failed to load crypt key. (file: %s, key size: %d)", pCryptFile, iCryptKeySize); + m_nCryptKeyPackageCnt -= iPackageCnt; + } + else + { + int nCurKeySize = (int)m_vecPackageCryptKeys.size(); + m_vecPackageCryptKeys.resize( nCurKeySize + sizeof(int) + iCryptKeySize); + + memcpy( &m_vecPackageCryptKeys[nCurKeySize], &iCryptKeySize, sizeof(int)); + fread( &m_vecPackageCryptKeys[nCurKeySize + sizeof(int)], sizeof(BYTE), iCryptKeySize, fp ); + sys_log(0, "[PackageCryptInfo] %s loaded. (key size: %d, count: %d, total: %d)", pCryptFile, iCryptKeySize, iPackageCnt, m_nCryptKeyPackageCnt); + } + } + + //about SDB data + //total packagecnt (4byte) + // for packagecnt + // db name hash 4byte( stl.h stringhash ) +child node size(4byte) + + //stream to client + // sdb file cnt( 4byte ) + // for sdb file cnt + // filename hash ( stl.h stringhash ) + // related map name size(4), relate map name + // sdb block size( 1byte ) + // sdb blocks + + int iSDBPackageCnt; + fread(&iSDBPackageCnt, sizeof(int), 1, fp); + + DWORD dwPackageNameHash, dwPackageStreamSize, dwSDBFileCnt, dwFileNameHash, dwMapNameSize; + + std::string strRelatedMapName; + + if (0 == iCryptKeySize && 0 == iSDBPackageCnt) + return false; + + for( int i = 0; i < iSDBPackageCnt; ++i ) + { + fread(&dwPackageNameHash, sizeof(DWORD), 1, fp); + fread(&dwPackageStreamSize, sizeof(DWORD), 1, fp); + + fread(&dwSDBFileCnt, sizeof(DWORD), 1, fp); + + sys_log(0, "[PackageCryptInfo] SDB Loaded. (Name Hash : %d, Stream Size: %d, File Count: %d)", dwPackageNameHash,dwPackageStreamSize, dwSDBFileCnt); + + for( int j = 0; j < (int)dwSDBFileCnt; ++j ) + { + fread(&dwFileNameHash, sizeof(DWORD), 1, fp); + fread(&dwMapNameSize, sizeof(DWORD), 1, fp); + + strRelatedMapName.resize( dwMapNameSize ); + fread(&strRelatedMapName[0], sizeof(BYTE), dwMapNameSize, fp); + + sys_log(0, "[PackageCryptInfo] \t SDB each file info loaded.(MapName: %s, NameHash: %X)", strRelatedMapName.c_str(), dwFileNameHash); + + BYTE bSDBStreamSize; + std::vector vecSDBStream; + fread(&bSDBStreamSize, sizeof(BYTE), 1, fp); + + vecSDBStream.resize(bSDBStreamSize); + fread(&vecSDBStream[0], sizeof(BYTE), bSDBStreamSize, fp); + + //reconstruct it + TPackageSDBMap::iterator it = m_mapPackageSDB.find( strRelatedMapName ); + if (it == m_mapPackageSDB.end()) + { + TPerFileSDBInfo fileSDBInfo; + m_mapPackageSDB[strRelatedMapName] = fileSDBInfo; + } + + TSupplementaryDataBlockInfo SDBInfo; + std::vector& rSDBInfos = m_mapPackageSDB[strRelatedMapName].vecSDBInfos; + { + SDBInfo.dwPackageIdentifier = dwPackageNameHash; + SDBInfo.dwFileIdentifier = dwFileNameHash; + SDBInfo.vecSDBStream.resize(bSDBStreamSize); + + memcpy(&SDBInfo.vecSDBStream[0], &vecSDBStream[0], bSDBStreamSize); + + rSDBInfos.emplace_back(SDBInfo); + } + } + } + + fclose(fp); + return true; +} + +bool CClientPackageCryptInfo::LoadPackageCryptInfo( const char* pCryptInfoDir ) +{ + DIR * pDir = opendir(pCryptInfoDir); + + if (!pDir) + return false; + + m_nCryptKeyPackageCnt = 0; + if( m_pSerializedCryptKeyStream ) + { + delete[] m_pSerializedCryptKeyStream; + m_pSerializedCryptKeyStream = NULL; + } + + m_mapPackageSDB.clear(); + m_vecPackageCryptKeys.clear(); + + const char szPrefixCryptInfoFile[] = "cshybridcrypt"; + + dirent * pDirEnt; + while ((pDirEnt = readdir(pDir))) + { + //if (strncmp( &(pDirEnt->d_name[0]), szPrefixCryptInfoFile, strlen(szPrefixCryptInfoFile)) ) + if (std::string::npos == std::string(pDirEnt->d_name).find(szPrefixCryptInfoFile)) + { + sys_log(0, "[PackageCryptInfo] %s is not crypt file. pass!", pDirEnt->d_name); + continue; + } + + std::string strFullPathName = std::string(pCryptInfoDir) + std::string(pDirEnt->d_name); + + sys_log(0, "[PackageCryptInfo] Try to load crypt file: %s", strFullPathName.c_str()); + + if (false == LoadPackageCryptFile( strFullPathName.c_str() )) + sys_err("[PackageCryptInfo] Failed to load %s", strFullPathName.c_str()); + } + + closedir(pDir); + return true; +} + +void CClientPackageCryptInfo::GetPackageCryptKeys( BYTE** ppData, int& iDataSize ) +{ + int nCryptKeySize = m_vecPackageCryptKeys.size(); + int iStreamSize = sizeof(int)+nCryptKeySize; + + //NOTE : Crypt Key Info isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated ) + //it`s not safe but due to performance benefit we don`t do re-serialize. + if( m_pSerializedCryptKeyStream ) + { + *ppData = m_pSerializedCryptKeyStream; + iDataSize = iStreamSize; + return; + } + + if( nCryptKeySize > 0 ) + { + m_pSerializedCryptKeyStream = new BYTE[iStreamSize]; + memcpy(&m_pSerializedCryptKeyStream[0], &m_nCryptKeyPackageCnt, sizeof(int) ); + memcpy(&m_pSerializedCryptKeyStream[sizeof(int)], &m_vecPackageCryptKeys[0], nCryptKeySize ); + + *ppData = m_pSerializedCryptKeyStream; + iDataSize = iStreamSize; + } + else + { + *ppData = NULL; + iDataSize = 0; + } +} + +bool CClientPackageCryptInfo::GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize ) +{ + std::string strLowerMapName = pMapName; + stl_lowers(strLowerMapName); + + TPackageSDBMap::iterator it = m_mapPackageSDB.find( strLowerMapName.c_str() ); + if( it == m_mapPackageSDB.end() || it->second.vecSDBInfos.size() == 0 ) + { + //sys_err("GetRelatedMapSDBStreams Failed(%s)", strLowerMapName.c_str()); + return false; + } + + *ppData = it->second.GetSerializedStream(); + iDataSize = it->second.GetSize(); + + //sys_log(0, "GetRelatedMapSDBStreams Size(%d)", iDataSize); + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.h b/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.h new file mode 100644 index 000000000..26e236c82 --- /dev/null +++ b/source-server/Srcs/Server/game/src/ClientPackageCryptInfo.h @@ -0,0 +1,110 @@ +#ifndef __INC_CLIENTPACKAGE_CRYPTINFO_H +#define __INC_CLIENTPACKAGE_CRYPTINFO_H + +#include + +#pragma pack(1) + +typedef struct SSupplementaryDataBlockInfo +{ + DWORD dwPackageIdentifier; + DWORD dwFileIdentifier; + std::vector vecSDBStream; + + void Serialize( BYTE* pStream ) + { + memcpy(pStream, &dwPackageIdentifier, sizeof(DWORD)); + memcpy(pStream+4, &dwFileIdentifier, sizeof(DWORD)); + + BYTE bSize = vecSDBStream.size(); + memcpy(pStream+8, &bSize, sizeof(BYTE)); + memcpy(pStream+9, &vecSDBStream[0], bSize); + } + + DWORD GetSerializedSize() const + { + return sizeof(DWORD)*2 + sizeof(BYTE) + vecSDBStream.size(); + } +} TSupplementaryDataBlockInfo; + +#pragma pack() + +class CClientPackageCryptInfo +{ +public: + CClientPackageCryptInfo(); + ~CClientPackageCryptInfo(); + + bool LoadPackageCryptInfo( const char* pCryptInfoDir ); + void GetPackageCryptKeys( BYTE** ppData, int& iDataSize ); + + bool GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize ); + +private: + bool LoadPackageCryptFile( const char* pCryptFile ); + +private: + int m_nCryptKeyPackageCnt; + std::vector m_vecPackageCryptKeys; + BYTE* m_pSerializedCryptKeyStream; + + typedef struct SPerFileSDBInfo + { + SPerFileSDBInfo() : m_pSerializedStream(NULL) {} + ~SPerFileSDBInfo() + { + if(m_pSerializedStream) + { + delete[]m_pSerializedStream; + } + } + + DWORD GetSize() const + { + DWORD dwSize = 4; //initial vecSDBInfo count + + for(int i = 0; i < (int)vecSDBInfos.size(); ++i) + { + dwSize += vecSDBInfos[i].GetSerializedSize(); + } + + return dwSize; + } + + BYTE* GetSerializedStream() + { + //NOTE : SDB Data isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated ) + //it`s not safe but due to performance benefit we don`t do re-serialize. + if(m_pSerializedStream) + return m_pSerializedStream; + + m_pSerializedStream = new BYTE[GetSize()]; + + int iWrittenOffset = 0; + int iSDBInfoSize = vecSDBInfos.size(); + + //write size + memcpy( m_pSerializedStream, &iSDBInfoSize, sizeof(int) ); + iWrittenOffset += sizeof(int); + for(int i = 0; i < iSDBInfoSize; ++i) + { + vecSDBInfos[i].Serialize( m_pSerializedStream + iWrittenOffset ); + iWrittenOffset += vecSDBInfos[i].GetSerializedSize(); + } + + return m_pSerializedStream; + } + + std::vector vecSDBInfos; + + private: + BYTE* m_pSerializedStream; + + } TPerFileSDBInfo; + + typedef boost::unordered_map TPackageSDBMap; //key: related map name + TPackageSDBMap m_mapPackageSDB; +}; + +#endif //__INC_CLIENTPACKAGE_CRYPTINFO_H +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/DragonLair.cpp b/source-server/Srcs/Server/game/src/DragonLair.cpp new file mode 100644 index 000000000..5a3397657 --- /dev/null +++ b/source-server/Srcs/Server/game/src/DragonLair.cpp @@ -0,0 +1,232 @@ +#include "stdafx.h" +#include "config.h" + +#include "DragonLair.h" + +#include "entity.h" +#include "sectree_manager.h" +#include "char.h" +#include "guild.h" +#include "locale_service.h" +#include "regen.h" +#include "log.h" +#include "utils.h" + +struct FWarpToDragronLairWithGuildMembers +{ + DWORD dwGuildID; + long mapIndex; + long x, y; + + FWarpToDragronLairWithGuildMembers( DWORD guildID, long map, long X, long Y ) + : dwGuildID(guildID), mapIndex(map), x(X), y(Y) + { + } + + void operator()(LPENTITY ent) + { + if (NULL != ent && true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER pChar = static_cast(ent); + + if (true == pChar->IsPC()) + { + if (NULL != pChar->GetGuild()) + { + if (dwGuildID == pChar->GetGuild()->GetID()) + { + pChar->WarpSet(x, y, mapIndex); + } + } + } + } + } +}; + +struct FWarpToVillage +{ + void operator() (LPENTITY ent) + { + if (NULL != ent) + { + LPCHARACTER pChar = static_cast(ent); + + if (NULL != pChar) + { + if (true == pChar->IsPC()) + { + pChar->GoHome(); + } + } + } + } +}; + +EVENTINFO(tag_DragonLair_Collapse_EventInfo) +{ + int step; + CDragonLair* pLair; + long InstanceMapIndex; + + tag_DragonLair_Collapse_EventInfo() + : step( 0 ) + , pLair( 0 ) + , InstanceMapIndex( 0 ) + { + } +}; + +EVENTFUNC( DragonLair_Collapse_Event ) +{ + tag_DragonLair_Collapse_EventInfo* pInfo = dynamic_cast(event->info); + + if ( pInfo == NULL ) + { + sys_err( "DragonLair_Collapse_Event> Null pointer" ); + return 0; + } + + if (0 == pInfo->step) + { + char buf[512]; + snprintf(buf, 512, LC_TEXT("밡 %d ʸ ׾ȿФ"), pInfo->pLair->GetEstimatedTime()); + SendNoticeMap(buf, pInfo->InstanceMapIndex, true); + + pInfo->step++; + + return PASSES_PER_SEC( 30 ); + } + else if (1 == pInfo->step) + { + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pInfo->InstanceMapIndex ); + + if (NULL != pMap) + { + FWarpToVillage f; + pMap->for_each( f ); + } + + pInfo->step++; + + return PASSES_PER_SEC( 30 ); + } + else + { + SECTREE_MANAGER::instance().DestroyPrivateMap( pInfo->InstanceMapIndex ); + M2_DELETE(pInfo->pLair); + } + + return 0; +} + +CDragonLair::CDragonLair(DWORD guildID, long BaseMapID, long PrivateMapID) + : GuildID_(guildID), BaseMapIndex_(BaseMapID), PrivateMapIndex_(PrivateMapID) +{ + StartTime_ = get_global_time(); +} + +CDragonLair::~CDragonLair() +{ +} + +DWORD CDragonLair::GetEstimatedTime() const +{ + return get_global_time() - StartTime_; +} + +void CDragonLair::OnDragonDead(LPCHARACTER pDragon) +{ + sys_log(0, "DragonLair: ׾ȿ"); + + LogManager::instance().DragonSlayLog( GuildID_, pDragon->GetMobTable().dwVnum, StartTime_, get_global_time() ); +} + +CDragonLairManager::CDragonLairManager() +{ +} + +CDragonLairManager::~CDragonLairManager() +{ +} + +bool CDragonLairManager::Start(long MapIndexFrom, long BaseMapIndex, DWORD GuildID) +{ + long instanceMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(BaseMapIndex); + if (instanceMapIndex == 0) { + sys_err("CDragonLairManager::Start() : no private map index available"); + return false; + } + + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(MapIndexFrom); + + if (NULL != pMap) + { + LPSECTREE_MAP pTargetMap = SECTREE_MANAGER::instance().GetMap(BaseMapIndex); + + if (NULL == pTargetMap) + { + return false; + } + + const TMapRegion* pRegionInfo = SECTREE_MANAGER::instance().GetMapRegion( pTargetMap->m_setting.iIndex ); + + if (NULL != pRegionInfo) + { + FWarpToDragronLairWithGuildMembers f(GuildID, instanceMapIndex, 844000, 1066900); + + pMap->for_each( f ); + + LairMap_.emplace(GuildID, M2_NEW CDragonLair(GuildID, BaseMapIndex, instanceMapIndex)); + + std::string strMapBasePath( LocaleService_GetMapPath() ); + + strMapBasePath += "/" + pRegionInfo->strMapName + "/instance_regen.txt"; + + sys_log(0, "%s", strMapBasePath.c_str()); + + regen_do(strMapBasePath.c_str(), instanceMapIndex, pTargetMap->m_setting.iBaseX, pTargetMap->m_setting.iBaseY, NULL, true); + + return true; + } + } + + return false; +} + +void CDragonLairManager::OnDragonDead(LPCHARACTER pDragon, DWORD KillerGuildID) +{ + if (NULL == pDragon) + return; + + if (false == pDragon->IsMonster()) + return; + + boost::unordered_map::iterator iter = LairMap_.find( KillerGuildID ); + + if (LairMap_.end() == iter) + { + return; + } + + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pDragon->GetMapIndex() ); + + if (NULL == iter->second || NULL == pMap) + { + LairMap_.erase( iter ); + return; + } + + iter->second->OnDragonDead( pDragon ); + + tag_DragonLair_Collapse_EventInfo* info; + info = AllocEventInfo(); + + info->step = 0; + info->pLair = iter->second; + info->InstanceMapIndex = pDragon->GetMapIndex(); + + event_create(DragonLair_Collapse_Event, info, PASSES_PER_SEC(10)); + + LairMap_.erase( iter ); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/DragonLair.h b/source-server/Srcs/Server/game/src/DragonLair.h new file mode 100644 index 000000000..7e5a77d9e --- /dev/null +++ b/source-server/Srcs/Server/game/src/DragonLair.h @@ -0,0 +1,36 @@ +#include + +#include "../../common/stl.h" + +class CDragonLair +{ + public: + CDragonLair (DWORD dwGuildID, long BaseMapID, long PrivateMapID); + virtual ~CDragonLair (); + + DWORD GetEstimatedTime () const; + + void OnDragonDead (LPCHARACTER pDragon); + + private: + DWORD StartTime_; + DWORD GuildID_; + [[maybe_unused]] long BaseMapIndex_; + [[maybe_unused]] long PrivateMapIndex_; +}; + +class CDragonLairManager : public singleton +{ + public: + CDragonLairManager (); + virtual ~CDragonLairManager (); + + bool Start (long MapIndexFrom, long BaseMapIndex, DWORD GuildID); + void OnDragonDead (LPCHARACTER pDragon, DWORD KillerGuildID); + + size_t GetLairCount () const { return LairMap_.size(); } + + private: + boost::unordered_map LairMap_; +}; +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/DragonSoul.cpp b/source-server/Srcs/Server/game/src/DragonSoul.cpp new file mode 100644 index 000000000..971a24beb --- /dev/null +++ b/source-server/Srcs/Server/game/src/DragonSoul.cpp @@ -0,0 +1,1113 @@ +#include "stdafx.h" +#include "constants.h" +#include "item.h" +#include "item_manager.h" +#include "unique_item.h" +#include "packet.h" +#include "desc.h" +#include "char.h" +#include "dragon_soul_table.h" +#include "log.h" +#include "DragonSoul.h" +#include + +typedef std::vector TTokenVector; + +int Gamble(std::vector& vec_probs) +{ + float range = 0.f; + for (size_t i = 0; i < vec_probs.size(); i++) + { + range += vec_probs[i]; + } + float fProb = fnumber(0.f, range); + float sum = 0.f; + for (size_t idx = 0; idx < vec_probs.size(); idx++) + { + sum += vec_probs[idx]; + if (sum >= fProb) + return idx; + } + return -1; +} + +bool MakeDistinctRandomNumberSet(std::list prob_lst, OUT std::vector& random_set) +{ + int size = prob_lst.size(); + int n = random_set.size(); + if (size < n) + return false; + + std::vector select_bit(size, 0); + for (int i = 0; i < n; i++) + { + float range = 0.f; + for (std::list ::iterator it = prob_lst.begin(); it != prob_lst.end(); it++) + { + range += *it; + } + float r = fnumber (0.f, range); + float sum = 0.f; + int idx = 0; + for (std::list ::iterator it = prob_lst.begin(); it != prob_lst.end(); it++) + { + while (select_bit[idx++]); + + sum += *it; + if (sum >= r) + { + select_bit[idx - 1] = 1; + random_set[i] = idx - 1; + prob_lst.erase(it); + break; + } + } + } + return true; +} + +BYTE GetType(DWORD dwVnum) +{ + return (dwVnum / 10000); +} + +BYTE GetGradeIdx(DWORD dwVnum) +{ + return (dwVnum / 1000) % 10; +} + +BYTE GetStepIdx(DWORD dwVnum) +{ + return (dwVnum / 100) % 10; +} + +BYTE GetStrengthIdx(DWORD dwVnum) +{ + return (dwVnum / 10) % 10; +} + +bool DSManager::ReadDragonSoulTableFile(const char * c_pszFileName) +{ + m_pTable = new DragonSoulTable(); + return m_pTable->ReadDragonSoulTableFile(c_pszFileName); +} + +void DSManager::GetDragonSoulInfo(DWORD dwVnum, BYTE& bType, BYTE& bGrade, BYTE& bStep, BYTE& bStrength) const +{ + bType = GetType(dwVnum); + bGrade = GetGradeIdx(dwVnum); + bStep = GetStepIdx(dwVnum); + bStrength = GetStrengthIdx(dwVnum); +} + +bool DSManager::IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const +{ + if (NULL == pItem) + return false; + + WORD wBaseCell = GetBasePosition(pItem); + if (WORD_MAX == wBaseCell) + return false; + + if (Cell.window_type != DRAGON_SOUL_INVENTORY + || (Cell.cell < wBaseCell || Cell.cell >= wBaseCell + DRAGON_SOUL_BOX_SIZE)) + { + return false; + } + else + return true; +} + +WORD DSManager::GetBasePosition(const LPITEM pItem) const +{ + if (NULL == pItem) + return WORD_MAX; + + BYTE type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pItem->GetVnum(), type, grade_idx, step_idx, strength_idx); + + BYTE col_type = pItem->GetSubType(); + BYTE row_type = grade_idx; + if (row_type > DRAGON_SOUL_GRADE_MAX) + return WORD_MAX; + + return col_type * DRAGON_SOUL_GRADE_MAX * DRAGON_SOUL_BOX_SIZE + row_type * DRAGON_SOUL_BOX_SIZE; // @fixme322 DRAGON_SOUL_STEP_MAX -> DRAGON_SOUL_GRADE_MAX +} + +bool DSManager::RefreshItemAttributes(LPITEM pDS) +{ + if (!pDS->IsDragonSoul()) + { + sys_err ("This item(ID : %d) is not DragonSoul.", pDS->GetID()); + return false; + } + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pDS->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + DragonSoulTable::TVecApplys vec_basic_applys; + DragonSoulTable::TVecApplys vec_addtional_applys; + + if (!m_pTable->GetBasicApplys(ds_type, vec_basic_applys)) + { + sys_err ("There is no BasicApply about %d type dragon soul.", ds_type); + return false; + } + + if (!m_pTable->GetAdditionalApplys(ds_type, vec_addtional_applys)) + { + sys_err ("There is no AdditionalApply about %d type dragon soul.", ds_type); + return false; + } + + int basic_apply_num, add_min, add_max; + if (!m_pTable->GetApplyNumSettings(ds_type, grade_idx, basic_apply_num, add_min, add_max)) + { + sys_err ("In ApplyNumSettings, INVALID VALUES Group type(%d), GRADE idx(%d)", ds_type, grade_idx); + return false; + } + + float fWeight = 0.f; + if (!m_pTable->GetWeight(ds_type, grade_idx, step_idx, strength_idx, fWeight)) + { + return false; + } + fWeight /= 100.f; + + int n = MIN(basic_apply_num, vec_basic_applys.size()); + for (int i = 0; i < n; i++) + { + const SApply& basic_apply = vec_basic_applys[i]; + BYTE bType = basic_apply.apply_type; + short sValue = (short)(ceil((float)basic_apply.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(i, bType, sValue); + } + + for (int i = DRAGON_SOUL_ADDITIONAL_ATTR_START_IDX; i < ITEM_ATTRIBUTE_MAX_NUM; i++) + { + BYTE bType = pDS->GetAttributeType(i); + short sValue = 0; + if (APPLY_NONE == bType) + continue; + for (size_t j = 0; j < vec_addtional_applys.size(); j++) + { + if (vec_addtional_applys[j].apply_type == bType) + { + sValue = vec_addtional_applys[j].apply_value; + break; + } + } + pDS->SetForceAttribute(i, bType, (short)(ceil((float)sValue * fWeight - 0.01f))); + } + return true; +} + +bool DSManager::PutAttributes(LPITEM pDS) +{ + if (!pDS->IsDragonSoul()) + { + sys_err ("This item(ID : %d) is not DragonSoul.", pDS->GetID()); + return false; + } + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pDS->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + DragonSoulTable::TVecApplys vec_basic_applys; + DragonSoulTable::TVecApplys vec_addtional_applys; + + if (!m_pTable->GetBasicApplys(ds_type, vec_basic_applys)) + { + sys_err ("There is no BasicApply about %d type dragon soul.", ds_type); + return false; + } + if (!m_pTable->GetAdditionalApplys(ds_type, vec_addtional_applys)) + { + sys_err ("There is no AdditionalApply about %d type dragon soul.", ds_type); + return false; + } + + int basic_apply_num, add_min, add_max; + if (!m_pTable->GetApplyNumSettings(ds_type, grade_idx, basic_apply_num, add_min, add_max)) + { + sys_err ("In ApplyNumSettings, INVALID VALUES Group type(%d), GRADE idx(%d)", ds_type, grade_idx); + return false; + } + + float fWeight = 0.f; + if (!m_pTable->GetWeight(ds_type, grade_idx, step_idx, strength_idx, fWeight)) + { + return false; + } + fWeight /= 100.f; + + int n = MIN(basic_apply_num, vec_basic_applys.size()); + for (int i = 0; i < n; i++) + { + const SApply& basic_apply = vec_basic_applys[i]; + BYTE bType = basic_apply.apply_type; + short sValue = (short)(ceil((float)basic_apply.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(i, bType, sValue); + } + + BYTE additional_attr_num = MIN(number (add_min, add_max), 3); + + std::vector random_set; + if (additional_attr_num > 0) + { + random_set.resize(additional_attr_num); + std::list list_probs; + for (size_t i = 0; i < vec_addtional_applys.size(); i++) + { + list_probs.emplace_back(vec_addtional_applys[i].prob); + } + if (!MakeDistinctRandomNumberSet(list_probs, random_set)) + { + sys_err ("MakeDistinctRandomNumberSet error."); + return false; + } + + for (int i = 0; i < additional_attr_num; i++) + { + int r = random_set[i]; + const SApply& additional_attr = vec_addtional_applys[r]; + BYTE bType = additional_attr.apply_type; + short sValue = (short)(ceil((float)additional_attr.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(DRAGON_SOUL_ADDITIONAL_ATTR_START_IDX + i, bType, sValue); + } + } + + return true; +} + +bool DSManager::DragonSoulItemInitialize(LPITEM pItem) +{ + if (NULL == pItem || !pItem->IsDragonSoul()) + return false; + PutAttributes(pItem); + int time = DSManager::instance().GetDuration(pItem); + if (time > 0) + pItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, time); + return true; +} + +DWORD DSManager::MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine) +{ + return bType * 10000 + grade * 1000 + step * 100 + refine * 10; +} + +int DSManager::GetDuration(const LPITEM pItem) const +{ + return pItem->GetDuration(); +} + +bool DSManager::ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor) +{ + if (NULL == ch || NULL == pItem) + return false; + if (pItem->IsEquipped()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȥ ϴ.")); + return false; + } + + DWORD dwVnum = pItem->GetVnum(); + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(dwVnum, ds_type, grade_idx, step_idx, strength_idx); + + int iBonus = 0; + + if (NULL != pExtractor) + { + iBonus = pExtractor->GetValue(0); + } + + std::vector vec_chargings; + std::vector vec_probs; + + if (!m_pTable->GetDragonHeartExtValues(ds_type, grade_idx, vec_chargings, vec_probs)) + { + return false; + } + + int idx = Gamble(vec_probs); + + //float sum = 0.f; + if (-1 == idx) + { + sys_err ("Gamble is failed. ds_type(%d), grade_idx(%d)", ds_type, grade_idx); + return false; + } + + float fCharge = vec_chargings[idx] * (100 + iBonus) / 100.f; + fCharge = std::MINMAX (0.f, fCharge, 100.f); + + if (fCharge < FLT_EPSILON) + { + pItem->SetCount(pItem->GetCount() - 1); + if (NULL != pExtractor) + { + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_FAIL", ""); + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ⿡ Ͽϴ.")); + return false; + } + else + { + LPITEM pDH = ITEM_MANAGER::instance().CreateItem(DRAGON_HEART_VNUM); + + if (NULL == pDH) + { + sys_err ("Cannot create DRAGON_HEART(%d).", DRAGON_HEART_VNUM); + return false; + } + + pItem->SetCount(pItem->GetCount() - 1); + if (NULL != pExtractor) + { + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + + int iCharge = (int)(fCharge + 0.5f); + pDH->SetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX, iCharge); + ch->AutoGiveItem(pDH, true); + + std::string s = boost::lexical_cast (iCharge); + s += "%s"; + LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_SUCCESS", s.c_str()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ⿡ Ͽϴ.")); + return true; + } +} + +bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM pExtractor) +{ + if (NULL == ch || NULL == pItem) + { + sys_err ("NULL POINTER. ch(%p) or pItem(%p)", ch, pItem); + return false; + } + + if (!IsValidCellForThisItem(pItem, DestCell) || ch->GetItem(DestCell)) // @fixme160 (added GetItem check) + { + int iEmptyCell = ch->GetEmptyDragonSoulInventory(pItem); + if (iEmptyCell < 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + return false; + } + else + { + DestCell.window_type = DRAGON_SOUL_INVENTORY; + DestCell.cell = iEmptyCell; + } + } + + if (!pItem->IsEquipped() || !pItem->RemoveFromCharacter()) + return false; + + bool bSuccess; + DWORD dwByProduct = 0; + int iBonus = 0; + float fProb; + float fDice; + + { + //DWORD dwVnum = pItem->GetVnum(); + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + if (!m_pTable->GetDragonSoulExtValues(ds_type, grade_idx, fProb, dwByProduct)) + { + pItem->AddToCharacter(ch, DestCell); + return true; + } + + if (NULL != pExtractor) + { + iBonus = pExtractor->GetValue(ITEM_VALUE_DRAGON_SOUL_POLL_OUT_BONUS_IDX); + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + fDice = fnumber(0.f, 100.f); + bSuccess = fDice <= (fProb * (100 + iBonus) / 100.f); + } + + { + char buf[128]; + + if (bSuccess) + { + if (pExtractor) + { + sprintf(buf, "dice(%d) prob(%d + %d) EXTR(VN:%d)", (int)fDice, (int)fProb, iBonus, pExtractor->GetVnum()); + } + else + { + sprintf(buf, "dice(%d) prob(%d)", (int)fDice, (int)fProb); + } + LogManager::instance().ItemLog(ch, pItem, "DS_PULL_OUT_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + pItem->AddToCharacter(ch, DestCell); + return true; + } + else + { + if (pExtractor) + { + sprintf(buf, "dice(%d) prob(%d + %d) EXTR(VN:%d) ByProd(VN:%d)", (int)fDice, (int)fProb, iBonus, pExtractor->GetVnum(), dwByProduct); + } + else + { + sprintf(buf, "dice(%d) prob(%d) ByProd(VNUM:%d)", (int)fDice, (int)fProb, dwByProduct); + } + LogManager::instance().ItemLog(ch, pItem, "DS_PULL_OUT_FAILED", buf); + M2_DESTROY_ITEM(pItem); + pItem = NULL; + if (dwByProduct) + { + LPITEM pByProduct = ch->AutoGiveItem(dwByProduct, true); + if (pByProduct) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽ %s ϴ."), pByProduct->GetName()); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + } + } + + return bSuccess; +} + +bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot upgrade dragon soul without refine window."); + return false; + } + + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + if (aItemPoses[i].IsEquipPosition()) + return false; + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (NULL != pItem) + { + if (!pItem->IsDragonSoul()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + + set_items.emplace(pItem); + } + } + + if (set_items.size() == 0) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + return false; + } + + int count = set_items.size(); + int need_count = 0; + int fee = 0; + std::vector vec_probs; + //float prob_sum; + + BYTE ds_type, grade_idx, step_idx, strength_idx; + int result_grade; + + std::set ::iterator it = set_items.begin(); + { + LPITEM pItem = *it; + + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + if (!m_pTable->GetRefineGradeValues(ds_type, grade_idx, need_count, fee, vec_probs)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + } + while (++it != set_items.end()) + { + LPITEM pItem = *it; + + if (pItem->IsEquipped()) + { + return false; + } + + if (ds_type != GetType(pItem->GetVnum()) || grade_idx != GetGradeIdx(pItem->GetVnum())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + } + + if (count != need_count) + { + sys_err ("Possiblity of invalid client. Name %s", ch->GetName()); + BYTE bSubHeader = count < need_count? DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL : DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL; + SendRefineResultPacket(ch, bSubHeader, NPOS); + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + if (-1 == (result_grade = Gamble(vec_probs))) + { + sys_err ("Gamble failed. See RefineGardeTables' probabilities"); + return false; + } + + LPITEM pResultItem = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(ds_type, (BYTE)result_grade, 0, 0)); + + if (NULL == pResultItem) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(ds_type, (BYTE)result_grade, 0, 0)); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + int left_count = need_count; + + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + int n = pItem->GetCount(); + if (left_count > n) + { + pItem->RemoveFromCharacter(); + M2_DESTROY_ITEM(pItem); + left_count -= n; + } + else + { + pItem->SetCount(n - left_count); + } + } + + ch->AutoGiveItem(pResultItem, true); + + if (result_grade > grade_idx) + { + char buf[128]; + sprintf(buf, "GRADE : %d -> %d", grade_idx, result_grade); + LogManager::instance().ItemLog(ch, pResultItem, "DS_GRADE_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return true; + } + else + { + char buf[128]; + sprintf(buf, "GRADE : %d -> %d", grade_idx, result_grade); + LogManager::instance().ItemLog(ch, pResultItem, "DS_GRADE_REFINE_FAIL", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return false; + } +} + +bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot use dragon soul refine window."); + return false; + } + + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (NULL != pItem) + { + if (!pItem->IsDragonSoul()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + set_items.emplace(pItem); + } + } + + if (set_items.size() == 0) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + return false; + } + + std::string stGroupName; + int count = set_items.size(); + int need_count = 0; + int fee = 0; + std::vector vec_probs; + + BYTE ds_type, grade_idx, step_idx, strength_idx; + int result_step; + + std::set ::iterator it = set_items.begin(); + { + LPITEM pItem = *it; + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + if (!m_pTable->GetRefineStepValues(ds_type, step_idx, need_count, fee, vec_probs)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + while(++it != set_items.end()) + { + LPITEM pItem = *it; + + if (pItem->IsEquipped()) + { + return false; + } + if (ds_type != GetType(pItem->GetVnum()) || grade_idx != GetGradeIdx(pItem->GetVnum()) || step_idx != GetStepIdx(pItem->GetVnum())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + if (count != need_count) + { + sys_err ("Possiblity of invalid client. Name %s", ch->GetName()); + BYTE bSubHeader = count < need_count? DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL : DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL; + SendRefineResultPacket(ch, bSubHeader, NPOS); + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + //float sum = 0.f; + + if (-1 == (result_step = Gamble(vec_probs))) + { + sys_err ("Gamble failed. See RefineStepTables' probabilities"); + return false; + } + + LPITEM pResultItem = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(ds_type, grade_idx, (BYTE)result_step, 0)); + + if (NULL == pResultItem) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(ds_type, grade_idx, (BYTE)result_step, 0)); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + int left_count = need_count; + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + int n = pItem->GetCount(); + if (left_count > n) + { + pItem->RemoveFromCharacter(); + M2_DESTROY_ITEM(pItem); + left_count -= n; + } + else + { + pItem->SetCount(n - left_count); + } + } + + ch->AutoGiveItem(pResultItem, true); + if (result_step > step_idx) + { + char buf[128]; + sprintf(buf, "STEP : %d -> %d", step_idx, result_step); + LogManager::instance().ItemLog(ch, pResultItem, "DS_STEP_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return true; + } + else + { + char buf[128]; + sprintf(buf, "STEP : %d -> %d", step_idx, result_step); + LogManager::instance().ItemLog(ch, pResultItem, "DS_STEP_REFINE_FAIL", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return false; + } +} + +bool IsDragonSoulRefineMaterial(LPITEM pItem) +{ + if (pItem->GetType() != ITEM_MATERIAL) + return false; + return (pItem->GetSubType() == MATERIAL_DS_REFINE_NORMAL || + pItem->GetSubType() == MATERIAL_DS_REFINE_BLESSED || + pItem->GetSubType() == MATERIAL_DS_REFINE_HOLLY); +} + +bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot use dragon soul refine window."); + return false; + } + + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (pItem) + { + set_items.emplace(pItem); + } + } + if (set_items.size() == 0) + { + return false; + } + + int fee; + + LPITEM pRefineStone = NULL; + LPITEM pDragonSoul = NULL; + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + + if (pItem->IsEquipped()) + { + return false; + } + + if (pItem->IsDragonSoul()) + { + if (pDragonSoul != NULL) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + pDragonSoul = pItem; + } + else if(IsDragonSoulRefineMaterial(pItem)) + { + if (pRefineStone != NULL) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + pRefineStone = pItem; + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + BYTE bType=0, bGrade=0, bStep=0, bStrength=0; + + if (!pDragonSoul || !pRefineStone) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + return false; + } + + if (NULL != pDragonSoul) + { + GetDragonSoulInfo(pDragonSoul->GetVnum(), bType, bGrade, bStep, bStrength); + + float fWeight = 0.f; + if (!m_pTable->GetWeight(bType, bGrade, bStep, bStrength + 1, fWeight)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + return false; + } + + if (fWeight < FLT_EPSILON) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + return false; + } + } + + float fProb; + if (!m_pTable->GetRefineStrengthValues(bType, pRefineStone->GetSubType(), bStrength, fee, fProb)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + LPITEM pResult = NULL; + BYTE bSubHeader; + + if (fnumber(0.f, 100.f) <= fProb) + { + pResult = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(bType, bGrade, bStep, bStrength + 1)); + if (NULL == pResult) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(bType, bGrade, bStep, bStrength + 1)); + return false; + } + pDragonSoul->RemoveFromCharacter(); + + pDragonSoul->CopyAttributeTo(pResult); + RefreshItemAttributes(pResult); + + pDragonSoul->SetCount(pDragonSoul->GetCount() - 1); + pRefineStone->SetCount(pRefineStone->GetCount() - 1); + + char buf[128]; + sprintf(buf, "STRENGTH : %d -> %d", bStrength, bStrength + 1); + LogManager::instance().ItemLog(ch, pDragonSoul, "DS_STRENGTH_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ߽ϴ.")); + ch->AutoGiveItem(pResult, true); + bSubHeader = DS_SUB_HEADER_REFINE_SUCCEED; + } + else + { + if (bStrength != 0) + { + pResult = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(bType, bGrade, bStep, bStrength - 1)); + if (NULL == pResult) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(bType, bGrade, bStep, bStrength - 1)); + return false; + } + pDragonSoul->CopyAttributeTo(pResult); + RefreshItemAttributes(pResult); + } + bSubHeader = DS_SUB_HEADER_REFINE_FAIL; + + char buf[128]; + sprintf(buf, "STRENGTH : %d -> %d", bStrength, bStrength - 1); + + LogManager::instance().ItemLog(ch, pDragonSoul, "DS_STRENGTH_REFINE_FAIL", buf); + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ߽ϴ.")); + pDragonSoul->SetCount(pDragonSoul->GetCount() - 1); + pRefineStone->SetCount(pRefineStone->GetCount() - 1); + if (NULL != pResult) + ch->AutoGiveItem(pResult, true); + + } + + SendRefineResultPacket(ch, bSubHeader, NULL == pResult? NPOS : TItemPos (pResult->GetWindow(), pResult->GetCell())); + + return true; +} + +void DSManager::SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos) +{ + TPacketGCDragonSoulRefine pack; + pack.bSubType = bSubHeader; + + if (pos.IsValidItemPosition()) + { + pack.Pos = pos; + } + LPDESC d = ch->GetDesc(); + if (NULL == d) + { + return ; + } + else + { + d->Packet(&pack, sizeof(pack)); + } +} + +int DSManager::LeftTime(LPITEM pItem) const +{ + if (pItem == NULL) + return false; + + if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0) + { + return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC); + } + + else + { + return INT_MAX; + } +} + +bool DSManager::IsTimeLeftDragonSoul(LPITEM pItem) const +{ + if (pItem == NULL) + return false; + + if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0) + { + return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC) > 0; + } + + else + { + return true; + } +} + +bool DSManager::IsActiveDragonSoul(LPITEM pItem) const +{ + return pItem->GetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX); +} + +bool DSManager::ActivateDragonSoul(LPITEM pItem) +{ + if (NULL == pItem) + return false; + LPCHARACTER pOwner = pItem->GetOwner(); + if (NULL == pOwner) + return false; + + int deck_idx = pOwner->DragonSoul_GetActiveDeck(); + + if (deck_idx < 0) + return false; + + if (INVENTORY_MAX_NUM + WEAR_MAX_NUM + DS_SLOT_MAX * deck_idx <= pItem->GetCell() && + pItem->GetCell() < INVENTORY_MAX_NUM + WEAR_MAX_NUM + DS_SLOT_MAX * (deck_idx + 1)) + { + if (IsTimeLeftDragonSoul(pItem) && !IsActiveDragonSoul(pItem)) + { + char buf[128]; + sprintf (buf, "LEFT TIME(%d)", LeftTime(pItem)); + LogManager::instance().ItemLog(pOwner, pItem, "DS_ACTIVATE", buf); + pItem->ModifyPoints(true); + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 1); + + pItem->StartTimerBasedOnWearExpireEvent(); + } + return true; + } + else + return false; +} + +bool DSManager::DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState) +{ + if (NULL == pItem) + return false; + + LPCHARACTER pOwner = pItem->GetOwner(); + if (NULL == pOwner) + return false; + + if (!IsActiveDragonSoul(pItem)) + return false; + + char buf[128]; + pItem->StopTimerBasedOnWearExpireEvent(); + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 0); + pItem->ModifyPoints(false); + + sprintf (buf, "LEFT TIME(%d)", LeftTime(pItem)); + LogManager::instance().ItemLog(pOwner, pItem, "DS_DEACTIVATE", buf); + + if (false == bSkipRefreshOwnerActiveState) + RefreshDragonSoulState(pOwner); + + return true; +} + +void DSManager::RefreshDragonSoulState(LPCHARACTER ch) +{ + if (NULL == ch) + return ; + for (int i = WEAR_MAX_NUM; i < WEAR_MAX_NUM + DS_SLOT_MAX * DRAGON_SOUL_DECK_MAX_NUM; i++) + { + LPITEM pItem = ch->GetWear(i); + if (pItem != NULL) + { + if(IsActiveDragonSoul(pItem)) + { + return; + } + } + } + ch->DragonSoul_DeactivateAll(); +} + +DSManager::DSManager() +{ + m_pTable = NULL; +} + +DSManager::~DSManager() +{ + if (m_pTable) + delete m_pTable; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/DragonSoul.h b/source-server/Srcs/Server/game/src/DragonSoul.h new file mode 100644 index 000000000..2bff93441 --- /dev/null +++ b/source-server/Srcs/Server/game/src/DragonSoul.h @@ -0,0 +1,51 @@ +#ifndef __INC_METIN_II_GAME_DRAGON_SOUL_H__ +#define __INC_METIN_II_GAME_DRAGON_SOUL_H__ + +#include "../../common/length.h" + +class CHARACTER; +class CItem; + +class DragonSoulTable; + +class DSManager : public singleton +{ +public: + DSManager(); + ~DSManager(); + bool ReadDragonSoulTableFile(const char * c_pszFileName); + + void GetDragonSoulInfo(DWORD dwVnum, OUT BYTE& bType, OUT BYTE& bGrade, OUT BYTE& bStep, OUT BYTE& bRefine) const; + WORD GetBasePosition(const LPITEM pItem) const; + bool IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const; + int GetDuration(const LPITEM pItem) const; + + bool ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor = NULL); + + bool PullOut(LPCHARACTER ch, TItemPos DestCell, IN OUT LPITEM& pItem, LPITEM pExtractor = NULL); + + bool DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + bool DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + bool DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + + bool DragonSoulItemInitialize(LPITEM pItem); + + bool IsTimeLeftDragonSoul(LPITEM pItem) const; + int LeftTime(LPITEM pItem) const; + bool ActivateDragonSoul(LPITEM pItem); + bool DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState = false); + bool IsActiveDragonSoul(LPITEM pItem) const; +private: + void SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos); + + void RefreshDragonSoulState(LPCHARACTER ch); + + DWORD MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine); + bool PutAttributes(LPITEM pDS); + bool RefreshItemAttributes(LPITEM pItem); + + DragonSoulTable* m_pTable; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/FSM.cpp b/source-server/Srcs/Server/game/src/FSM.cpp new file mode 100644 index 000000000..ef09778bf --- /dev/null +++ b/source-server/Srcs/Server/game/src/FSM.cpp @@ -0,0 +1,62 @@ +// Local Includes +#include "stdafx.h" +#include +#include + +#include "FSM.h" + +// Constructor +CFSM::CFSM() +{ + // Initialize States + m_stateInitial.Set(this, &CFSM::BeginStateInitial, &CFSM::StateInitial, &CFSM::EndStateInitial); + + // Initialize State Machine + m_pCurrentState = static_cast(&m_stateInitial); + m_pNewState = NULL; +} + +//====================================================================================================== +// Global Functions + +// Update +void CFSM::Update() +{ + // Check New State + if (m_pNewState) + { + // Execute End State + m_pCurrentState->ExecuteEndState(); + + // Set New State + m_pCurrentState = m_pNewState; + m_pNewState = 0; + + // Execute Begin State + m_pCurrentState->ExecuteBeginState(); + } + + // Execute State + m_pCurrentState->ExecuteState(); +} + +//====================================================================================================== +// State Functions + +// Is State +bool CFSM::IsState(CState & State) const +{ + return (m_pCurrentState == &State); +} + +// Goto State +bool CFSM::GotoState(CState & NewState) +{ + if (IsState(NewState) && m_pNewState == &NewState) + return true; + + // Set New State + m_pNewState = &NewState; + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/FSM.h b/source-server/Srcs/Server/game/src/FSM.h new file mode 100644 index 000000000..2c785a1a0 --- /dev/null +++ b/source-server/Srcs/Server/game/src/FSM.h @@ -0,0 +1,35 @@ +#ifndef _fsm_fsm_h +#define _fsm_fsm_h + +// Local Includes +#include "state.h" + +// FSM Class +class CFSM +{ + protected: + CState * m_pCurrentState; // Current State + CState * m_pNewState; // New State + CStateTemplate m_stateInitial; // Initial State + + public: + // Constructor + CFSM(); + + // Destructor + virtual ~CFSM() {} + + // Global Functions + virtual void Update(); + + // State Functions + bool IsState(CState &State) const; + bool GotoState(CState &NewState); + + virtual void BeginStateInitial() {} + virtual void StateInitial() {} + virtual void EndStateInitial() {} +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.cpp b/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.cpp new file mode 100644 index 000000000..a78b0f237 --- /dev/null +++ b/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.cpp @@ -0,0 +1,135 @@ +#include "stdafx.h" +#include "FileMonitor_FreeBSD.h" +#include "../../libthecore/include/log.h" + +#define INVALID_KERNEL_EVENT -1 + +FileMonitorFreeBSD::FileMonitorFreeBSD() +{ + m_KernelEventQueue = INVALID_KERNEL_EVENT; +} + +FileMonitorFreeBSD::~FileMonitorFreeBSD() +{ + if( m_KernelEventQueue != INVALID_KERNEL_EVENT ) + { + close ( m_KernelEventQueue ); + m_KernelEventQueue = INVALID_KERNEL_EVENT; + } + + TMonitorFileHashMap::iterator it; + for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it ) + { + close(it->second.fhMonitor); + } + + m_FileLists.clear(); + + m_MonitoredEventLists.clear(); + m_TriggeredEventLists.clear(); +} + +void FileMonitorFreeBSD::Update(DWORD dwPulses) +{ + if( m_KernelEventQueue == INVALID_KERNEL_EVENT || m_FileLists.size() == 0 ) + return; + + int nEvent = kevent(m_KernelEventQueue, &m_TriggeredEventLists[0], (int)m_TriggeredEventLists.size(), &m_MonitoredEventLists[0], (int)m_MonitoredEventLists.size(), NULL ); + if( nEvent == INVALID_KERNEL_EVENT ) + { + return; + } + else if( nEvent > 0 ) + { + for( int i = 0; i < nEvent; ++i ) + { + int nEventFlags = m_MonitoredEventLists[i].flags; + eFileUpdatedOptions eUpdateOption = e_FileUpdate_None; + + if (nEventFlags & EV_ERROR) + eUpdateOption = e_FileUpdate_Error; + + else if (nEventFlags & NOTE_DELETE) + eUpdateOption = e_FileUpdate_Deleted; + + else if (nEventFlags & NOTE_EXTEND || nEventFlags & NOTE_WRITE) + eUpdateOption = e_FileUpdate_Modified; + + else if (nEventFlags & NOTE_ATTRIB) + eUpdateOption = e_FileUpdate_AttrModified; + + else if (nEventFlags & NOTE_LINK) + eUpdateOption = e_FileUpdate_Linked; + + else if (nEventFlags & NOTE_RENAME) + eUpdateOption = e_FileUpdate_Renamed; + + else if (nEventFlags & NOTE_REVOKE) + eUpdateOption = e_FileUpdate_Revoked; + + if( eUpdateOption != e_FileUpdate_None ) + { + TMonitorFileHashMap::iterator it; + for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it ) + { + FileIOContext_FreeBSD& context = it->second; + if( context.idxToEventList == i ) + { + std::string strModifedFileName = it->first; + context.pListenFunc( strModifedFileName, eUpdateOption ); + break; + } + } + } + } + } +} + +void FileMonitorFreeBSD::AddWatch(const std::string& strFileName, PFN_FileChangeListener pListenerFunc) +{ + int iFileHandle = -1; + if( (iFileHandle = open(strFileName.c_str(), O_RDONLY)) == -1) + { + sys_err("FileMonitorFreeBSD:AddWatch : can`t open file(%s).\n", strFileName.c_str()); + return; + } + + //create kqueue if not exists + if( m_KernelEventQueue == INVALID_KERNEL_EVENT ) + m_KernelEventQueue = kqueue(); + + if( m_KernelEventQueue == INVALID_KERNEL_EVENT ) + { + sys_err("FileMonitorFreeBSD:AddWatch : failed to create kqueue.\n"); + return; + } + + TMonitorFileHashMap::iterator it = m_FileLists.find( strFileName ); + if( it != m_FileLists.end() ) + { + sys_log(0, "FileMonitorFreeBSD:AddWatch : trying to add duplicated watch on file(%s).\n", strFileName.c_str() ); + return; + } + + //set file context + FileIOContext_FreeBSD context; + { + context.fhMonitor = iFileHandle; + context.idxToEventList = (int)m_MonitoredEventLists.size(); + context.pListenFunc = pListenerFunc; + } + + m_FileLists[strFileName] = context; + + //set events + struct kevent kTriggerEvent, kMonitorEvent; + + EV_SET(&kTriggerEvent, iFileHandle, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, + 0, 0); + + m_TriggeredEventLists.emplace_back(kTriggerEvent); + m_MonitoredEventLists.emplace_back(kMonitorEvent); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.h b/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.h new file mode 100644 index 000000000..d0a7844f8 --- /dev/null +++ b/source-server/Srcs/Server/game/src/FileMonitor_FreeBSD.h @@ -0,0 +1,47 @@ +#ifndef FILEMONITOR_FREEBSD_INCLUDED +#define FILEMONITOR_FREEBSD_INCLUDED + +#include "IFileMonitor.h" +#include +#include +#include +#include +#include + +struct FileIOContext_FreeBSD +{ + int fhMonitor; + int idxToEventList; // evtTrigger & evtMonitor index should be same + PFN_FileChangeListener pListenFunc; +}; + +class FileMonitorFreeBSD : public IFileMonitor +{ +private: + FileMonitorFreeBSD(); //hidden + +public: + virtual ~FileMonitorFreeBSD(); + + void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc); + void Update (DWORD dwPulses); + + static FileMonitorFreeBSD& Instance() + { + static FileMonitorFreeBSD theMonitor; + return theMonitor; + } + +private: + typedef boost::unordered_map TMonitorFileHashMap; + typedef std::vector TEventList; + + TMonitorFileHashMap m_FileLists; + TEventList m_MonitoredEventLists; + TEventList m_TriggeredEventLists; + + int m_KernelEventQueue; +}; + +#endif //FILEMONITOR_FREEBSD_INCLUDED +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/IFileMonitor.h b/source-server/Srcs/Server/game/src/IFileMonitor.h new file mode 100644 index 000000000..a2aeba755 --- /dev/null +++ b/source-server/Srcs/Server/game/src/IFileMonitor.h @@ -0,0 +1,31 @@ +#ifndef IFILEMONITOR_INCLUDED +#define IFILEMONITOR_INCLUDED + +//#include +#include + +enum eFileUpdatedOptions +{ + e_FileUpdate_None = -1, + e_FileUpdate_Error, + e_FileUpdate_Deleted, + e_FileUpdate_Modified, + e_FileUpdate_AttrModified, + e_FileUpdate_Linked, + e_FileUpdate_Renamed, + e_FileUpdate_Revoked, +}; + +// TODO : in FreeBSD boost function doesn`t work with boost bind +// so currently we only support for static function ptr only +//typedef boost::function< void ( const std::string&, eFileUpdatedOptions ) > PFN_FileChangeListener; +typedef void (* PFN_FileChangeListener )(const std::string&, eFileUpdatedOptions); + +struct IFileMonitor +{ + virtual void Update (DWORD dwPulses) = 0; + virtual void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc) = 0; +}; + +#endif // IFILEMONITOR_INCLUDED +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/Makefile b/source-server/Srcs/Server/game/src/Makefile new file mode 100644 index 000000000..c77ba7abf --- /dev/null +++ b/source-server/Srcs/Server/game/src/Makefile @@ -0,0 +1,228 @@ +CC = clang +CXX = clang++ + +GAME_VERSION := $(shell cat ../../__REVISION__) +LBITS := $(shell getconf LONG_BIT) + +GccMajorVersion := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.`) +GccMinorVersion := $(shell expr `$(CXX) -dumpversion | cut -f2 -d.`) +GccMinorEGT8 := $(shell expr $(GccMinorVersion) \>= 8) + +INCDIR = +LIBDIR = +BINDIR = .. +OBJDIR = .obj +$(shell if [ ! -d $(OBJDIR) ]; then mkdir $(OBJDIR); fi) + +## LIST OF CONSTANTS BEGIN +ENABLE_GOOGLE_TEST = 0 +ENABLE_LUA_5_VERSION = 0 +ENABLE_GCC_AUTODEPEND = 1 +ENABLE_STATIC = 0 +## LIST OF CONSTANTS END + +# Depend Path File +ifneq ($(ENABLE_GCC_AUTODEPEND), 1) +DEPFILE = Depend +endif + +# Standard Libraries +LIBS = -lm -lmd + +# Project Flags +CFLAGS = -m32 -g -Wall -O2 -pipe -fexceptions -fno-strict-aliasing -pthread -D_THREAD_SAFE -DNDEBUG +CFLAGS += -Wno-deprecated-declarations -Wno-nonnull-compare -Wno-deprecated-declarations -Wno-array-bounds -Wno-address +CFLAGS += -Wno-int-in-bool-context -Wno-format-truncation -Wno-stringop-truncation -Wno-sign-compare -Wno-deprecated-enum-enum-conversion +CXXFLAGS = -std=c++20 + +# for clang +ifneq '' '$(findstring clang++,$(CXX))' +CFLAGS += -Wno-invalid-source-encoding -Wno-deprecated-anon-enum-enum-conversion -Wno-unknown-warning-option +# for gcc +else ifneq '' '$(findstring g++,$(CXX))' +# for 32bit on 64bit +ifeq ($(LBITS),64) +CFLAGS += -L/usr/local/lib32/gcc12 +CFLAGS += -Wl,-rpath=/usr/local/lib32/gcc12 +else +# for 32bit on 32bit +CXXFLAGS += -Wl,-rpath=/usr/local/lib/gcc12 +endif +endif + +# MySQL +INCDIR += -I/usr/local/include/mysql +ifeq ($(LBITS),64) +LIBS += ../../../Extern/lib/libmysqlclient.a -lz +else +LIBS += /usr/local/lib/mysql/libmysqlclient.a /usr/lib/libz.a +endif + +ifeq ($(ENABLE_STATIC), 1) +CFLAGS += -static +endif + +ifeq ($(GccMinorEGT8), 1) +CFLAGS += -Wno-unused-local-typedefs +endif + +# FreeBSD stack protector +CFLAGS += -fstack-protector-all + +# Version defines +CFLAGS += -D__USER__=\"$(USER)\" -D__HOSTNAME__=\"$(HOSTNAME)\" -D__PWD__=\"$(PWD)\" -D__GAME_VERSION__=\"$(GAME_VERSION)\" + +# Boost +INCDIR += -I../../../Extern/include/boost + +# DevIL +INCDIR += -I../../../Extern/include/IL +LIBS += ../../../Extern/lib/libIL.a + +# CryptoPP +LIBS += ../../../Extern/lib/libcryptopp.a + +# GTest +ifeq ($(ENABLE_GOOGLE_TEST), 1) +LIBS += /usr/local/lib/libgtest.a +CFLAGS += -DENABLE_GOOGLE_TEST +endif + +# OpenSSL +INCDIR += -I/usr/include +ifeq ($(LBITS),64) +LIBS += /usr/lib32/libcrypto.a /usr/lib32/libssl.a +else +LIBS += -lssl -lcrypto +endif + +# Lua +CFLAGS += -DENABLE_LUA_5_VERSION=ENABLE_LUA_5_VERSION +ifeq ($(ENABLE_LUA_5_VERSION), 2) +INCDIR += -I../../liblua/5.2/install/include +LIBDIR += -L../../liblua/5.2/install/lib +LIBS += ../../liblua/5.2/install/lib/liblua.a +else +INCDIR += -I../../liblua/5.0/include +LIBDIR += -L../../liblua/5.0/lib +LIBS += ../../liblua/5.0/lib/liblua.a ../../liblua/5.0/lib/liblualib.a +endif + +# Project Libraries +INCDIR += -I../../../Extern/include +INCDIR += -I/usr/local/include +LIBDIR += -L/usr/local/lib + +LIBDIR += -L../../libthecore/lib -L../../libpoly -L../../libsql -L../../libgame/lib +LIBS += -lthecore -lpoly -lsql -lgame + +# PROJECT_SRC_FILES BEGIN +MAINCPP = main.cpp + +CFILE = minilzo.c + +CPPFILE = FSM.cpp MarkConvert.cpp MarkImage.cpp MarkManager.cpp OXEvent.cpp ani.cpp\ + arena.cpp banword.cpp battle.cpp blend_item.cpp buffer_manager.cpp building.cpp castle.cpp\ + char.cpp char_affect.cpp char_battle.cpp char_change_empire.cpp char_horse.cpp char_item.cpp char_manager.cpp\ + char_quickslot.cpp char_resist.cpp char_skill.cpp char_state.cpp PetSystem.cpp cmd.cpp cmd_emotion.cpp cmd_general.cpp\ + cmd_gm.cpp cmd_oxevent.cpp config.cpp constants.cpp crc32.cpp cube.cpp db.cpp desc.cpp\ + desc_client.cpp desc_manager.cpp desc_p2p.cpp dungeon.cpp empire_text_convert.cpp entity.cpp\ + entity_view.cpp event.cpp event_queue.cpp exchange.cpp file_loader.cpp fishing.cpp gm.cpp guild.cpp\ + guild_manager.cpp guild_war.cpp horse_rider.cpp horsename_manager.cpp input.cpp input_auth.cpp input_db.cpp\ + input_login.cpp input_main.cpp input_p2p.cpp input_udp.cpp\ + item.cpp item_addon.cpp item_attribute.cpp item_manager.cpp item_manager_idrange.cpp locale.cpp\ + locale_service.cpp log.cpp login_data.cpp lzo_manager.cpp marriage.cpp\ + messenger_manager.cpp mining.cpp mob_manager.cpp monarch.cpp motion.cpp over9refine.cpp p2p.cpp packet_info.cpp\ + party.cpp polymorph.cpp priv_manager.cpp pvp.cpp\ + questevent.cpp questlua.cpp questlua_affect.cpp questlua_arena.cpp questlua_building.cpp\ + questlua_danceevent.cpp questlua_dungeon.cpp questlua_forked.cpp questlua_game.cpp questlua_global.cpp\ + questlua_guild.cpp questlua_horse.cpp questlua_pet.cpp questlua_item.cpp questlua_marriage.cpp questlua_mgmt.cpp\ + questlua_monarch.cpp questlua_npc.cpp questlua_oxevent.cpp questlua_party.cpp questlua_pc.cpp\ + questlua_quest.cpp questlua_target.cpp questmanager.cpp questnpc.cpp questpc.cpp\ + refine.cpp regen.cpp safebox.cpp sectree.cpp sectree_manager.cpp sequence.cpp shop.cpp\ + skill.cpp start_position.cpp target.cpp text_file_loader.cpp trigger.cpp utils.cpp vector.cpp war_map.cpp\ + wedding.cpp xmas_event.cpp version.cpp panama.cpp threeway_war.cpp map_location.cpp\ + BlueDragon.cpp BlueDragon_Binder.cpp DragonLair.cpp questlua_dragonlair.cpp\ + skill_power.cpp affect.cpp\ + FileMonitor_FreeBSD.cpp ClientPackageCryptInfo.cpp cipher.cpp\ + buff_on_attributes.cpp dragon_soul_table.cpp DragonSoul.cpp\ + group_text_parse_tree.cpp char_dragonsoul.cpp questlua_dragonsoul.cpp\ + shop_manager.cpp shopEx.cpp item_manager_read_tables.cpp shutdown_manager.cpp\ + questlua_dnd.cpp +# PROJECT_SRC_FILES END + +# PROJECT_OBJ_FILES BEGIN +COBJS = $(CFILE:%.c=$(OBJDIR)/%.o) +CPPOBJS = $(CPPFILE:%.cpp=$(OBJDIR)/%.o) +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) +CDEPS = $(COBJS:%.o=%.d) +CPPDEPS = $(CPPOBJS:%.o=%.d) +endif + +MAINOBJ = $(MAINCPP:%.cpp=$(OBJDIR)/%.o) +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) +MAINDEPS = $(MAINOBJ:%.o=%.d) +endif + +# PROJECT_OBJ_FILES END + +# Target Paths +MAIN_TARGET = $(BINDIR)/game_r$(GAME_VERSION) + +default: $(MAIN_TARGET) + +$(OBJDIR)/%.o: %.c + @echo compiling $< + @$(CC) $(CFLAGS) $(INCDIR) -c $< -o $@ +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @$(CC) -MM -MG -MP $(CFLAGS) $(INCDIR) -c $< -o $(OBJDIR)/$*.d + @sed -i '' -e's/$*.o:/$(OBJDIR)\/$*.o:/g' $(OBJDIR)/$*.d +endif + +$(OBJDIR)/%.o: %.cpp + @echo compiling $< + @$(CXX) $(CFLAGS) $(CXXFLAGS) $(INCDIR) -c $< -o $@ +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @$(CXX) -MM -MG -MP $(CFLAGS) $(CXXFLAGS) $(INCDIR) -c $< -o $(OBJDIR)/$*.d + @sed -i '' -e's/$*.o:/$(OBJDIR)\/$*.o:/g' $(OBJDIR)/$*.d +endif + +limit_time: + @echo update limit time + @python update_limit_time.py + +$(MAIN_TARGET): $(CPPOBJS) $(COBJS) $(MAINOBJ) + @echo linking $(MAIN_TARGET) + @$(CXX) $(CFLAGS) $(CXXFLAGS) $(LIBDIR) $(COBJS) $(CPPOBJS) $(MAINOBJ) $(LIBS) -o $(MAIN_TARGET) + +symlink: + @ln -fs game_r$(GAME_VERSION) $(BINDIR)/game_symlink + +strip: + @cp $(MAIN_TARGET) $(BINDIR)/game_r + @strip $(BINDIR)/game_r + +clean: + @echo cleaning $(MAIN_TARGET) $(OBJDIR) + @rm -f $(COBJS) $(CPPOBJS) $(MAINOBJ) +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @rm -f $(CDEPS) $(CPPDEPS) $(MAINDEPS) +endif + @rm -f $(BINDIR)/game_r* + +dep: +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) + @echo "Note: gcc autodepend is autodetected, so target dep skipped" +else + makedepend -f $(DEPFILE) $(INCDIR) -I/usr/include/c++/3.3 -I/usr/include/c++/4.2 -p$(OBJDIR)/ $(CPPFILE) $(CFILE) $(MAINCPP) 2> /dev/null > $(DEPFILE) +endif + +# AUTO_DEPEND_CHECK BEGIN +ifeq ($(ENABLE_GCC_AUTODEPEND), 1) +sinclude $(CDEPS) +sinclude $(CPPDEPS) +sinclude $(MAINDEPS) +else +sinclude $(DEPFILE) +endif +# AUTO_DEPEND_CHECK END diff --git a/source-server/Srcs/Server/game/src/MarkConvert.cpp b/source-server/Srcs/Server/game/src/MarkConvert.cpp new file mode 100644 index 000000000..56f1d4c27 --- /dev/null +++ b/source-server/Srcs/Server/game/src/MarkConvert.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "MarkManager.h" + +#ifdef __WIN32__ +#include +#endif + +#define OLD_MARK_INDEX_FILENAME "guild_mark.idx" +#define OLD_MARK_DATA_FILENAME "guild_mark.tga" + +static Pixel * LoadOldGuildMarkImageFile() +{ + FILE * fp = fopen(OLD_MARK_DATA_FILENAME, "rb"); + + if (!fp) + { + sys_err("cannot open %s", OLD_MARK_INDEX_FILENAME); + return NULL; + } + + int dataSize = 512 * 512 * sizeof(Pixel); + Pixel * dataPtr = (Pixel *) malloc(dataSize); + + fread(dataPtr, dataSize, 1, fp); + + fclose(fp); + + return dataPtr; +} + +bool GuildMarkConvert(const std::vector & vecGuildID) +{ +#ifndef __WIN32__ + mkdir("mark", S_IRWXU); +#else + _mkdir("mark"); +#endif + +#ifndef __WIN32__ + if (0 != access(OLD_MARK_INDEX_FILENAME, F_OK)) +#else + if (0 != _access(OLD_MARK_INDEX_FILENAME, 0)) +#endif + return true; + + FILE* fp = fopen(OLD_MARK_INDEX_FILENAME, "r"); + + if (NULL == fp) + return false; + + Pixel * oldImagePtr = LoadOldGuildMarkImageFile(); + + if (NULL == oldImagePtr) + { + fclose(fp); + return false; + } + + sys_log(0, "Guild Mark Converting Start."); + + char line[256]; + DWORD guild_id; + DWORD mark_id; + Pixel mark[SGuildMark::SIZE]; + + while (fgets(line, sizeof(line)-1, fp)) + { + sscanf(line, "%u %u", &guild_id, &mark_id); + + if (find(vecGuildID.begin(), vecGuildID.end(), guild_id) == vecGuildID.end()) + { + sys_log(0, " skipping guild ID %u", guild_id); + continue; + } + + uint row = mark_id / 32; + uint col = mark_id % 32; + + if (row >= 42) + { + sys_err("invalid mark_id %u", mark_id); + continue; + } + + uint sx = col * 16; + uint sy = row * 12; + + Pixel * src = oldImagePtr + sy * 512 + sx; + Pixel * dst = mark; + + for (int y = 0; y != SGuildMark::HEIGHT; ++y) + { + for (int x = 0; x != SGuildMark::WIDTH; ++x) + *(dst++) = *(src+x); + + src += 512; + } + + CGuildMarkManager::instance().SaveMark(guild_id, (BYTE *) mark); + line[0] = '\0'; + } + + free(oldImagePtr); + fclose(fp); + +#ifndef __WIN32__ + system("mv -f guild_mark.idx guild_mark.idx.removable"); + system("mv -f guild_mark.tga guild_mark.tga.removable"); +#else + system("move /Y guild_mark.idx guild_mark.idx.removable"); + system("move /Y guild_mark.tga guild_mark.tga.removable"); +#endif + + sys_log(0, "Guild Mark Converting Complete."); + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/MarkImage.cpp b/source-server/Srcs/Server/game/src/MarkImage.cpp new file mode 100644 index 000000000..a0fbcc3c4 --- /dev/null +++ b/source-server/Srcs/Server/game/src/MarkImage.cpp @@ -0,0 +1,293 @@ +#include "stdafx.h" +#include "MarkImage.h" + +#include "crc32.h" +#include "lzo_manager.h" +#define CLZO LZOManager + +CGuildMarkImage * NewMarkImage() +{ + return M2_NEW CGuildMarkImage; +} + +void DeleteMarkImage(CGuildMarkImage * pkImage) +{ + M2_DELETE(pkImage); +} + +CGuildMarkImage::CGuildMarkImage() + : m_uImg(INVALID_HANDLE) +{ + memset( &m_apxImage, 0, sizeof(m_apxImage) ); +} + +CGuildMarkImage::~CGuildMarkImage() +{ + Destroy(); +} + +void CGuildMarkImage::Destroy() +{ + if (INVALID_HANDLE == m_uImg) + return; + + ilDeleteImages(1, &m_uImg); + m_uImg = INVALID_HANDLE; +} + +void CGuildMarkImage::Create() +{ + if (INVALID_HANDLE != m_uImg) + return; + + ilGenImages(1, &m_uImg); +} + +bool CGuildMarkImage::Save(const char* c_szFileName) +{ + ilEnable(IL_FILE_OVERWRITE); + ilBindImage(m_uImg); + + if (!ilSave(IL_TGA, (const ILstring)c_szFileName)) + return false; + + return true; +} + +bool CGuildMarkImage::Build(const char * c_szFileName) +{ + sys_log(0, "GuildMarkImage: creating new file %s", c_szFileName); + + Destroy(); + Create(); + + ilBindImage(m_uImg); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + + BYTE * data = (BYTE *) malloc(sizeof(Pixel) * WIDTH * HEIGHT); + memset(data, 0, sizeof(Pixel) * WIDTH * HEIGHT); + + if (!ilTexImage(WIDTH, HEIGHT, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data)) + { + sys_err("GuildMarkImage: cannot initialize image"); + return false; + } + + free(data); + + ilEnable(IL_FILE_OVERWRITE); + + if (!ilSave(IL_TGA, (const ILstring)c_szFileName)) + return false; + + return true; +} + +bool CGuildMarkImage::Load(const char * c_szFileName) +{ + Destroy(); + Create(); + + ilBindImage(m_uImg); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + + if (!ilLoad(IL_TYPE_UNKNOWN, (const ILstring) c_szFileName)) + { + sys_err("GuildMarkImage: %s cannot open file.", c_szFileName); + return false; + } + + if (ilGetInteger(IL_IMAGE_WIDTH) != WIDTH) + { + sys_err("GuildMarkImage: %s width must be %u", c_szFileName, WIDTH); + return false; + } + + if (ilGetInteger(IL_IMAGE_HEIGHT) != HEIGHT) + { + sys_err("GuildMarkImage: %s height must be %u", c_szFileName, HEIGHT); + return false; + } + + ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE); + + BuildAllBlocks(); + return true; +} + +void CGuildMarkImage::PutData(UINT x, UINT y, UINT width, UINT height, void * data) +{ + ilBindImage(m_uImg); + ilSetPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data); +} + +void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * data) +{ + ilBindImage(m_uImg); + ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data); +} + +// SERVER +bool CGuildMarkImage::SaveMark(DWORD posMark, BYTE * pbImage) +{ + if (posMark >= MARK_TOTAL_COUNT) + { + sys_err("GuildMarkImage::CopyMarkFromData: Invalid mark position %u", posMark); + return false; + } + + DWORD colMark = posMark % MARK_COL_COUNT; + DWORD rowMark = posMark / MARK_COL_COUNT; + + printf("PutMark pos %u %ux%u\n", posMark, colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT); + PutData(colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, pbImage); + + DWORD rowBlock = rowMark / SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT; + DWORD colBlock = colMark / SGuildMarkBlock::MARK_PER_BLOCK_WIDTH; + + Pixel apxBuf[SGuildMarkBlock::SIZE]; + GetData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + m_aakBlock[rowBlock][colBlock].Compress(apxBuf); + return true; +} + +bool CGuildMarkImage::DeleteMark(DWORD posMark) +{ + Pixel image[SGuildMark::SIZE]; + memset(&image, 0, sizeof(image)); + return SaveMark(posMark, (BYTE *) &image); +} + +// CLIENT +bool CGuildMarkImage::SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize) +{ + if (posBlock >= BLOCK_TOTAL_COUNT) + return false; + + Pixel apxBuf[SGuildMarkBlock::SIZE]; + lzo_uint sizeBuf = sizeof(apxBuf); + + if (LZO_E_OK != lzo1x_decompress_safe(pbComp, dwCompSize, (BYTE *) apxBuf, &sizeBuf, CLZO::Instance().GetWorkMemory())) + { + sys_err("GuildMarkImage::CopyBlockFromCompressedData: cannot decompress, compressed size = %u", dwCompSize); + return false; + } + + if (sizeBuf != sizeof(apxBuf)) + { + sys_err("GuildMarkImage::CopyBlockFromCompressedData: image corrupted, decompressed size = %u", sizeBuf); + return false; + } + + DWORD rowBlock = posBlock / BLOCK_COL_COUNT; + DWORD colBlock = posBlock % BLOCK_COL_COUNT; + + PutData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + + m_aakBlock[rowBlock][colBlock].CopyFrom(pbComp, dwCompSize, GetCRC32((const char *) apxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE)); + return true; +} + +void CGuildMarkImage::BuildAllBlocks() +{ + Pixel apxBuf[SGuildMarkBlock::SIZE]; + sys_log(0, "GuildMarkImage::BuildAllBlocks"); + + for (UINT row = 0; row < BLOCK_ROW_COUNT; ++row) + for (UINT col = 0; col < BLOCK_COL_COUNT; ++col) + { + GetData(col * SGuildMarkBlock::WIDTH, row * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + m_aakBlock[row][col].Compress(apxBuf); + } +} + +DWORD CGuildMarkImage::GetEmptyPosition() +{ + SGuildMark kMark; + + for (DWORD row = 0; row < MARK_ROW_COUNT; ++row) + { + for (DWORD col = 0; col < MARK_COL_COUNT; ++col) + { + GetData(col * SGuildMark::WIDTH, row * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, kMark.m_apxBuf); + + if (kMark.IsEmpty()) + return (row * MARK_COL_COUNT + col); + } + } + + return INVALID_MARK_POSITION; +} + +void CGuildMarkImage::GetDiffBlocks(const DWORD * crcList, std::map & mapDiffBlocks) +{ + BYTE posBlock = 0; + + for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row) + for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col) + { + if (m_aakBlock[row][col].m_crc != *crcList) + { + mapDiffBlocks.emplace(posBlock, &m_aakBlock[row][col]); + } + ++crcList; + ++posBlock; + } +} + +void CGuildMarkImage::GetBlockCRCList(DWORD * crcList) +{ + for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row) + for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col) + *(crcList++) = m_aakBlock[row][col].GetCRC(); +} + +//////////////////////////////////////////////////////////////////////////////// +void SGuildMark::Clear() +{ + for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel) + m_apxBuf[iPixel] = 0xff000000; +} + +bool SGuildMark::IsEmpty() +{ + for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel) + if (m_apxBuf[iPixel] != 0x00000000) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +DWORD SGuildMarkBlock::GetCRC() const +{ + return m_crc; +} + +void SGuildMarkBlock::CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc) +{ + if (dwCompSize > MAX_COMP_SIZE) + return; + + m_sizeCompBuf = dwCompSize; + thecore_memcpy(m_abCompBuf, pbCompBuf, dwCompSize); + m_crc = crc; + //printf("SGuildMarkBlock::CopyFrom: %u > %u crc %u\n", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf, m_crc); +} + +void SGuildMarkBlock::Compress(const Pixel * pxBuf) +{ + m_sizeCompBuf = MAX_COMP_SIZE; + + if (LZO_E_OK != lzo1x_1_compress((const BYTE *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE, m_abCompBuf, &m_sizeCompBuf, CLZO::Instance().GetWorkMemory())) + { + sys_err("SGuildMarkBlock::Compress: Error! %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf); + return; + } + + //sys_log(0, "SGuildMarkBlock::Compress %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf); + m_crc = GetCRC32((const char *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/MarkImage.h b/source-server/Srcs/Server/game/src/MarkImage.h new file mode 100644 index 000000000..79d44cfe8 --- /dev/null +++ b/source-server/Srcs/Server/game/src/MarkImage.h @@ -0,0 +1,112 @@ +#ifndef __INC_METIN_II_MARKIMAGE_H__ +#define __INC_METIN_II_MARKIMAGE_H__ + +#include +#include "minilzo.h" + +typedef unsigned long Pixel; + +struct SGuildMark +{ + enum + { + WIDTH = 16, + HEIGHT = 12, + SIZE = WIDTH * HEIGHT, + }; + + /////////////////////////////////////////////////////////////////////////////// + Pixel m_apxBuf[SIZE]; + + /////////////////////////////////////////////////////////////////////////////// + void Clear(); + bool IsEmpty(); +}; + +struct SGuildMarkBlock +{ + enum + { + MARK_PER_BLOCK_WIDTH = 4, + MARK_PER_BLOCK_HEIGHT = 4, + + WIDTH = SGuildMark::WIDTH * MARK_PER_BLOCK_WIDTH, + HEIGHT = SGuildMark::HEIGHT * MARK_PER_BLOCK_HEIGHT, + + SIZE = WIDTH * HEIGHT, + MAX_COMP_SIZE = (SIZE * sizeof(Pixel)) + ((SIZE * sizeof(Pixel)) >> 4) + 64 + 3 + }; + + /////////////////////////////////////////////////////////////////////////////// + Pixel m_apxBuf[SIZE]; + + BYTE m_abCompBuf[MAX_COMP_SIZE]; + lzo_uint m_sizeCompBuf; + DWORD m_crc; + + /////////////////////////////////////////////////////////////////////////////// + DWORD GetCRC() const; + + void CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc); + void Compress(const Pixel * pxBuf); +}; + +class CGuildMarkImage +{ + public: + enum + { + WIDTH = 512, + HEIGHT = 512, + + BLOCK_ROW_COUNT = HEIGHT / SGuildMarkBlock::HEIGHT, // 10 + BLOCK_COL_COUNT = WIDTH / SGuildMarkBlock::WIDTH, // 8 + + BLOCK_TOTAL_COUNT = BLOCK_ROW_COUNT * BLOCK_COL_COUNT, // 80 + + MARK_ROW_COUNT = BLOCK_ROW_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT, // 40 + MARK_COL_COUNT = BLOCK_COL_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_WIDTH, // 32 + + MARK_TOTAL_COUNT = MARK_ROW_COUNT * MARK_COL_COUNT, // 1280 + + INVALID_MARK_POSITION = 0xffffffff, + }; + + CGuildMarkImage(); + virtual ~CGuildMarkImage(); + + void Create(); + void Destroy(); + + bool Build(const char * c_szFileName); + bool Save(const char* c_szFileName); + bool Load(const char* c_szFileName); + + void PutData(UINT x, UINT y, UINT width, UINT height, void* data); + void GetData(UINT x, UINT y, UINT width, UINT height, void* data); + + bool SaveMark(DWORD posMark, BYTE * pbMarkImage); + bool DeleteMark(DWORD posMark); + bool SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize); + + DWORD GetEmptyPosition(); + + void GetBlockCRCList(DWORD * crcList); + void GetDiffBlocks(const DWORD * crcList, std::map & mapDiffBlocks); + + private: + enum + { + INVALID_HANDLE = 0xffffffff, + }; + + void BuildAllBlocks(); + + SGuildMarkBlock m_aakBlock[BLOCK_ROW_COUNT][BLOCK_COL_COUNT]; + Pixel m_apxImage[WIDTH * HEIGHT * sizeof(Pixel)]; + + ILuint m_uImg; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/MarkManager.cpp b/source-server/Srcs/Server/game/src/MarkManager.cpp new file mode 100644 index 000000000..26156e337 --- /dev/null +++ b/source-server/Srcs/Server/game/src/MarkManager.cpp @@ -0,0 +1,389 @@ +#include "stdafx.h" +#include "MarkManager.h" + +#include "crc32.h" + +CGuildMarkImage * CGuildMarkManager::__NewImage() +{ + return M2_NEW CGuildMarkImage; +} + +void CGuildMarkManager::__DeleteImage(CGuildMarkImage * pkImgDel) +{ + M2_DELETE(pkImgDel); +} + +CGuildMarkManager::CGuildMarkManager() +{ + for (DWORD i = 0; i < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT; ++i) + m_setFreeMarkID.emplace(i); +} + +CGuildMarkManager::~CGuildMarkManager() +{ + for (std::map::iterator it = m_mapIdx_Image.begin(); it != m_mapIdx_Image.end(); ++it) + __DeleteImage(it->second); + + m_mapIdx_Image.clear(); +} + +bool CGuildMarkManager::GetMarkImageFilename(DWORD imgIdx, std::string & path) const +{ + if (imgIdx >= MAX_IMAGE_COUNT) + return false; + + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_%u.tga", m_pathPrefix.c_str(), imgIdx); + path = buf; + return true; +} + +void CGuildMarkManager::SetMarkPathPrefix(const char * prefix) +{ + m_pathPrefix = prefix; +} + +bool CGuildMarkManager::LoadMarkIndex() +{ + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str()); + FILE * fp = fopen(buf, "r"); + + if (!fp) + return false; + + DWORD guildID; + DWORD markID; + + char line[256]; + + while (fgets(line, sizeof(line)-1, fp)) + { + sscanf(line, "%u %u", &guildID, &markID); + line[0] = '\0'; + AddMarkIDByGuildID(guildID, markID); + } + + LoadMarkImages(); + + fclose(fp); + return true; +} + +bool CGuildMarkManager::SaveMarkIndex() +{ + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str()); + FILE * fp = fopen(buf, "w"); + + if (!fp) + { + sys_err("MarkManager::SaveMarkIndex: cannot open index file."); + return false; + } + + for (std::map::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + fprintf(fp, "%u %u\n", it->first, it->second); + + fclose(fp); + sys_log(0, "MarkManager::SaveMarkIndex: index count %d", m_mapGID_MarkID.size()); + return true; +} + +void CGuildMarkManager::LoadMarkImages() +{ + bool isMarkExists[MAX_IMAGE_COUNT]; + memset(isMarkExists, 0, sizeof(isMarkExists)); + + for (std::map::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + { + DWORD markID = it->second; + + if (markID < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT) + isMarkExists[markID / CGuildMarkImage::MARK_TOTAL_COUNT] = true; + } + + for (DWORD i = 0; i < MAX_IMAGE_COUNT; ++i) + if (isMarkExists[i]) + __GetImage(i); +} + +void CGuildMarkManager::SaveMarkImage(DWORD imgIdx) +{ + std::string path; + + if (GetMarkImageFilename(imgIdx, path)) + if (!__GetImage(imgIdx)->Save(path.c_str())) + sys_err("%s Save failed\n", path.c_str()); +} + +CGuildMarkImage * CGuildMarkManager::__GetImage(DWORD imgIdx) +{ + std::map::iterator it = m_mapIdx_Image.find(imgIdx); + + if (it == m_mapIdx_Image.end()) + { + std::string imagePath; + + if (GetMarkImageFilename(imgIdx, imagePath)) + { + CGuildMarkImage * pkImage = __NewImage(); + m_mapIdx_Image.emplace(imgIdx, pkImage); + + if (!pkImage->Load(imagePath.c_str())) + { + pkImage->Build(imagePath.c_str()); + pkImage->Load(imagePath.c_str()); + } + + return pkImage; + } + else + return NULL; + } + else + return it->second; +} + +bool CGuildMarkManager::AddMarkIDByGuildID(DWORD guildID, DWORD markID) +{ + if (markID >= MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT) + return false; + + //sys_log(0, "MarkManager: guild_id=%d mark_id=%d", guildID, markID); + m_mapGID_MarkID.emplace(guildID, markID); + m_setFreeMarkID.erase(markID); + return true; +} + +DWORD CGuildMarkManager::GetMarkID(DWORD guildID) +{ + std::map::iterator it = m_mapGID_MarkID.find(guildID); + + if (it == m_mapGID_MarkID.end()) + return INVALID_MARK_ID; + + return it->second; +} + +DWORD CGuildMarkManager::__AllocMarkID(DWORD guildID) +{ + std::set::iterator it = m_setFreeMarkID.lower_bound(0); + + if (it == m_setFreeMarkID.end()) + return INVALID_MARK_ID; + + DWORD markID = *it; + + DWORD imgIdx = markID / CGuildMarkImage::MARK_TOTAL_COUNT; + CGuildMarkImage * pkImage = __GetImage(imgIdx); + + if (pkImage && AddMarkIDByGuildID(guildID, markID)) + return markID; + + return INVALID_MARK_ID; +} + +DWORD CGuildMarkManager::GetMarkImageCount() const +{ + return m_mapIdx_Image.size(); +} + +DWORD CGuildMarkManager::GetMarkCount() const +{ + return m_mapGID_MarkID.size(); +} + +// SERVER +void CGuildMarkManager::CopyMarkIdx(char * pcBuf) const +{ + WORD * pwBuf = (WORD *) pcBuf; + + for (std::map::const_iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + { + *(pwBuf++) = it->first; // guild id + *(pwBuf++) = it->second; // mark id + } +} + +// SERVER +DWORD CGuildMarkManager::SaveMark(DWORD guildID, BYTE * pbMarkImage) +{ + DWORD idMark; + + if ((idMark = GetMarkID(guildID)) == INVALID_MARK_ID) + { + if ((idMark = __AllocMarkID(guildID)) == INVALID_MARK_ID) + { + sys_err("CGuildMarkManager: cannot alloc mark id %u", guildID); + return false; + } + else + sys_log(0, "SaveMark: mark id alloc %u", idMark); + } + else + sys_log(0, "SaveMark: mark id found %u", idMark); + + DWORD imgIdx = (idMark / CGuildMarkImage::MARK_TOTAL_COUNT); + CGuildMarkImage * pkImage = __GetImage(imgIdx); + + if (pkImage) + { + pkImage->SaveMark(idMark % CGuildMarkImage::MARK_TOTAL_COUNT, pbMarkImage); + + SaveMarkImage(imgIdx); + SaveMarkIndex(); + } + + return idMark; +} + +// SERVER +void CGuildMarkManager::DeleteMark(DWORD guildID) +{ + std::map::iterator it = m_mapGID_MarkID.find(guildID); + + if (it == m_mapGID_MarkID.end()) + return; + + CGuildMarkImage * pkImage; + + if ((pkImage = __GetImage(it->second / CGuildMarkImage::MARK_TOTAL_COUNT)) != NULL) + pkImage->DeleteMark(it->second % CGuildMarkImage::MARK_TOTAL_COUNT); + + m_setFreeMarkID.emplace(it->second); + m_mapGID_MarkID.erase(it); + + SaveMarkIndex(); +} + +// SERVER +void CGuildMarkManager::GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map & mapDiffBlocks) +{ + mapDiffBlocks.clear(); + + if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx)) + { + sys_err("invalid idx %u", imgIdx); + return; + } + + CGuildMarkImage * p = __GetImage(imgIdx); + + if (p) + p->GetDiffBlocks(crcList, mapDiffBlocks); +} + +// CLIENT +bool CGuildMarkManager::SaveBlockFromCompressedData(DWORD imgIdx, DWORD posBlock, const BYTE * pbBlock, DWORD dwSize) +{ + CGuildMarkImage * pkImage = __GetImage(imgIdx); + + if (pkImage) + pkImage->SaveBlockFromCompressedData(posBlock, pbBlock, dwSize); + + return false; +} + +// CLIENT +bool CGuildMarkManager::GetBlockCRCList(DWORD imgIdx, DWORD * crcList) +{ + if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx)) + { + sys_err("invalid idx %u", imgIdx); + return false; + } + + CGuildMarkImage * p = __GetImage(imgIdx); + + if (p) + p->GetBlockCRCList(crcList); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// Symbol +/////////////////////////////////////////////////////////////////////////////////////// +const CGuildMarkManager::TGuildSymbol * CGuildMarkManager::GetGuildSymbol(DWORD guildID) +{ + std::map::iterator it = m_mapSymbol.find(guildID); + + if (it == m_mapSymbol.end()) + return NULL; + + return &it->second; +} + +bool CGuildMarkManager::LoadSymbol(const char* filename) +{ + FILE* fp = fopen(filename, "rb"); + + if (!fp) + return true; + else + { + DWORD symbolCount; + fread(&symbolCount, 4, 1, fp); + + for (DWORD i = 0; i < symbolCount; i++) + { + DWORD guildID; + DWORD dwSize; + fread(&guildID, 4, 1, fp); + fread(&dwSize, 4, 1, fp); + + TGuildSymbol gs; + gs.raw.resize(dwSize); + fread(&gs.raw[0], 1, dwSize, fp); + gs.crc = GetCRC32(reinterpret_cast(&gs.raw[0]), dwSize); + m_mapSymbol.emplace(guildID, gs); + } + } + + fclose(fp); + return true; +} + +void CGuildMarkManager::SaveSymbol(const char* filename) +{ + FILE* fp = fopen(filename, "wb"); + if (!fp) + { + sys_err("Cannot open Symbol file (name: %s)", filename); + return; + } + + DWORD symbolCount = m_mapSymbol.size(); + fwrite(&symbolCount, 4, 1, fp); + + for (std::map::iterator it = m_mapSymbol.begin(); it != m_mapSymbol.end(); ++it) + { + DWORD guildID = it->first; + DWORD dwSize = it->second.raw.size(); + fwrite(&guildID, 4, 1, fp); + fwrite(&dwSize, 4, 1, fp); + fwrite(&it->second.raw[0], 1, dwSize, fp); + } + + fclose(fp); +} + +void CGuildMarkManager::UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData) +{ + sys_log(0, "GuildSymbolUpload guildID %u Size %d", guildID, iSize); + + if (m_mapSymbol.find(guildID) == m_mapSymbol.end()) + m_mapSymbol.emplace(guildID, TGuildSymbol()); + + TGuildSymbol& rSymbol = m_mapSymbol[guildID]; + rSymbol.raw.clear(); + + if (iSize > 0) + { + rSymbol.raw.reserve(iSize); + std::copy(pbyData, (pbyData + iSize), std::back_inserter(rSymbol.raw)); + rSymbol.crc = GetCRC32(reinterpret_cast(pbyData), iSize); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/MarkManager.h b/source-server/Srcs/Server/game/src/MarkManager.h new file mode 100644 index 000000000..214ad2d2f --- /dev/null +++ b/source-server/Srcs/Server/game/src/MarkManager.h @@ -0,0 +1,82 @@ +#ifndef __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__ +#define __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__ + +#include "MarkImage.h" + +class CGuildMarkManager : public singleton +{ + public: + enum + { + MAX_IMAGE_COUNT = 5, + INVALID_MARK_ID = 0xffffffff, + }; + + // Symbol + struct TGuildSymbol + { + DWORD crc; + std::vector raw; + }; + + CGuildMarkManager(); + virtual ~CGuildMarkManager(); + + const TGuildSymbol * GetGuildSymbol(DWORD GID); + bool LoadSymbol(const char* filename); + void SaveSymbol(const char* filename); + void UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData); + + // Mark + + void SetMarkPathPrefix(const char * prefix); + + bool LoadMarkIndex(); + bool SaveMarkIndex(); + + void LoadMarkImages(); + void SaveMarkImage(DWORD imgIdx); + + bool GetMarkImageFilename(DWORD imgIdx, std::string & path) const; + bool AddMarkIDByGuildID(DWORD guildID, DWORD markID); + DWORD GetMarkImageCount() const; + DWORD GetMarkCount() const; + DWORD GetMarkID(DWORD guildID); + + // SERVER + void CopyMarkIdx(char * pcBuf) const; + DWORD SaveMark(DWORD guildID, BYTE * pbMarkImage); + void DeleteMark(DWORD guildID); + void GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map & mapDiffBlocks); + + // CLIENT + bool SaveBlockFromCompressedData(DWORD imgIdx, DWORD idBlock, const BYTE * pbBlock, DWORD dwSize); + bool GetBlockCRCList(DWORD imgIdx, DWORD * crcList); + + private: + + // Mark + + CGuildMarkImage * __NewImage(); + void __DeleteImage(CGuildMarkImage * pkImgDel); + + DWORD __AllocMarkID(DWORD guildID); + + CGuildMarkImage * __GetImage(DWORD imgIdx); + CGuildMarkImage * __GetImagePtr(DWORD idMark); + + std::map m_mapIdx_Image; // index = image index + std::map m_mapGID_MarkID; // index = guild id + + std::set m_setFreeMarkID; + std::string m_pathPrefix; + + private: + + // Symbol + + std::map m_mapSymbol; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/OXEvent.cpp b/source-server/Srcs/Server/game/src/OXEvent.cpp new file mode 100644 index 000000000..fd3cf594f --- /dev/null +++ b/source-server/Srcs/Server/game/src/OXEvent.cpp @@ -0,0 +1,432 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "questmanager.h" +#include "start_position.h" +#include "packet.h" +#include "buffer_manager.h" +#include "log.h" +#include "char.h" +#include "char_manager.h" +#include "OXEvent.h" +#include "desc.h" + +bool COXEventManager::Initialize() +{ + m_timedEvent = NULL; + m_map_char.clear(); + m_map_attender.clear(); + m_vec_quiz.clear(); + + SetStatus(OXEVENT_FINISH); + + return true; +} + +void COXEventManager::Destroy() +{ + CloseEvent(); + + m_map_char.clear(); + m_map_attender.clear(); + m_vec_quiz.clear(); + + SetStatus(OXEVENT_FINISH); +} + +OXEventStatus COXEventManager::GetStatus() +{ + BYTE ret = quest::CQuestManager::instance().GetEventFlag("oxevent_status"); + + switch (ret) + { + case 0 : + return OXEVENT_FINISH; + + case 1 : + return OXEVENT_OPEN; + + case 2 : + return OXEVENT_CLOSE; + + case 3 : + return OXEVENT_QUIZ; + + default : + return OXEVENT_ERR; + } + + return OXEVENT_ERR; +} + +void COXEventManager::SetStatus(OXEventStatus status) +{ + BYTE val = 0; + + switch (status) + { + case OXEVENT_OPEN : + val = 1; + break; + + case OXEVENT_CLOSE : + val = 2; + break; + + case OXEVENT_QUIZ : + val = 3; + break; + + case OXEVENT_FINISH : + case OXEVENT_ERR : + default : + val = 0; + break; + } + quest::CQuestManager::instance().RequestSetEventFlag("oxevent_status", val); +} + +bool COXEventManager::Enter(LPCHARACTER pkChar) +{ + if (GetStatus() == OXEVENT_FINISH) + { + sys_log(0, "OXEVENT : map finished. but char enter. %s", pkChar->GetName()); + return false; + } + + PIXEL_POSITION pos = pkChar->GetXYZ(); + + if (pos.x == 896500 && pos.y == 24600) + { + return EnterAttender(pkChar); + } + else if (pos.x == 896300 && pos.y == 28900) + { + return EnterAudience(pkChar); + } + else + { + sys_log(0, "OXEVENT : wrong pos enter %d %d", pos.x, pos.y); + return false; + } + + return false; +} + +bool COXEventManager::EnterAttender(LPCHARACTER pkChar) +{ + DWORD pid = pkChar->GetPlayerID(); + + m_map_char.emplace(pid, pid); + m_map_attender.emplace(pid, pid); + + return true; +} + +bool COXEventManager::EnterAudience(LPCHARACTER pkChar) +{ + DWORD pid = pkChar->GetPlayerID(); + + m_map_char.emplace(pid, pid); + + return true; +} + +bool COXEventManager::AddQuiz(unsigned char level, const char* pszQuestion, bool answer) +{ + if (m_vec_quiz.size() < (size_t) level + 1) + m_vec_quiz.resize(level + 1); + + struct tag_Quiz tmpQuiz; + + tmpQuiz.level = level; + strlcpy(tmpQuiz.Quiz, pszQuestion, sizeof(tmpQuiz.Quiz)); + tmpQuiz.answer = answer; + + m_vec_quiz[level].emplace_back(tmpQuiz); + return true; +} + +bool COXEventManager::ShowQuizList(LPCHARACTER pkChar) +{ + int c = 0; + + for (size_t i = 0; i < m_vec_quiz.size(); ++i) + { + for (size_t j = 0; j < m_vec_quiz[i].size(); ++j, ++c) + { + pkChar->ChatPacket(CHAT_TYPE_INFO, "%d %s %s", m_vec_quiz[i][j].level, m_vec_quiz[i][j].Quiz, m_vec_quiz[i][j].answer ? LC_TEXT("") : LC_TEXT("")); + } + } + + pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" : %d"), c); + return true; +} + +void COXEventManager::ClearQuiz() +{ + for (unsigned int i = 0; i < m_vec_quiz.size(); ++i) + { + m_vec_quiz[i].clear(); + } + + m_vec_quiz.clear(); +} + +EVENTINFO(OXEventInfoData) +{ + bool answer; + + OXEventInfoData() + : answer( false ) + { + } +}; + +EVENTFUNC(oxevent_timer) +{ + static BYTE flag = 0; + OXEventInfoData* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "oxevent_timer> Null pointer" ); + return 0; + } + + switch (flag) + { + case 0: + SendNoticeMap(LC_TEXT("10ʵ ϰڽϴ."), OXEVENT_MAP_INDEX, true); + flag++; + return PASSES_PER_SEC(10); + + case 1: + SendNoticeMap(LC_TEXT(""), OXEVENT_MAP_INDEX, true); + + if (info->answer == true) + { + COXEventManager::instance().CheckAnswer(true); + SendNoticeMap(LC_TEXT("O Դϴ"), OXEVENT_MAP_INDEX, true); + } + else + { + COXEventManager::instance().CheckAnswer(false); + SendNoticeMap(LC_TEXT("X Դϴ"), OXEVENT_MAP_INDEX, true); + } + + SendNoticeMap(LC_TEXT("5 Ʋ е ٱ ̵ Űڽϴ."), OXEVENT_MAP_INDEX, true); + + flag++; + return PASSES_PER_SEC(5); + + case 2: + COXEventManager::instance().WarpToAudience(); + COXEventManager::instance().SetStatus(OXEVENT_CLOSE); + SendNoticeMap(LC_TEXT(" غּ."), OXEVENT_MAP_INDEX, true); + flag = 0; + break; + } + return 0; +} + +bool COXEventManager::Quiz(unsigned char level, int timelimit) +{ + if (m_vec_quiz.size() == 0) return false; + if (level > m_vec_quiz.size()) level = m_vec_quiz.size() - 1; + if (m_vec_quiz[level].size() <= 0) return false; + + if (timelimit < 0) timelimit = 30; + + int idx = number(0, m_vec_quiz[level].size()-1); + + SendNoticeMap(LC_TEXT(" Դϴ."), OXEVENT_MAP_INDEX, true); + SendNoticeMap(m_vec_quiz[level][idx].Quiz, OXEVENT_MAP_INDEX, true); + SendNoticeMap(LC_TEXT(" O, Ʋ X ̵ּ"), OXEVENT_MAP_INDEX, true); + + if (m_timedEvent != NULL) { + event_cancel(&m_timedEvent); + } + + OXEventInfoData* answer = AllocEventInfo(); + + answer->answer = m_vec_quiz[level][idx].answer; + + timelimit -= 15; + m_timedEvent = event_create(oxevent_timer, answer, PASSES_PER_SEC(timelimit)); + + SetStatus(OXEVENT_QUIZ); + + m_vec_quiz[level].erase(m_vec_quiz[level].begin()+idx); + return true; +} + +bool COXEventManager::CheckAnswer(bool answer) +{ + if (m_map_attender.size() <= 0) return true; + + itertype(m_map_attender) iter = m_map_attender.begin(); + itertype(m_map_attender) iter_tmp; + + m_map_miss.clear(); + + int rect[4]; + if (answer != true) + { + rect[0] = 892600; + rect[1] = 22900; + rect[2] = 896300; + rect[3] = 26400; + } + else + { + rect[0] = 896600; + rect[1] = 22900; + rect[2] = 900300; + rect[3] = 26400; + } + + LPCHARACTER pkChar = NULL; + PIXEL_POSITION pos; + for (; iter != m_map_attender.end();) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + if (pkChar != NULL) + { + pos = pkChar->GetXYZ(); + + if (pos.x < rect[0] || pos.x > rect[2] || pos.y < rect[1] || pos.y > rect[3]) + { + pkChar->EffectPacket(SE_FAIL); + iter_tmp = iter; + iter++; + m_map_attender.erase(iter_tmp); + m_map_miss.emplace(pkChar->GetPlayerID(), pkChar->GetPlayerID()); + } + else + { + pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Դϴ!")); + // pkChar->CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), pkChar); + char chatbuf[256]; + int len = snprintf(chatbuf, sizeof(chatbuf), + "%s %u %u", number(0, 1) == 1 ? "cheer1" : "cheer2", (DWORD)pkChar->GetVID(), 0); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + pkChar->PacketAround(buf.read_peek(), buf.size()); + pkChar->EffectPacket(SE_SUCCESS); + + ++iter; + } + } + else + { + itertype(m_map_char) err = m_map_char.find(iter->first); + if (err != m_map_char.end()) m_map_char.erase(err); + + itertype(m_map_miss) err2 = m_map_miss.find(iter->first); + if (err2 != m_map_miss.end()) m_map_miss.erase(err2); + + iter_tmp = iter; + ++iter; + m_map_attender.erase(iter_tmp); + } + } + return true; +} + +void COXEventManager::WarpToAudience() +{ + if (m_map_miss.size() <= 0) return; + + itertype(m_map_miss) iter = m_map_miss.begin(); + LPCHARACTER pkChar = NULL; + + for (; iter != m_map_miss.end(); ++iter) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar != NULL) + { + switch ( number(0, 3)) + { + case 0 : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break; + case 1 : pkChar->Show(OXEVENT_MAP_INDEX, 890900, 28100); break; + case 2 : pkChar->Show(OXEVENT_MAP_INDEX, 896600, 20500); break; + case 3 : pkChar->Show(OXEVENT_MAP_INDEX, 902500, 28100); break; + default : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break; + } + } + } + + m_map_miss.clear(); +} + +bool COXEventManager::CloseEvent() +{ + if (m_timedEvent != NULL) { + event_cancel(&m_timedEvent); + } + + itertype(m_map_char) iter = m_map_char.begin(); + + LPCHARACTER pkChar = NULL; + for (; iter != m_map_char.end(); ++iter) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar != NULL) + pkChar->WarpSet(EMPIRE_START_X(pkChar->GetEmpire()), EMPIRE_START_Y(pkChar->GetEmpire())); + } + + Initialize(); // @fixme157 (instead of simply doing m_map_char.clear();) + + return true; +} + +bool COXEventManager::LogWinner() +{ + itertype(m_map_attender) iter = m_map_attender.begin(); + + for (; iter != m_map_attender.end(); ++iter) + { + LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar) + LogManager::instance().CharLog(pkChar, 0, "OXEVENT", "LastManStanding"); + } + + return true; +} + +bool COXEventManager::GiveItemToAttender(DWORD dwItemVnum, BYTE count) +{ + itertype(m_map_attender) iter = m_map_attender.begin(); + + for (; iter != m_map_attender.end(); ++iter) + { + LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar) + { + pkChar->AutoGiveItem(dwItemVnum, count); + LogManager::instance().ItemLog(pkChar->GetPlayerID(), 0, count, dwItemVnum, "OXEVENT_REWARD", "", pkChar->GetDesc()->GetHostName(), dwItemVnum); + } + } + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/OXEvent.h b/source-server/Srcs/Server/game/src/OXEvent.h new file mode 100644 index 000000000..fcb282443 --- /dev/null +++ b/source-server/Srcs/Server/game/src/OXEvent.h @@ -0,0 +1,64 @@ +#define OXEVENT_MAP_INDEX 113 + +struct tag_Quiz +{ + char level; + char Quiz[256]; + bool answer; +}; + +enum OXEventStatus +{ + OXEVENT_FINISH = 0, + OXEVENT_OPEN = 1, + OXEVENT_CLOSE = 2, + OXEVENT_QUIZ = 3, + + OXEVENT_ERR = 0xff +}; + +class COXEventManager : public singleton +{ + private : + std::map m_map_char; + std::map m_map_attender; + std::map m_map_miss; + + std::vector > m_vec_quiz; + + LPEVENT m_timedEvent; + + protected : + bool CheckAnswer(); + + bool EnterAudience(LPCHARACTER pChar); + bool EnterAttender(LPCHARACTER pChar); + + public : + bool Initialize(); + void Destroy(); + + OXEventStatus GetStatus(); + void SetStatus(OXEventStatus status); + + bool LoadQuizScript(const char* szFileName); + + bool Enter(LPCHARACTER pChar); + + bool CloseEvent(); + + void ClearQuiz(); + bool AddQuiz(unsigned char level, const char* pszQuestion, bool answer); + bool ShowQuizList(LPCHARACTER pChar); + + bool Quiz(unsigned char level, int timelimit); + bool GiveItemToAttender(DWORD dwItemVnum, BYTE count); + + bool CheckAnswer(bool answer); + void WarpToAudience(); + + bool LogWinner(); + + DWORD GetAttenderCount() { return m_map_attender.size(); } +}; +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/PetSystem.cpp b/source-server/Srcs/Server/game/src/PetSystem.cpp new file mode 100644 index 000000000..76542ad3e --- /dev/null +++ b/source-server/Srcs/Server/game/src/PetSystem.cpp @@ -0,0 +1,621 @@ +#include "stdafx.h" +#include "config.h" +#include "utils.h" +#include "vector.h" +#include "char.h" +#include "sectree_manager.h" +#include "char_manager.h" +#include "mob_manager.h" +#include "PetSystem.h" +#include "../../common/VnumHelper.h" +#include "packet.h" +#include "item_manager.h" +#include "item.h" + +EVENTINFO(petsystem_event_info) +{ + CPetSystem* pPetSystem; +}; + +EVENTFUNC(petsystem_update_event) +{ + petsystem_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "check_speedhack_event> Null pointer" ); + return 0; + } + + CPetSystem* pPetSystem = info->pPetSystem; + + if (NULL == pPetSystem) + return 0; + + pPetSystem->Update(0); + + return PASSES_PER_SEC(1) / 4; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// CPetActor +/////////////////////////////////////////////////////////////////////////////////////// + +CPetActor::CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options) +{ + m_dwVnum = vnum; + m_dwVID = 0; + m_dwOptions = options; + m_dwLastActionTime = 0; + + m_pkChar = 0; + m_pkOwner = owner; + + m_originalMoveSpeed = 0; + + m_dwSummonItemVID = 0; + m_dwSummonItemVnum = 0; +} + +CPetActor::~CPetActor() +{ + this->Unsummon( + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + false + #endif + ); + + m_pkOwner = 0; +} + +void CPetActor::SetName(const char* name) +{ + std::string petName = m_pkOwner->GetName(); + petName += "'s "; + petName += (name) ? name : "Pet"; + + if (IsSummoned()) + m_pkChar->SetName(petName); + + m_name = petName; +} + +bool CPetActor::Mount() +{ + if (0 == m_pkOwner) + return false; + + if (true == HasOption(EPetOption_Mountable)) + m_pkOwner->MountVnum(m_dwVnum); + + return m_pkOwner->GetMountVnum() == m_dwVnum;; +} + +void CPetActor::Unmount() +{ + if (0 == m_pkOwner) + return; + + if (m_pkOwner->IsHorseRiding()) + m_pkOwner->StopRiding(); +} + +void CPetActor::Unsummon( + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + bool updateSocket + #endif +) +{ + if (true == this->IsSummoned()) + { + this->ClearBuff(); + this->SetSummonItem(NULL + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + , updateSocket + #endif + ); + + if (NULL != m_pkOwner) + m_pkOwner->ComputePoints(); + + if (NULL != m_pkChar) + M2_DESTROY_CHARACTER(m_pkChar); + + m_pkChar = 0; + m_dwVID = 0; + } +} + +DWORD CPetActor::Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar) +{ + long x = m_pkOwner->GetX(); + long y = m_pkOwner->GetY(); + long z = m_pkOwner->GetZ(); + + if (true == bSpawnFar) + { + x += (number(0, 1) * 2 - 1) * number(2000, 2500); + y += (number(0, 1) * 2 - 1) * number(2000, 2500); + } + else + { + x += number(-100, 100); + y += number(-100, 100); + } + + if (0 != m_pkChar) + { + m_pkChar->Show (m_pkOwner->GetMapIndex(), x, y); + m_dwVID = m_pkChar->GetVID(); + + return m_dwVID; + } + + m_pkChar = CHARACTER_MANAGER::instance().SpawnMob( + m_dwVnum, + m_pkOwner->GetMapIndex(), + x, y, z, + false, (int)(m_pkOwner->GetRotation()+180), false); + + if (0 == m_pkChar) + { + sys_err("[CPetSystem::Summon] Failed to summon the pet. (vnum: %d)", m_dwVnum); + return 0; + } + + m_pkChar->SetPet(); + + m_pkChar->SetEmpire(m_pkOwner->GetEmpire()); + + m_dwVID = m_pkChar->GetVID(); + + this->SetName(petName); + + this->SetSummonItem(pSummonItem); + m_pkOwner->ComputePoints(); + m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z); + + return m_dwVID; +} + +bool CPetActor::_UpdatAloneActionAI(float fMinDist, float fMaxDist) +{ + float fDist = number(fMinDist, fMaxDist); + float r = (float)number (0, 359); + float dest_x = GetOwner()->GetX() + fDist * cos(r); + float dest_y = GetOwner()->GetY() + fDist * sin(r); + + m_pkChar->SetNowWalking(true); + + if (!m_pkChar->IsStateMove() && m_pkChar->Goto(dest_x, dest_y)) + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + m_dwLastActionTime = get_dword_time(); + + return true; +} + +bool CPetActor::_UpdateFollowAI() +{ + if (0 == m_pkChar->m_pkMobData) + { + //sys_err("[CPetActor::_UpdateFollowAI] m_pkChar->m_pkMobData is NULL"); + return false; + } + + if (0 == m_originalMoveSpeed) + { + const CMob* mobData = CMobManager::Instance().Get(m_dwVnum); + + if (0 != mobData) + m_originalMoveSpeed = mobData->m_table.sMovingSpeed; + } + float START_FOLLOW_DISTANCE = 300.0f; + float START_RUN_DISTANCE = 900.0f; + + float RESPAWN_DISTANCE = 4500.f; + int APPROACH = 200; + + bool bRun = false; + + DWORD currentTime = get_dword_time(); + + long ownerX = m_pkOwner->GetX(); long ownerY = m_pkOwner->GetY(); + long charX = m_pkChar->GetX(); long charY = m_pkChar->GetY(); + + float fDist = DISTANCE_APPROX(charX - ownerX, charY - ownerY); + + if (fDist >= RESPAWN_DISTANCE) + { + float fOwnerRot = m_pkOwner->GetRotation() * 3.141592f / 180.f; + float fx = -APPROACH * cos(fOwnerRot); + float fy = -APPROACH * sin(fOwnerRot); + if (m_pkChar->Show(m_pkOwner->GetMapIndex(), ownerX + fx, ownerY + fy)) + { + return true; + } + } + + if (fDist >= START_FOLLOW_DISTANCE) + { + if( fDist >= START_RUN_DISTANCE) + { + bRun = true; + } + + m_pkChar->SetNowWalking(!bRun); + + Follow(APPROACH); + + m_pkChar->SetLastAttacked(currentTime); + m_dwLastActionTime = currentTime; + } + + else + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + return true; +} + +bool CPetActor::Update(DWORD deltaTime) +{ + bool bResult = true; + + if (m_pkOwner->IsDead() || (IsSummoned() && m_pkChar->IsDead()) + || NULL == ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID()) + || ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID())->GetOwner() != this->GetOwner() + ) + { + this->Unsummon(); + return true; + } + + if (this->IsSummoned() && HasOption(EPetOption_Followable)) + bResult = bResult && this->_UpdateFollowAI(); + + return bResult; +} + +bool CPetActor::Follow(float fMinDistance) +{ + if( !m_pkOwner || !m_pkChar) + return false; + + float fOwnerX = m_pkOwner->GetX(); + float fOwnerY = m_pkOwner->GetY(); + + float fPetX = m_pkChar->GetX(); + float fPetY = m_pkChar->GetY(); + + float fDist = DISTANCE_SQRT(fOwnerX - fPetX, fOwnerY - fPetY); + if (fDist <= fMinDistance) + return false; + + m_pkChar->SetRotationToXY(fOwnerX, fOwnerY); + + float fx, fy; + + float fDistToGo = fDist - fMinDistance; + GetDeltaByDegree(m_pkChar->GetRotation(), fDistToGo, &fx, &fy); + + if (!m_pkChar->Goto((int)(fPetX+fx+0.5f), (int)(fPetY+fy+0.5f)) ) + return false; + + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0, 0); + + return true; +} + +bool CPetActor::SetSummonItem(LPITEM pItem + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + , bool updateSocket + #endif +) +{ + if (!pItem) + { + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + LPITEM pSummonItem = ITEM_MANAGER::instance().FindByVID(m_dwSummonItemVID); + if (pSummonItem && updateSocket) + pSummonItem->SetSocket(PET_SEAL_ACTIVE_SOCKET_IDX, false); + #endif + m_dwSummonItemVID = 0; + m_dwSummonItemVnum = 0; + return false; + } + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + pItem->SetSocket(PET_SEAL_ACTIVE_SOCKET_IDX, true); + #endif + m_dwSummonItemVID = pItem->GetVID(); + m_dwSummonItemVnum = pItem->GetVnum(); + return true; +} + +bool __PetCheckBuff(const CPetActor* pPetActor) +{ + bool bMustHaveBuff = true; + switch (pPetActor->GetVnum()) + { + case 34004: + case 34009: + if (!pPetActor->GetOwner()->GetDungeon()) + bMustHaveBuff = false; + default: + break; + } + return bMustHaveBuff; +} + +void CPetActor::GiveBuff() +{ + if (!__PetCheckBuff(this)) + return; + LPITEM item = ITEM_MANAGER::instance().FindByVID(m_dwSummonItemVID); + if (item) + item->ModifyPoints(true); + return ; +} + +void CPetActor::ClearBuff() +{ + if (NULL == m_pkOwner) + return ; + TItemTable* item_proto = ITEM_MANAGER::instance().GetTable(m_dwSummonItemVnum); + if (NULL == item_proto) + return; + if (!__PetCheckBuff(this)) // @fixme129 + return; + for (int i = 0; i < ITEM_APPLY_MAX_NUM; i++) + { + if (item_proto->aApplies[i].bType == APPLY_NONE) + continue; + m_pkOwner->ApplyPoint(item_proto->aApplies[i].bType, -item_proto->aApplies[i].lValue); + } + + return ; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// CPetSystem +/////////////////////////////////////////////////////////////////////////////////////// + +CPetSystem::CPetSystem(LPCHARACTER owner) +{ + // assert(0 != owner && "[CPetSystem::CPetSystem] Invalid owner"); + + m_pkOwner = owner; + m_dwUpdatePeriod = 400; + + m_dwLastUpdateTime = 0; +} + +CPetSystem::~CPetSystem() +{ + Destroy(); +} + +void CPetSystem::Destroy() +{ + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor) + { + delete petActor; + } + } + event_cancel(&m_pkPetSystemUpdateEvent); + m_petActorMap.clear(); +} + +bool CPetSystem::Update(DWORD deltaTime) +{ + bool bResult = true; + + DWORD currentTime = get_dword_time(); + + if (m_dwUpdatePeriod > currentTime - m_dwLastUpdateTime) + return true; + + std::vector v_garbageActor; + + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor && petActor->IsSummoned()) + { + LPCHARACTER pPet = petActor->GetCharacter(); + + if (NULL == CHARACTER_MANAGER::instance().Find(pPet->GetVID())) + { + v_garbageActor.emplace_back(petActor); + } + else + { + bResult = bResult && petActor->Update(deltaTime); + } + } + } + for (std::vector::iterator it = v_garbageActor.begin(); it != v_garbageActor.end(); it++) + DeletePet(*it); + + m_dwLastUpdateTime = currentTime; + + return bResult; +} + +void CPetSystem::DeletePet(DWORD mobVnum) +{ + TPetActorMap::iterator iter = m_petActorMap.find(mobVnum); + + if (m_petActorMap.end() == iter) + { + sys_err("[CPetSystem::DeletePet] Can't find pet on my list (VNUM: %d)", mobVnum); + return; + } + + CPetActor* petActor = iter->second; + + if (0 == petActor) + sys_err("[CPetSystem::DeletePet] Null Pointer (petActor)"); + else + delete petActor; + + m_petActorMap.erase(iter); +} + +void CPetSystem::DeletePet(CPetActor* petActor) +{ + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + if (iter->second == petActor) + { + delete petActor; + m_petActorMap.erase(iter); + + return; + } + } + + sys_err("[CPetSystem::DeletePet] Can't find petActor(0x%x) on my list(size: %d) ", petActor, m_petActorMap.size()); +} + +void CPetSystem::Unsummon(DWORD vnum, bool bDeleteFromList) +{ + CPetActor* actor = this->GetByVnum(vnum); + + if (0 == actor) + { + sys_err("[CPetSystem::GetByVnum(%d)] Null Pointer (petActor)", vnum); + return; + } + actor->Unsummon(); + + if (true == bDeleteFromList) + this->DeletePet(actor); + + bool bActive = false; + for (TPetActorMap::iterator it = m_petActorMap.begin(); it != m_petActorMap.end(); it++) + { + bActive |= it->second->IsSummoned(); + } + if (false == bActive) + { + event_cancel(&m_pkPetSystemUpdateEvent); + m_pkPetSystemUpdateEvent = NULL; + } +} + +void CPetSystem::UnsummonAll() +{ + for (auto & iter : m_petActorMap) + { + auto * actor = iter.second; + if (actor) + actor->Unsummon(); + } + + bool bActive = false; + for (auto & it : m_petActorMap) + bActive |= it.second->IsSummoned(); + if (!bActive) + { + event_cancel(&m_pkPetSystemUpdateEvent); + m_pkPetSystemUpdateEvent = nullptr; + } +} + +CPetActor* CPetSystem::Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options) +{ + CPetActor* petActor = this->GetByVnum(mobVnum); + + if (0 == petActor) + { + petActor = M2_NEW CPetActor(m_pkOwner, mobVnum, options); + m_petActorMap.emplace(mobVnum, petActor); + } + +#ifdef ENABLE_NEWSTUFF + DWORD petVID = petActor->Summon(petName, pSummonItem, bSpawnFar); + if (!petVID) + sys_err("[CPetSystem::Summon(%d)] Null Pointer (petVID)", pSummonItem); +#endif + + if (NULL == m_pkPetSystemUpdateEvent) + { + petsystem_event_info* info = AllocEventInfo(); + + info->pPetSystem = this; + + m_pkPetSystemUpdateEvent = event_create(petsystem_update_event, info, PASSES_PER_SEC(1) / 4); + } + + return petActor; +} + +CPetActor* CPetSystem::GetByVID(DWORD vid) const +{ + CPetActor* petActor = 0; + + bool bFound = false; + + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + petActor = iter->second; + + if (0 == petActor) + { + sys_err("[CPetSystem::GetByVID(%d)] Null Pointer (petActor)", vid); + continue; + } + + bFound = petActor->GetVID() == vid; + + if (true == bFound) + break; + } + + return bFound ? petActor : 0; +} + +CPetActor* CPetSystem::GetByVnum(DWORD vnum) const +{ + CPetActor* petActor = 0; + + TPetActorMap::const_iterator iter = m_petActorMap.find(vnum); + + if (m_petActorMap.end() != iter) + petActor = iter->second; + + return petActor; +} + +size_t CPetSystem::CountSummoned() const +{ + size_t count = 0; + + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor) + { + if (petActor->IsSummoned()) + ++count; + } + } + + return count; +} + +void CPetSystem::RefreshBuff() +{ + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + if (petActor && petActor->IsSummoned()) + petActor->GiveBuff(); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/PetSystem.h b/source-server/Srcs/Server/game/src/PetSystem.h new file mode 100644 index 000000000..913b2faf2 --- /dev/null +++ b/source-server/Srcs/Server/game/src/PetSystem.h @@ -0,0 +1,128 @@ +#ifndef __HEADER_PET_SYSTEM__ +#define __HEADER_PET_SYSTEM__ +#include "../../common/CommonDefines.h" + +class CHARACTER; + +struct SPetAbility +{ +}; + +class CPetActor +{ +public: + enum EPetOptions + { + EPetOption_Followable = 1 << 0, + EPetOption_Mountable = 1 << 1, + EPetOption_Summonable = 1 << 2, + EPetOption_Combatable = 1 << 3, + }; + +protected: + friend class CPetSystem; + + CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options = EPetOption_Followable | EPetOption_Summonable); + + virtual ~CPetActor(); + + virtual bool Update(DWORD deltaTime); + +protected: + virtual bool _UpdateFollowAI(); + virtual bool _UpdatAloneActionAI(float fMinDist, float fMaxDist); + +private: + bool Follow(float fMinDistance = 50.f); + +public: + LPCHARACTER GetCharacter() const { return m_pkChar; } + LPCHARACTER GetOwner() const { return m_pkOwner; } + DWORD GetVID() const { return m_dwVID; } + DWORD GetVnum() const { return m_dwVnum; } + + bool HasOption(EPetOptions option) const { return m_dwOptions & option; } + + void SetName(const char* petName); + + bool Mount(); + void Unmount(); + + DWORD Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar = false); + void Unsummon( + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + bool updateSocket = true + #endif + ); + + bool IsSummoned() const { return 0 != m_pkChar; } + bool SetSummonItem(LPITEM pItem + #ifdef USE_ACTIVE_PET_SEAL_EFFECT + , bool updateSocket = true + #endif + ); + DWORD GetSummonItemVID () { return m_dwSummonItemVID; } + + void GiveBuff(); + void ClearBuff(); + +private: + DWORD m_dwVnum; + DWORD m_dwVID; + DWORD m_dwOptions; + DWORD m_dwLastActionTime; + DWORD m_dwSummonItemVID; + DWORD m_dwSummonItemVnum; + + short m_originalMoveSpeed; + + std::string m_name; + + LPCHARACTER m_pkChar; // Instance of pet(CHARACTER) + LPCHARACTER m_pkOwner; +}; + +/** +*/ +class CPetSystem +{ +public: + typedef boost::unordered_map TPetActorMap; + +public: + CPetSystem(LPCHARACTER owner); + virtual ~CPetSystem(); + + CPetActor* GetByVID(DWORD vid) const; + CPetActor* GetByVnum(DWORD vnum) const; + + bool Update(DWORD deltaTime); + void Destroy(); + + size_t CountSummoned() const; + +public: + void SetUpdatePeriod(DWORD ms); + + CPetActor* Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable); + + void Unsummon(DWORD mobVnum, bool bDeleteFromList = false); + void Unsummon(CPetActor* petActor, bool bDeleteFromList = false); + void UnsummonAll(); + + CPetActor* AddPet(DWORD mobVnum, const char* petName, const SPetAbility& ability, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable | CPetActor::EPetOption_Combatable); + + void DeletePet(DWORD mobVnum); + void DeletePet(CPetActor* petActor); + void RefreshBuff(); + +private: + TPetActorMap m_petActorMap; + LPCHARACTER m_pkOwner; + DWORD m_dwUpdatePeriod; + DWORD m_dwLastUpdateTime; + LPEVENT m_pkPetSystemUpdateEvent; +}; + +#endif //__HEADER_PET_SYSTEM__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/affect.cpp b/source-server/Srcs/Server/game/src/affect.cpp new file mode 100644 index 000000000..115ef90cf --- /dev/null +++ b/source-server/Srcs/Server/game/src/affect.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" + +#ifndef DEBUG_ALLOC +#include +#endif + +#include "affect.h" + +#ifndef DEBUG_ALLOC +boost::object_pool affect_pool; +#endif + +CAffect* CAffect::Acquire() +{ +#ifndef DEBUG_ALLOC + return affect_pool.malloc(); +#else + return M2_NEW CAffect; +#endif +} + +void CAffect::Release(CAffect* p) +{ +#ifndef DEBUG_ALLOC + affect_pool.free(p); +#else + M2_DELETE(p); +#endif +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/affect.h b/source-server/Srcs/Server/game/src/affect.h new file mode 100644 index 000000000..0f6a8150a --- /dev/null +++ b/source-server/Srcs/Server/game/src/affect.h @@ -0,0 +1,187 @@ +#ifndef __INC_AFFECT_H +#define __INC_AFFECT_H + +class CAffect +{ + public: + DWORD dwType; + BYTE bApplyOn; + long lApplyValue; + DWORD dwFlag; + long lDuration; + long lSPCost; + + static CAffect* Acquire(); + static void Release(CAffect* p); +}; + +enum EAffectTypes +{ + AFFECT_NONE, + + AFFECT_MOV_SPEED = 200, + AFFECT_ATT_SPEED, + AFFECT_ATT_GRADE, + AFFECT_INVISIBILITY, + AFFECT_STR, + AFFECT_DEX, // 205 + AFFECT_CON, + AFFECT_INT, + AFFECT_FISH_MIND_PILL, + + AFFECT_POISON, + AFFECT_STUN, // 210 + AFFECT_SLOW, + AFFECT_DUNGEON_READY, + AFFECT_DUNGEON_UNIQUE, + + AFFECT_BUILDING, + AFFECT_REVIVE_INVISIBLE, // 215 + AFFECT_FIRE, + AFFECT_CAST_SPEED, + AFFECT_HP_RECOVER_CONTINUE, + AFFECT_SP_RECOVER_CONTINUE, + + AFFECT_POLYMORPH, // 220 + AFFECT_MOUNT, + + AFFECT_WAR_FLAG, // 222 + + AFFECT_BLOCK_CHAT, // 223 + AFFECT_CHINA_FIREWORK, + + AFFECT_BOW_DISTANCE, // 225 + AFFECT_DEF_GRADE, // 226 +#ifdef ENABLE_WOLFMAN_CHARACTER + AFFECT_BLEEDING, // 227 +#endif + + AFFECT_PREMIUM_START = 500, + AFFECT_EXP_BONUS = 500, + AFFECT_ITEM_BONUS = 501, + AFFECT_SAFEBOX = 502, // PREMIUM_SAFEBOX, + AFFECT_AUTOLOOT = 503, // PREMIUM_AUTOLOOT, + AFFECT_FISH_MIND = 504, // PREMIUM_FISH_MIND, + AFFECT_MARRIAGE_FAST = 505, + AFFECT_GOLD_BONUS = 506, + AFFECT_PREMIUM_END = 509, + + AFFECT_MALL = 510, + AFFECT_NO_DEATH_PENALTY = 511, + AFFECT_SKILL_BOOK_BONUS = 512, + AFFECT_SKILL_NO_BOOK_DELAY = 513, + + AFFECT_HAIR = 514, + AFFECT_COLLECT = 515, + AFFECT_EXP_BONUS_EURO_FREE = 516, + AFFECT_EXP_BONUS_EURO_FREE_UNDER_15 = 517, + AFFECT_UNIQUE_ABILITY = 518, + + AFFECT_CUBE_1, + AFFECT_CUBE_2, + AFFECT_CUBE_3, + AFFECT_CUBE_4, + AFFECT_CUBE_5, + AFFECT_CUBE_6, + AFFECT_CUBE_7, + AFFECT_CUBE_8, + AFFECT_CUBE_9, + AFFECT_CUBE_10, + AFFECT_CUBE_11, + AFFECT_CUBE_12, + + AFFECT_BLEND, + + AFFECT_HORSE_NAME, + AFFECT_MOUNT_BONUS, + + AFFECT_AUTO_HP_RECOVERY = 534, + AFFECT_AUTO_SP_RECOVERY = 535, + + AFFECT_DRAGON_SOUL_QUALIFIED = 540, + AFFECT_DRAGON_SOUL_DECK_0 = 541, + AFFECT_DRAGON_SOUL_DECK_1 = 542, + + AFFECT_RAMADAN_ABILITY = 300, + AFFECT_RAMADAN_RING = 301, + + AFFECT_NOG_ABILITY = 302, + AFFECT_HOLLY_STONE_POWER = 303, + + AFFECT_QUEST_START_IDX = 1000 +}; + +enum EAffectBits +{ + AFF_NONE, + + AFF_YMIR, + AFF_INVISIBILITY, + AFF_SPAWN, + + AFF_POISON, + AFF_SLOW, + AFF_STUN, + + AFF_DUNGEON_READY, + AFF_DUNGEON_UNIQUE, + + AFF_BUILDING_CONSTRUCTION_SMALL, + AFF_BUILDING_CONSTRUCTION_LARGE, + AFF_BUILDING_UPGRADE, + + AFF_MOV_SPEED_POTION, + AFF_ATT_SPEED_POTION, + + AFF_FISH_MIND, + + AFF_JEONGWIHON, + AFF_GEOMGYEONG, + AFF_CHEONGEUN, + AFF_GYEONGGONG, + AFF_EUNHYUNG, + AFF_GWIGUM, + AFF_TERROR, + AFF_JUMAGAP, + AFF_HOSIN, + AFF_BOHO, + AFF_KWAESOK, + AFF_MANASHIELD, + AFF_MUYEONG, + AFF_REVIVE_INVISIBLE, + AFF_FIRE, + AFF_GICHEON, + AFF_JEUNGRYEOK, + AFF_TANHWAN_DASH, + AFF_PABEOP, + AFF_CHEONGEUN_WITH_FALL, + AFF_POLYMORPH, + AFF_WAR_FLAG1, + AFF_WAR_FLAG2, + AFF_WAR_FLAG3, + + AFF_CHINA_FIREWORK, + AFF_HAIR, + AFF_GERMANY, + AFF_RAMADAN_RING, + +#ifdef ENABLE_WOLFMAN_CHARACTER + AFF_BLEEDING, // 42 + AFF_RED_POSSESSION, // 44 + AFF_BLUE_POSSESSION, // 43 +#endif + + AFF_BITS_MAX +}; + +extern void SendAffectAddPacket(LPDESC d, CAffect * pkAff); + +// AFFECT_DURATION_BUG_FIX +enum AffectVariable +{ + INFINITE_AFFECT_DURATION = 60 * 365 * 24 * 60 * 60 +}; +// END_AFFECT_DURATION_BUG_FIX + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/affect_flag.h b/source-server/Srcs/Server/game/src/affect_flag.h new file mode 100644 index 000000000..adfa7ef80 --- /dev/null +++ b/source-server/Srcs/Server/game/src/affect_flag.h @@ -0,0 +1,70 @@ +#ifndef __INC_METIN_II_AFFECT_FLAG_H__ +#define __INC_METIN_II_AFFECT_FLAG_H__ + +#ifndef IS_SET +#define IS_SET(flag, bit) ((flag) & (bit)) +#endif + +#ifndef SET_BIT +#define SET_BIT(var, bit) ((var) |= (bit)) +#endif + +#ifndef REMOVE_BIT +#define REMOVE_BIT(var, bit) ((var) &= ~(bit)) +#endif + +#ifndef TOGGLE_BIT +#define TOGGLE_BIT(var, bit) ((var) = (var) ^ (bit)) +#endif + +struct TAffectFlag +{ + DWORD bits[2]; + + inline TAffectFlag() { bits[0] = 0; bits[1] = 0; } + inline TAffectFlag(DWORD v1, DWORD v2 = 0) {bits[0] = v1; bits[1] = v2;} + + inline bool IsSet(int flag) const + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return false; + + return IS_SET(bits[(flag - 1) >> 5], (((DWORD)1) << ((flag - 1) & 31))); + } + + inline void Set(int flag) + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return; + + SET_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31))); + } + + inline void Reset(int flag) + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return; + + REMOVE_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31))); + } + + inline TAffectFlag& operator = (const TAffectFlag& rhs) + { + bits[0] = rhs.bits[0]; + bits[1] = rhs.bits[1]; + return *this; + } +}; + +inline bool operator == (const TAffectFlag& lhs, const TAffectFlag& rhs) +{ + return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1]; +} + +inline bool operator != (const TAffectFlag& lhs, const TAffectFlag& rhs) +{ + return !(lhs == rhs); +} + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/ani.cpp b/source-server/Srcs/Server/game/src/ani.cpp new file mode 100644 index 000000000..a6c4611b9 --- /dev/null +++ b/source-server/Srcs/Server/game/src/ani.cpp @@ -0,0 +1,388 @@ +#define _ani_cpp_ + +#include "stdafx.h" +#include "char.h" +#include "item.h" +#include "ani.h" +#include "../../common/CommonDefines.h" + +const char* FN_race_name(int race) +{ +#define FN_NAME(race) case race: return #race + switch (race) + { + FN_NAME(MAIN_RACE_WARRIOR_M); + FN_NAME(MAIN_RACE_ASSASSIN_W); + FN_NAME(MAIN_RACE_SURA_M); + FN_NAME(MAIN_RACE_SHAMAN_W); + FN_NAME(MAIN_RACE_WARRIOR_W); + FN_NAME(MAIN_RACE_ASSASSIN_M); + FN_NAME(MAIN_RACE_SURA_W); + FN_NAME(MAIN_RACE_SHAMAN_M); +#ifdef ENABLE_WOLFMAN_CHARACTER + FN_NAME(MAIN_RACE_WOLFMAN_M); +#endif + FN_NAME(MAIN_RACE_MAX_NUM); + } + + return "UNKNOWN"; +#undef FN_NAME +} + +const char* FN_weapon_type(int weapon) +{ +#define FN_NAME(weapon) case weapon: return #weapon + switch (weapon) + { + FN_NAME(WEAPON_SWORD); + FN_NAME(WEAPON_DAGGER); + FN_NAME(WEAPON_BOW); + FN_NAME(WEAPON_TWO_HANDED); + FN_NAME(WEAPON_BELL); + FN_NAME(WEAPON_FAN); + FN_NAME(WEAPON_ARROW); + FN_NAME(WEAPON_MOUNT_SPEAR); +#ifdef ENABLE_WOLFMAN_CHARACTER + FN_NAME(WEAPON_CLAW); +#endif +#ifdef ENABLE_QUIVER_SYSTEM + FN_NAME(WEAPON_QUIVER); +#endif + FN_NAME(WEAPON_NUM_TYPES); + } + + return "UNKNOWN"; +#undef FN_NAME +} + +class ANI +{ + protected: + DWORD m_speed[MAIN_RACE_MAX_NUM][2][WEAPON_NUM_TYPES][9]; + + public: + ANI(); + + public: + bool load(); + bool load_one_race(int race, const char *dir_name); + DWORD load_one_weapon(const char *dir_name, int weapon, BYTE combo, bool horse); + DWORD attack_speed(int race, int weapon, BYTE combo = 0, bool horse = false); + + void print_attack_speed(); +}; + +static class ANI s_ANI; + +DWORD FN_attack_speed_from_file(const char *file) +{ + FILE * fp = fopen(file, "r"); + + if (NULL == fp) + return 0; + + int speed = 1000; + + const char *key = "DirectInputTime"; + const char *delim = " \t\r\n"; + const char *field, *value; + + char buf[1024]; + + while (fgets(buf, 1024, fp)) + { + field = strtok(buf, delim); + value = strtok(NULL, delim); + + if (field && value) + { + if (0 == strcasecmp(field, key)) + { + float f_speed = strtof(value, NULL); + speed = (int) (f_speed * 1000.0); + break; + } + } + } + + fclose(fp); + return speed; +} + +ANI::ANI() +{ + // set default value + for (int race = 0; race < MAIN_RACE_MAX_NUM; ++race) + { + for (int weapon = 0; weapon < WEAPON_NUM_TYPES; ++weapon) + { + for (BYTE combo = 0; combo <= 8; ++combo) + { + m_speed[race][0][weapon][combo] = 1000; + m_speed[race][1][weapon][combo] = 1000; + } + } + } +} + +bool ANI::load() +{ + const char* dir_name[MAIN_RACE_MAX_NUM] = { + "data/pc/warrior", + "data/pc/assassin", + "data/pc/sura", + "data/pc/shaman", + "data/pc2/warrior", + "data/pc2/assassin", + "data/pc2/sura", + "data/pc2/shaman", +#ifdef ENABLE_WOLFMAN_CHARACTER + "data/pc3/wolfman", +#endif + }; + + for (int race = 0; race GetWear(WEAR_WEAPON); + + if (NULL == item) + return speed; + + if (ITEM_WEAPON != item->GetType()) + return speed; + + int race = ch->GetRaceNum(); + int weapon = item->GetSubType(); + + if (weapon == WEAPON_TWO_HANDED) + weapon = WEAPON_SWORD; + + return s_ANI.attack_speed(race, weapon); +} + +DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo) +{ + LPITEM item = ch->GetWear(WEAR_WEAPON); + + if (NULL == item || combo > 8) + return 1000; + + return s_ANI.attack_speed(ch->GetRaceNum(), item->GetSubType(), combo, ch->IsRiding()); +} + +void ani_print_attack_speed() +{ + s_ANI.print_attack_speed(); +} + +#if 0 +int main(int argc, char **argv) +{ + ani_init(); + ani_print_attack_speed(); + exit(0); +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/ani.h b/source-server/Srcs/Server/game/src/ani.h new file mode 100644 index 000000000..93919c6fb --- /dev/null +++ b/source-server/Srcs/Server/game/src/ani.h @@ -0,0 +1,17 @@ +/********************************************************************* + * date : 2007.11.16 + * file : ani.h + * author : mhh + * description : + */ + +#ifndef _ani_h_ +#define _ani_h_ + +void ani_init(); +DWORD ani_attack_speed(LPCHARACTER ch); +void ani_print_attack_speed(); +DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo); + +#endif /* _ani_h_ */ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/any_function.h b/source-server/Srcs/Server/game/src/any_function.h new file mode 100644 index 000000000..e97e1c03c --- /dev/null +++ b/source-server/Srcs/Server/game/src/any_function.h @@ -0,0 +1,66 @@ +// See http://www.boost.org/libs/any for Documentation. + +#ifndef __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED +#define __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED + +// what: variant type boost::any +// who: contributed by Kevlin Henney, +// with features contributed and bugs found by +// Ed Brey, Mark Rodgers, Peter Dimov, and James Curran +// when: July 2001 +// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 + +#include +#include + +#define boost _boost_func_of_SQLMsg +#define func_arg_type SQLMsg* +#define func_arg pmsg +#include "any_function.inc" +#undef func_arg +#undef func_arg_type +#undef boost + +typedef _boost_func_of_SQLMsg::any any_function; + +#define boost _boost_func_of_void +#define func_arg_type +#define func_arg +#include "any_function.inc" +#undef func_arg +#undef func_arg_type +#undef boost + +typedef _boost_func_of_void::any any_void_function; + +template +class void_binder +{ + protected: + F f; + typename F::argument_type value; + public: + void_binder(const F& f, const typename F::argument_type x) + : f(f), value(x) {} + void operator()() const { + return f(value); + } +}; + + template +inline void_binder void_bind(const F& f, const Arg& arg) +{ + typedef typename F::argument_type arg_type; + return void_binder(f, arg_type(arg)); +} + +// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. +// +// Permission to use, copy, modify, and distribute this software for any +// purpose is hereby granted without fee, provided that this copyright and +// permissions notice appear in all copies and derivatives. +// +// This software is provided "as is" without express or implied warranty. + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/any_function.inc b/source-server/Srcs/Server/game/src/any_function.inc new file mode 100644 index 000000000..1658490ca --- /dev/null +++ b/source-server/Srcs/Server/game/src/any_function.inc @@ -0,0 +1,116 @@ +namespace boost +{ + class any + { + public: // structors + + any() + : content(0) + { + } + + template + any(const ValueType & value) + : content(new holder(value)) + { + } + + any(const any & other) + : content(other.content ? other.content->clone() : 0) + { + } + + ~any() + { + delete content; + } + + public: // modifiers + + any & swap(any & rhs) + { + std::swap(content, rhs.content); + return *this; + } + + template + any & operator=(const ValueType & rhs) + { + any(rhs).swap(*this); + return *this; + } + + any & operator=(const any & rhs) + { + any(rhs).swap(*this); + return *this; + } + + void operator ()(func_arg_type func_arg) + { + (*content)(func_arg); + } + + public: // queries + + bool empty() const + { + return !content; + } + + private: // types + + class placeholder + { + public: // structors + + virtual ~placeholder() + { + } + + public: // queries + + virtual placeholder * clone() const = 0; + + virtual void operator()(func_arg_type func_arg) = 0; + + }; + + template + class holder : public placeholder + { + public: // structors + + holder(const ValueType & value) + : held(value) + { + } + + public: // queries + + virtual placeholder * clone() const + { + return new holder(held); + } + + virtual void operator ()(func_arg_type func_arg) + { + held(func_arg); + } + + public: // representation + + ValueType held; + + }; + + private: // representation + + template + friend ValueType * any_cast(any *); + + placeholder * content; + + }; + +} diff --git a/source-server/Srcs/Server/game/src/arena.cpp b/source-server/Srcs/Server/game/src/arena.cpp new file mode 100644 index 000000000..51f412648 --- /dev/null +++ b/source-server/Srcs/Server/game/src/arena.cpp @@ -0,0 +1,1166 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "packet.h" +#include "desc.h" +#include "buffer_manager.h" +#include "start_position.h" +#include "questmanager.h" +#include "char.h" +#include "char_manager.h" +#include "arena.h" + +CArena::CArena(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + m_StartPointA.x = startA_X; + m_StartPointA.y = startA_Y; + m_StartPointA.z = 0; + + m_StartPointB.x = startB_X; + m_StartPointB.y = startB_Y; + m_StartPointB.z = 0; + + m_ObserverPoint.x = (startA_X + startB_X) / 2; + m_ObserverPoint.y = (startA_Y + startB_Y) / 2; + m_ObserverPoint.z = 0; + + m_pEvent = NULL; + m_pTimeOutEvent = NULL; + + Clear(); +} + +void CArena::Clear() +{ + m_dwPIDA = 0; + m_dwPIDB = 0; + + if (m_pEvent != NULL) + { + event_cancel(&m_pEvent); + } + + if (m_pTimeOutEvent != NULL) + { + event_cancel(&m_pTimeOutEvent); + } + + m_dwSetCount = 0; + m_dwSetPointOfA = 0; + m_dwSetPointOfB = 0; +} + +bool CArenaManager::AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + CArenaMap *pArenaMap = NULL; + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) + { + pArenaMap = M2_NEW CArenaMap; + m_mapArenaMap.emplace(mapIdx, pArenaMap); + } + else + { + pArenaMap = iter->second; + } + + if (pArenaMap->AddArena(mapIdx, startA_X, startA_Y, startB_X, startB_Y) == false) + { + sys_log(0, "CArenaManager::AddArena - AddMap Error MapID: %d", mapIdx); + return false; + } + + return true; +} + +bool CArenaMap::AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + for (auto & iter : m_listArena) + { + if (!iter->CheckArea(startA_X, startA_Y, startB_X, startB_Y)) + { + sys_log(0, "CArenaMap::AddArena - Same Start Position set. stA(%d, %d) stB(%d, %d)", startA_X, startA_Y, startB_X, startB_Y); + return false; + } + } + + m_dwMapIndex = mapIdx; + + CArena *pArena = M2_NEW CArena(startA_X, startA_Y, startB_X, startB_Y); + m_listArena.emplace_back(pArena); + + return true; +} + +void CArenaManager::Destroy() +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + pArenaMap->Destroy(); + + M2_DELETE(pArenaMap); + } + m_mapArenaMap.clear(); +} + +void CArenaMap::Destroy() +{ + itertype(m_listArena) iter = m_listArena.begin(); + + sys_log(0, "ARENA: ArenaMap will be destroy. mapIndex(%d)", m_dwMapIndex); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + pArena->EndDuel(); + + M2_DELETE(pArena); + } + m_listArena.clear(); +} + +bool CArena::CheckArea(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + if (m_StartPointA.x == startA_X && m_StartPointA.y == startA_Y && + m_StartPointB.x == startB_X && m_StartPointB.y == startB_Y) + return false; + return true; +} + +void CArenaManager::SendArenaMapListTo(LPCHARACTER pChar) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArena = iter->second; + pArena->SendArenaMapListTo(pChar, (iter->first)); + } +} + +void CArenaMap::SendArenaMapListTo(LPCHARACTER pChar, DWORD mapIdx) +{ + if (pChar == NULL) return; + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + pChar->ChatPacket(CHAT_TYPE_INFO, "ArenaMapInfo Map: %d stA(%d, %d) stB(%d, %d)", mapIdx, + (CArena*)(*iter)->GetStartPointA().x, (CArena*)(*iter)->GetStartPointA().y, + (CArena*)(*iter)->GetStartPointB().x, (CArena*)(*iter)->GetStartPointB().y); + } +} + +bool CArenaManager::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + if (pCharFrom == NULL || pCharTo == NULL) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap->StartDuel(pCharFrom, pCharTo, nSetPoint, nMinute) == true) + { + return true; + } + } + + return false; +} + +bool CArenaMap::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->IsEmpty() == true) + { + return pArena->StartDuel(pCharFrom, pCharTo, nSetPoint, nMinute); + } + } + + return false; +} + +EVENTINFO(TArenaEventInfo) +{ + CArena *pArena; + BYTE state; + + TArenaEventInfo() + : pArena(0) + , state(0) + { + } +}; + +EVENTFUNC(ready_to_start_event) +{ + if (event == NULL) + return 0; + + if (event->info == NULL) + return 0; + + TArenaEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "ready_to_start_event> Null pointer" ); + return 0; + } + + CArena* pArena = info->pArena; + + if (pArena == NULL) + { + sys_err("ARENA: Arena start event info is null."); + return 0; + } + + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA == NULL || chB == NULL) + { + sys_err("ARENA: Player err in event func ready_start_event"); + + if (chA != NULL) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + } + + if (chB != NULL) + { + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerBPID(), pArena->GetPlayerAPID()); + } + + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" 밡 մϴ.")); + + pArena->EndDuel(); + return 0; + } + + switch (info->state) + { + case 0: + { + chA->SetArena(pArena); + chB->SetArena(pArena); + + int count = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count"); + + if (count > 10000) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + else + { + chA->SetPotionLimit(count); + chB->SetPotionLimit(count); + + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d մϴ."), chA->GetPotionLimit()); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d մϴ."), chB->GetPotionLimit()); + } + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + + info->state++; + return PASSES_PER_SEC(10); + } + break; + + case 1: + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart) + 4; + + DWORD dwOppList[8]; + + dwOppList[0] = (DWORD)chB->GetVID(); + TEMP_BUFFER buf; + + buf.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf.write(&dwOppList[0], 4); + chA->GetDesc()->Packet(buf.read_peek(), buf.size()); + + dwOppList[0] = (DWORD)chA->GetVID(); + TEMP_BUFFER buf2; + + buf2.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf2.write(&dwOppList[0], 4); + chB->GetDesc()->Packet(buf2.read_peek(), buf2.size()); + + return 0; + } + break; + + case 2: + { + pArena->EndDuel(); + return 0; + } + break; + + case 3: + { + chA->Show(chA->GetMapIndex(), pArena->GetStartPointA().x * 100, pArena->GetStartPointA().y * 100); + chB->Show(chB->GetMapIndex(), pArena->GetStartPointB().x * 100, pArena->GetStartPointB().y * 100); + + chA->GetDesc()->SetPhase(PHASE_GAME); + chA->StartRecoveryEvent(); + chA->SetPosition(POS_STANDING); + chA->PointChange(POINT_HP, chA->GetMaxHP() - chA->GetHP()); + chA->PointChange(POINT_SP, chA->GetMaxSP() - chA->GetSP()); + chA->ViewReencode(); + + chB->GetDesc()->SetPhase(PHASE_GAME); + chB->StartRecoveryEvent(); + chB->SetPosition(POS_STANDING); + chB->PointChange(POINT_HP, chB->GetMaxHP() - chB->GetHP()); + chB->PointChange(POINT_SP, chB->GetMaxSP() - chB->GetSP()); + chB->ViewReencode(); + + TEMP_BUFFER buf; + TEMP_BUFFER buf2; + DWORD dwOppList[8]; + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart) + 4; + + dwOppList[0] = (DWORD)chB->GetVID(); + buf.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf.write(&dwOppList[0], 4); + chA->GetDesc()->Packet(buf.read_peek(), buf.size()); + + dwOppList[0] = (DWORD)chA->GetVID(); + buf2.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf2.write(&dwOppList[0], 4); + chB->GetDesc()->Packet(buf2.read_peek(), buf2.size()); + + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + + pArena->ClearEvent(); + + return 0; + } + break; + + default: + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + + sys_log(0, "ARENA: Something wrong in event func. info->state(%d)", info->state); + + pArena->EndDuel(); + + return 0; + } + } +} + +EVENTFUNC(duel_time_out) +{ + if (event == NULL) return 0; + if (event->info == NULL) return 0; + + TArenaEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "duel_time_out> Null pointer" ); + return 0; + } + + CArena* pArena = info->pArena; + + if (pArena == NULL) + { + sys_err("ARENA: Time out event error"); + return 0; + } + + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA == NULL || chB == NULL) + { + if (chA != NULL) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + } + + if (chB != NULL) + { + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerBPID(), pArena->GetPlayerAPID()); + } + + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + + pArena->EndDuel(); + return 0; + } + else + { + switch (info->state) + { + case 0: + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart); + + chA->GetDesc()->Packet(&duelStart, sizeof(TPacketGCDuelStart)); + chA->GetDesc()->Packet(&duelStart, sizeof(TPacketGCDuelStart)); + + info->state++; + + sys_log(0, "ARENA: Because of time over, duel is end. PIDA(%d) vs PIDB(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + + return PASSES_PER_SEC(10); + break; + + case 1: + pArena->EndDuel(); + break; + } + } + + return 0; +} + +bool CArena::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + this->m_dwPIDA = pCharFrom->GetPlayerID(); + this->m_dwPIDB = pCharTo->GetPlayerID(); + this->m_dwSetCount = nSetPoint; + + pCharFrom->WarpSet(GetStartPointA().x * 100, GetStartPointA().y * 100); + pCharTo->WarpSet(GetStartPointB().x * 100, GetStartPointB().y * 100); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 0; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + + if (m_pTimeOutEvent != NULL) { + event_cancel(&m_pTimeOutEvent); + } + + info = AllocEventInfo(); + + info->pArena = this; + info->state = 0; + + m_pTimeOutEvent = event_create(duel_time_out, info, PASSES_PER_SEC(nMinute*60)); + + pCharFrom->PointChange(POINT_HP, pCharFrom->GetMaxHP() - pCharFrom->GetHP()); + pCharFrom->PointChange(POINT_SP, pCharFrom->GetMaxSP() - pCharFrom->GetSP()); + + pCharTo->PointChange(POINT_HP, pCharTo->GetMaxHP() - pCharTo->GetHP()); + pCharTo->PointChange(POINT_SP, pCharTo->GetMaxSP() - pCharTo->GetSP()); + + sys_log(0, "ARENA: Start Duel with PID_A(%d) vs PID_B(%d)", GetPlayerAPID(), GetPlayerBPID()); + return true; +} + +void CArenaManager::EndAllDuel() +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap *pArenaMap = iter->second; + if (pArenaMap != NULL) + pArenaMap->EndAllDuel(); + } + + return; +} + +void CArenaMap::EndAllDuel() +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena *pArena = *iter; + if (pArena != NULL) + pArena->EndDuel(); + } +} + +void CArena::EndDuel() +{ + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + if (m_pTimeOutEvent != NULL) { + event_cancel(&m_pTimeOutEvent); + } + + LPCHARACTER playerA = GetPlayerA(); + LPCHARACTER playerB = GetPlayerB(); + + if (playerA != NULL) + { + playerA->SetPKMode(PK_MODE_PEACE); + playerA->StartRecoveryEvent(); + playerA->SetPosition(POS_STANDING); + playerA->PointChange(POINT_HP, playerA->GetMaxHP() - playerA->GetHP()); + playerA->PointChange(POINT_SP, playerA->GetMaxSP() - playerA->GetSP()); + + playerA->SetArena(NULL); + + playerA->WarpSet(ARENA_RETURN_POINT_X(playerA->GetEmpire()), ARENA_RETURN_POINT_Y(playerA->GetEmpire())); + } + + if (playerB != NULL) + { + playerB->SetPKMode(PK_MODE_PEACE); + playerB->StartRecoveryEvent(); + playerB->SetPosition(POS_STANDING); + playerB->PointChange(POINT_HP, playerB->GetMaxHP() - playerB->GetHP()); + playerB->PointChange(POINT_SP, playerB->GetMaxSP() - playerB->GetSP()); + + playerB->SetArena(NULL); + + playerB->WarpSet(ARENA_RETURN_POINT_X(playerB->GetEmpire()), ARENA_RETURN_POINT_Y(playerB->GetEmpire())); + } + + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = CHARACTER_MANAGER::instance().FindByPID(iter->first); + if (pChar != NULL) + { + pChar->WarpSet(ARENA_RETURN_POINT_X(pChar->GetEmpire()), ARENA_RETURN_POINT_Y(pChar->GetEmpire())); + } + } + + m_mapObserver.clear(); + + sys_log(0, "ARENA: End Duel PID_A(%d) vs PID_B(%d)", GetPlayerAPID(), GetPlayerBPID()); + + Clear(); +} + +void CArenaManager::GetDuelList(lua_State* L) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + int index = 1; + lua_newtable(L); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap != NULL) + index = pArenaMap->GetDuelList(L, index); + } +} + +int CArenaMap::GetDuelList(lua_State* L, int index) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena == NULL) continue; + + if (pArena->IsEmpty() == false) + { + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA != NULL && chB != NULL) + { + lua_newtable(L); + + lua_pushstring(L, chA->GetName()); + lua_rawseti(L, -2, 1); + + lua_pushstring(L, chB->GetName()); + lua_rawseti(L, -2, 2); + + lua_pushnumber(L, m_dwMapIndex); + lua_rawseti(L, -2, 3); + + lua_pushnumber(L, pArena->GetObserverPoint().x); + lua_rawseti(L, -2, 4); + + lua_pushnumber(L, pArena->GetObserverPoint().y); + lua_rawseti(L, -2, 5); + + lua_rawseti(L, -2, index++); + } + } + } + + return index; +} + +bool CArenaManager::CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim) +{ + if (pCharAttacker == NULL || pCharVictim == NULL) return false; + + if (pCharAttacker == pCharVictim) return false; + + long mapIndex = pCharAttacker->GetMapIndex(); + if (mapIndex != pCharVictim->GetMapIndex()) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIndex); + + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = (CArenaMap*)(iter->second); + return pArenaMap->CanAttack(pCharAttacker, pCharVictim); +} + +bool CArenaMap::CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim) +{ + if (pCharAttacker == NULL || pCharVictim == NULL) return false; + + DWORD dwPIDA = pCharAttacker->GetPlayerID(); + DWORD dwPIDB = pCharVictim->GetPlayerID(); + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->CanAttack(dwPIDA, dwPIDB) == true) + { + return true; + } + } + return false; +} + +bool CArena::CanAttack(DWORD dwPIDA, DWORD dwPIDB) +{ + if (m_dwPIDA == dwPIDA && m_dwPIDB == dwPIDB) return true; + if (m_dwPIDA == dwPIDB && m_dwPIDB == dwPIDA) return true; + + return false; +} + +bool CArenaManager::OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim) +{ + if (pCharKiller == NULL || pCharVictim == NULL) return false; + + long mapIndex = pCharKiller->GetMapIndex(); + if (mapIndex != pCharVictim->GetMapIndex()) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIndex); + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = (CArenaMap*)(iter->second); + return pArenaMap->OnDead(pCharKiller, pCharVictim); +} + +bool CArenaMap::OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim) +{ + DWORD dwPIDA = pCharKiller->GetPlayerID(); + DWORD dwPIDB = pCharVictim->GetPlayerID(); + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsMember(dwPIDA) == true && pArena->IsMember(dwPIDB) == true) + { + pArena->OnDead(dwPIDA, dwPIDB); + return true; + } + } + return false; +} + +bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB) +{ + bool restart = false; + + LPCHARACTER pCharA = GetPlayerA(); + LPCHARACTER pCharB = GetPlayerB(); + + if (pCharA == NULL && pCharB == NULL) + { + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ ߴմϴ.")); + restart = false; + } + else if (pCharA == NULL && pCharB != NULL) + { + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ij Ͽ մϴ.")); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ մϴ.")); + restart = false; + } + else if (pCharA != NULL && pCharB == NULL) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ij Ͽ մϴ.")); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ մϴ.")); + restart = false; + } + else if (pCharA != NULL && pCharB != NULL) + { + if (m_dwPIDA == dwPIDA) + { + m_dwSetPointOfA++; + + if (m_dwSetPointOfA >= m_dwSetCount) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + + sys_log(0, "ARENA: Duel is end. Winner %s(%d) Loser %s(%d)", + pCharA->GetName(), GetPlayerAPID(), pCharB->GetName(), GetPlayerBPID()); + } + else + { + restart = true; + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharA->GetName()); + pCharA->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharA->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + SendChatPacketToObserver(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + sys_log(0, "ARENA: %s(%d) won a round vs %s(%d)", + pCharA->GetName(), GetPlayerAPID(), pCharB->GetName(), GetPlayerBPID()); + } + } + else if (m_dwPIDB == dwPIDA) + { + m_dwSetPointOfB++; + if (m_dwSetPointOfB >= m_dwSetCount) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + + sys_log(0, "ARENA: Duel is end. Winner(%d) Loser(%d)", GetPlayerBPID(), GetPlayerAPID()); + } + else + { + restart = true; + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharB->GetName()); + pCharA->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharB->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + SendChatPacketToObserver(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + sys_log(0, "ARENA : PID(%d) won a round. Opp(%d)", GetPlayerBPID(), GetPlayerAPID()); + } + } + else + { + // wtf + sys_log(0, "ARENA : OnDead Error (%d, %d) (%d, %d)", m_dwPIDA, m_dwPIDB, dwPIDA, dwPIDB); + } + + int potion = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count"); + pCharA->SetPotionLimit(potion); + pCharB->SetPotionLimit(potion); + } + else + { + } + + if (restart == false) + { + if (pCharA != NULL) + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + if ( pCharB != NULL) + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 2; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + } + else + { + if (pCharA != NULL) + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + if (pCharB != NULL) + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 3; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + } + + return true; +} + +bool CArenaManager::AddObserver(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = iter->second; + return pArenaMap->AddObserver(pChar, ObserverX, ObserverY); +} + +bool CArenaMap::AddObserver(LPCHARACTER pChar, WORD ObserverX, WORD ObserverY) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsMyObserver(ObserverX, ObserverY) == true) + { + pChar->SetArena(pArena); + return pArena->AddObserver(pChar); + } + } + + return false; +} + +bool CArena::IsMyObserver(WORD ObserverX, WORD ObserverY) +{ + return ((ObserverX == m_ObserverPoint.x) && (ObserverY == m_ObserverPoint.y)); +} + +bool CArena::AddObserver(LPCHARACTER pChar) +{ + DWORD pid = pChar->GetPlayerID(); + + m_mapObserver.emplace(pid, (LPCHARACTER)NULL); + + pChar->SaveExitLocation(); + pChar->WarpSet(m_ObserverPoint.x * 100, m_ObserverPoint.y * 100); + + return true; +} + +bool CArenaManager::IsArenaMap(DWORD dwMapIndex) +{ + return m_mapArenaMap.find(dwMapIndex) != m_mapArenaMap.end(); +} + +MEMBER_IDENTITY CArenaManager::IsMember(DWORD dwMapIndex, DWORD PID) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(dwMapIndex); + + if (iter != m_mapArenaMap.end()) + { + CArenaMap* pArenaMap = iter->second; + return pArenaMap->IsMember(PID); + } + + return MEMBER_NO; +} + +MEMBER_IDENTITY CArenaMap::IsMember(DWORD PID) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsObserver(PID) == true) return MEMBER_OBSERVER; + if (pArena->IsMember(PID) == true) return MEMBER_DUELIST; + } + return MEMBER_NO; +} + +bool CArena::IsObserver(DWORD PID) +{ + itertype(m_mapObserver) iter = m_mapObserver.find(PID); + + return iter != m_mapObserver.end(); +} + +void CArena::OnDisconnect(DWORD pid) +{ + if (m_dwPIDA == pid) + { + if (GetPlayerB() != NULL) + GetPlayerB()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ijͰ Ͽ մϴ.")); + + sys_log(0, "ARENA : Duel is end because of Opp(%d) is disconnect. MyPID(%d)", GetPlayerAPID(), GetPlayerBPID()); + EndDuel(); + } + else if (m_dwPIDB == pid) + { + if (GetPlayerA() != NULL) + GetPlayerA()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ijͰ Ͽ մϴ.")); + + sys_log(0, "ARENA : Duel is end because of Opp(%d) is disconnect. MyPID(%d)", GetPlayerBPID(), GetPlayerAPID()); + EndDuel(); + } +} + +void CArena::RemoveObserver(DWORD pid) +{ + itertype(m_mapObserver) iter = m_mapObserver.find(pid); + + if (iter != m_mapObserver.end()) + { + m_mapObserver.erase(iter); + } +} + +void CArena::SendPacketToObserver(const void * c_pvData, int iSize) +{ + /* + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = iter->second; + + if (pChar != NULL) + { + if (pChar->GetDesc() != NULL) + { + pChar->GetDesc()->Packet(c_pvData, iSize); + } + } + } + */ +} + +void CArena::SendChatPacketToObserver(BYTE type, const char * format, ...) +{ + /* + char chatbuf[CHAT_MAX_LEN + 1]; + va_list args; + + va_start(args, format); + vsnprintf(chatbuf, sizeof(chatbuf), format, args); + va_end(args); + + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = iter->second; + + if (pChar != NULL) + { + if (pChar->GetDesc() != NULL) + { + pChar->ChatPacket(type, chatbuf); + } + } + } + */ +} + +bool CArenaManager::EndDuel(DWORD pid) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap->EndDuel(pid) == true) return true; + } + return false; +} + +bool CArenaMap::EndDuel(DWORD pid) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->IsMember(pid) == true) + { + pArena->EndDuel(); + return true; + } + } + return false; +} + +bool CArenaManager::RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + if (pChar == NULL) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) + { + sys_log(0, "ARENA : Cannot find ArenaMap. %d %d %d", mapIdx, ObserverX, ObserverY); + return false; + } + + CArenaMap* pArenaMap = iter->second; + return pArenaMap->RegisterObserverPtr(pChar, mapIdx, ObserverX, ObserverY); +} + +bool CArenaMap::RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); ++iter) + { + CArena* pArena = *iter; + + if (pArena->IsMyObserver(ObserverX, ObserverY) == true) + { + return pArena->RegisterObserverPtr(pChar); + } + } + + return false; +} + +bool CArena::RegisterObserverPtr(LPCHARACTER pChar) +{ + DWORD pid = pChar->GetPlayerID(); + itertype(m_mapObserver) iter = m_mapObserver.find(pid); + + if (iter == m_mapObserver.end()) + { + sys_log(0, "ARENA : not in ob list"); + return false; + } + + m_mapObserver[pid] = pChar; + return true; +} + +// #ifdef ENABLE_NEWSTUFF +bool IsAllowedPotionOnPVP(DWORD dwVnum) +{ + switch (dwVnum) + { + // blue potions + case 27004: + case 27005: + case 27006: + // auto blue potions + case 39040: + case 39041: + case 39042: + case 72727: + case 72728: + case 72729: + case 72730: + return true; + } + return false; +} + +bool IsLimitedPotionOnPVP(DWORD dwVnum) +{ + return IsLimitedPotion(dwVnum) && !IsAllowedPotionOnPVP(dwVnum); +} + +bool IsLimitedPotion(DWORD dwVnum) +{ + // @fixme122 + if ((50801 <= dwVnum) && (dwVnum <= 50826)) + return true; + + // @warme005 + switch (dwVnum) + { + case 50020: + case 50021: + case 50022: + case 50801: + case 50802: + case 50813: + case 50814: + case 50817: + case 50818: + case 50819: + case 50820: + case 50821: + case 50822: + case 50823: + case 50824: + case 50825: + case 50826: + case 71044: + case 71055: + return true; + } + return false; +} +// #endif + +bool CArenaManager::IsLimitedItem(long lMapIndex, DWORD dwVnum) +{ + if (IsArenaMap(lMapIndex) && IsLimitedPotion(dwVnum)) + return true; + + return false; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/arena.h b/source-server/Srcs/Server/game/src/arena.h new file mode 100644 index 000000000..724bf2a86 --- /dev/null +++ b/source-server/Srcs/Server/game/src/arena.h @@ -0,0 +1,145 @@ +#ifndef __CLASS_ARENA_MANAGER__ +#define __CLASS_ARENA_MANAGER__ + +#include + +enum MEMBER_IDENTITY +{ + MEMBER_NO, + MEMBER_DUELIST, + MEMBER_OBSERVER, + + MEMBER_MAX +}; + +// #ifdef ENABLE_NEWSTUFF +extern bool IsAllowedPotionOnPVP(DWORD dwVnum); +extern bool IsLimitedPotionOnPVP(DWORD dwVnum); +extern bool IsLimitedPotion(DWORD dwVnum); +// #endif + +class CArena +{ + friend class CArenaMap; + + private : + DWORD m_dwPIDA; + DWORD m_dwPIDB; + + LPEVENT m_pEvent; + LPEVENT m_pTimeOutEvent; + + PIXEL_POSITION m_StartPointA; + PIXEL_POSITION m_StartPointB; + PIXEL_POSITION m_ObserverPoint; + + DWORD m_dwSetCount; + DWORD m_dwSetPointOfA; + DWORD m_dwSetPointOfB; + + std::map m_mapObserver; + + protected : + CArena(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + + bool IsEmpty() const { return ((m_dwPIDA==0) && (m_dwPIDB==0)); } + bool IsMember(DWORD dwPID) const { return ((m_dwPIDA==dwPID) || (m_dwPIDB==dwPID)); } + + bool CheckArea(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + void Clear(); + + bool CanAttack(DWORD dwPIDA, DWORD dwPIDB); + bool OnDead(DWORD dwPIDA, DWORD dwPIDB); + + bool IsObserver(DWORD pid); + bool IsMyObserver(WORD ObserverX, WORD ObserverY); + bool AddObserver(LPCHARACTER pChar); + bool RegisterObserverPtr(LPCHARACTER pChar); + + public : + DWORD GetPlayerAPID() { return m_dwPIDA; } + DWORD GetPlayerBPID() { return m_dwPIDB; } + + LPCHARACTER GetPlayerA() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDA); } + LPCHARACTER GetPlayerB() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDB); } + + PIXEL_POSITION GetStartPointA() { return m_StartPointA; } + PIXEL_POSITION GetStartPointB() { return m_StartPointB; } + + PIXEL_POSITION GetObserverPoint() { return m_ObserverPoint; } + + void EndDuel(); + void ClearEvent() { m_pEvent = NULL; } + void OnDisconnect(DWORD pid); + void RemoveObserver(DWORD pid); + + void SendPacketToObserver(const void * c_pvData, int iSize); + void SendChatPacketToObserver(BYTE type, const char * format, ...); +}; + +class CArenaMap +{ + friend class CArenaManager; + + private : + DWORD m_dwMapIndex; + std::list m_listArena; + + protected : + void Destroy(); + + bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + void SendArenaMapListTo(LPCHARACTER pChar, DWORD dwMapIndex); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + void EndAllDuel(); + bool EndDuel(DWORD pid); + + int GetDuelList(lua_State* L, int index); + + bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim); + bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim); + + bool AddObserver(LPCHARACTER pChar, WORD ObserverX, WORD ObserverY); + bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + + MEMBER_IDENTITY IsMember(DWORD PID); +}; + +class CArenaManager : public singleton +{ + private : + std::map m_mapArenaMap; + + public : + bool Initialize(); + void Destroy(); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + + bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + + void SendArenaMapListTo(LPCHARACTER pChar); + + void EndAllDuel(); + bool EndDuel(DWORD pid); + + void GetDuelList(lua_State* L); + + bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim); + + bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim); + + bool AddObserver(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + + bool IsArenaMap(DWORD dwMapIndex); + MEMBER_IDENTITY IsMember(DWORD dwMapIndex, DWORD PID); + + bool IsLimitedItem( long lMapIndex, DWORD dwVnum ); +}; + +#endif /*__CLASS_ARENA_MANAGER__*/ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/banword.cpp b/source-server/Srcs/Server/game/src/banword.cpp new file mode 100644 index 000000000..3cb6c2f8d --- /dev/null +++ b/source-server/Srcs/Server/game/src/banword.cpp @@ -0,0 +1,126 @@ +#include "stdafx.h" +#include "constants.h" +#include "banword.h" +#include "config.h" + +#include "cmd.h" + +CBanwordManager::CBanwordManager() +{ +} + +CBanwordManager::~CBanwordManager() +{ +} + +bool CBanwordManager::Initialize(TBanwordTable * p, WORD wSize) +{ + m_hashmap_words.clear(); + + for (WORD i = 0; i < wSize; ++i, ++p) + m_hashmap_words[p->szWord] = true; + + char szBuf[256]; + snprintf(szBuf, sizeof(szBuf), "Banword reloaded! (total %zu banwords)", m_hashmap_words.size()); + SendLog(szBuf); + return true; +} + +bool CBanwordManager::Find(const char * c_pszString) +{ + return m_hashmap_words.end() != m_hashmap_words.find(c_pszString); +} + +bool CBanwordManager::CheckString(const char * c_pszString, size_t _len) +{ + if (m_hashmap_words.empty()) + return false; + + typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin(); + + while (it != m_hashmap_words.end()) + { + const std::string & r = it->first; + const char * tmp = c_pszString; + ssize_t len = _len; + + while (len > 0) + { + if (is_twobyte(tmp)) + { + if (!strncmp(tmp, r.c_str(), r.size())) + return true; + + tmp += 2; + len -= 2; + } + else + { + if (!strncmp(tmp, r.c_str(), r.size())) + return true; + + ++tmp; + --len; + } + } + + it++; + } + + return false; +} + +void CBanwordManager::ConvertString(char * c_pszString, size_t _len) +{ + typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin(); + + while (it != m_hashmap_words.end()) + { + const std::string & r = it->first; + + char * tmp = c_pszString; + ssize_t len = _len; + + while (len > 0) + { + if (is_twobyte(tmp)) + { + if (!strncmp(tmp, r.c_str(), r.size())) + { + memset(tmp, '*', r.size()); + tmp += r.size(); + len -= r.size(); + } + else + { + tmp += 2; + len -= 2; + } + } + else + { + if (*tmp == '*') + { + ++tmp; + --len; + continue; + } + + if (!strncmp(tmp, r.c_str(), r.size())) + { + memset(tmp, '*', r.size()); + tmp += r.size(); + len -= r.size(); + } + else + { + ++tmp; + --len; + } + } + } + + it++; + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/banword.h b/source-server/Srcs/Server/game/src/banword.h new file mode 100644 index 000000000..332687e3f --- /dev/null +++ b/source-server/Srcs/Server/game/src/banword.h @@ -0,0 +1,23 @@ +#ifndef BANWORD_MANAGER_H_ +#define BANWORD_MANAGER_H_ + +#include + +class CBanwordManager : public singleton +{ + public: + CBanwordManager(); + virtual ~CBanwordManager(); + + bool Initialize(TBanwordTable * p, WORD wSize); + bool Find(const char * c_pszString); + bool CheckString(const char * c_pszString, size_t _len); + void ConvertString(char * c_pszString, size_t _len); + + protected: + typedef boost::unordered_map TBanwordHashmap; + TBanwordHashmap m_hashmap_words; +}; + +#endif /* BANWORD_MANAGER_H_ */ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/battle.cpp b/source-server/Srcs/Server/game/src/battle.cpp new file mode 100644 index 000000000..d9e1762c3 --- /dev/null +++ b/source-server/Srcs/Server/game/src/battle.cpp @@ -0,0 +1,870 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc.h" +#include "char.h" +#include "char_manager.h" +#include "battle.h" +#include "item.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "vector.h" +#include "packet.h" +#include "pvp.h" +#include "profiler.h" +#include "guild.h" +#include "affect.h" +#include "unique_item.h" +#include "lua_incl.h" +#include "arena.h" +#include "castle.h" +#include "sectree.h" +#include "ani.h" +#include "locale_service.h" +#include "../../common/CommonDefines.h" + +int battle_hit(LPCHARACTER ch, LPCHARACTER victim, int & iRetDam); + +bool battle_distance_valid_by_xy(long x, long y, long tx, long ty) +{ + long distance = DISTANCE_APPROX(x - tx, y - ty); + + if (distance > 170) + return false; + + return true; +} + +bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim) +{ + return battle_distance_valid_by_xy(ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY()); +} + +bool timed_event_cancel(LPCHARACTER ch) +{ + if (ch->m_pkTimedEvent) + { + event_cancel(&ch->m_pkTimedEvent); + return true; + } + + return false; +} + +#ifdef NEW_ICEDAMAGE_SYSTEM +bool battle_is_icedamage(LPCHARACTER pAttacker, LPCHARACTER pVictim) +{ + if (pAttacker && pAttacker->IsPC()) + { + DWORD race = pAttacker->GetRaceNum(); + const DWORD tmp_dwNDRFlag = pVictim->GetNoDamageRaceFlag(); + if (tmp_dwNDRFlag && + (race < MAIN_RACE_MAX_NUM) && + (IS_SET(tmp_dwNDRFlag, 1< & tmp_setNDAFlag = pVictim->GetNoDamageAffectFlag(); + if (tmp_setNDAFlag.size()) + { + for (std::set::iterator it = tmp_setNDAFlag.begin(); it != tmp_setNDAFlag.end(); ++it) + { + if (!pAttacker->IsAffectFlag(*it)) + { + return false; + } + } + } + } + return true; +} +#endif + +bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim) +{ + if (victim->IsDead()) + return false; + + { + SECTREE *sectree = NULL; + + sectree = ch->GetSectree(); + if (sectree && sectree->IsAttr(ch->GetX(), ch->GetY(), ATTR_BANPK)) + return false; + + sectree = victim->GetSectree(); + if (sectree && sectree->IsAttr(victim->GetX(), victim->GetY(), ATTR_BANPK)) + return false; + } +#ifdef NEW_ICEDAMAGE_SYSTEM + if (!battle_is_icedamage(ch, victim)) + return false; +#endif + if (ch->IsStun() || ch->IsDead()) + return false; + + if (ch->IsPC() && victim->IsPC()) + { + CGuild* g1 = ch->GetGuild(); + CGuild* g2 = victim->GetGuild(); + + if (g1 && g2) + { + if (g1->UnderWar(g2->GetID())) + return true; + } + } + + if (IS_CASTLE_MAP(ch->GetMapIndex()) && false==castle_can_attack(ch, victim)) + return false; + + if (CArenaManager::instance().CanAttack(ch, victim) == true) + return true; + + return CPVPManager::instance().CanAttack(ch, victim); +} + +int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim) +{ + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + if (!victim || ch == victim) + return BATTLE_NONE; + + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + if (!battle_is_attackable(ch, victim)) + return BATTLE_NONE; + + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY()); + + if (!victim->IsBuilding()) + { + int max = 300; + + if (false == ch->IsPC()) + { + max = (int) (ch->GetMobAttackRange() * 1.15f); + } + else + { + if (false == victim->IsPC() && BATTLE_TYPE_MELEE == victim->GetMobBattleType()) + max = MAX(300, (int) (victim->GetMobAttackRange() * 1.15f)); + } + + if (distance > max) + { + if (test_server) + sys_log(0, "VICTIM_FAR: %s distance: %d max: %d", ch->GetName(), distance, max); + + return BATTLE_NONE; + } + } + + if (timed_event_cancel(ch)) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿ Ǿϴ.")); + + if (timed_event_cancel(victim)) + victim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿ Ǿϴ.")); + + ch->SetPosition(POS_FIGHTING); + ch->SetVictim(victim); + + const PIXEL_POSITION & vpos = victim->GetXYZ(); + ch->SetRotationToXY(vpos.x, vpos.y); + + int dam; + int ret = battle_hit(ch, victim, dam); + return (ret); +} + +void battle_end_ex(LPCHARACTER ch) +{ + if (ch->IsPosition(POS_FIGHTING)) + ch->SetPosition(POS_STANDING); +} + +void battle_end(LPCHARACTER ch) +{ + battle_end_ex(ch); +} + +// AG = Attack Grade +// AL = Attack Limit +int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev) +{ + if (iDam < 3) + iDam = number(1, 5); + + //return CALCULATE_DAMAGE_LVDELTA(iAttackerLev, iVictimLev, iDam); + return iDam; +} + +int CalcMagicDamageWithValue(int iDam, LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel()); +} + +int CalcMagicDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + int iDam = 0; + + if (pkAttacker->IsNPC()) + { + iDam = CalcMeleeDamage(pkAttacker, pkVictim, false, false); + } + + iDam += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + + return CalcMagicDamageWithValue(iDam, pkAttacker, pkVictim); +} + +float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating) +{ + int iARSrc; + int iERSrc; + + { + int attacker_dx = pkAttacker->GetPolymorphPoint(POINT_DX); + int attacker_lv = pkAttacker->GetLevel(); + + int victim_dx = pkVictim->GetPolymorphPoint(POINT_DX); + int victim_lv = pkAttacker->GetLevel(); + + iARSrc = MIN(90, (attacker_dx * 4 + attacker_lv * 2) / 6); + iERSrc = MIN(90, (victim_dx * 4 + victim_lv * 2) / 6); + } + + float fAR = ((float) iARSrc + 210.0f) / 300.0f; + + if (bIgnoreTargetRating) + return fAR; + + float fER = ((float) (iERSrc * 2 + 5) / (iERSrc + 95)) * 3.0f / 10.0f; + + return fAR - fER; +} + +int SumAttResBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int attBonusID, int resistBonusID) +{ + return pkAttacker->GetPoint(attBonusID) - pkVictim->GetPoint(resistBonusID); +} + +int SumBreakBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int attBonusID, int resistBonusID) +{ + if (!pkVictim->IsPC()) + return 100 - pkVictim->GetPoint(resistBonusID); + return 100 + pkAttacker->GetPoint(attBonusID) - pkVictim->GetPoint(resistBonusID); +} + +int CalcDamBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iDam) +{ + LPITEM pkWeapon = pkAttacker->GetWear(WEAR_WEAPON); + if (pkWeapon) + { + switch (pkWeapon->GetSubType()) + { + case WEAPON_SWORD: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_SWORD, POINT_RESIST_SWORD) / 100; + break; + + case WEAPON_TWO_HANDED: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_TWOHAND, POINT_RESIST_TWOHAND) / 100; + break; + + case WEAPON_DAGGER: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_DAGGER, POINT_RESIST_DAGGER) / 100; + break; + + case WEAPON_BELL: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_BELL, POINT_RESIST_BELL) / 100; + break; + + case WEAPON_FAN: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_FAN, POINT_RESIST_FAN) / 100; + break; + + #ifdef ENABLE_WOLFMAN_CHARACTER + case WEAPON_CLAW: + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_CLAW, POINT_RESIST_CLAW) / 100; + #if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_ITEM_CLAW_AS_DAGGER) + iDam = iDam * SumBreakBonus(pkAttacker, pkVictim, POINT_ATTBONUS_DAGGER, POINT_RESIST_DAGGER) / 100; + #endif + break; + #endif + } + } + return iDam; +} + +int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk) +{ + if (!pkVictim->IsPC()) + iAtk += pkAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_ATTACK_BONUS); + + if (!pkAttacker->IsPC()) + { + int iReduceDamagePct = pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_TRANSFER_DAMAGE); + iAtk = iAtk * (100 + iReduceDamagePct) / 100; + } + + if (pkAttacker->IsNPC() && pkVictim->IsPC()) + { + iAtk = (iAtk * CHARACTER_MANAGER::instance().GetMobDamageRate(pkAttacker)) / 100; + } + + if (pkVictim->IsNPC()) + { + if (pkVictim->IsRaceFlag(RACE_FLAG_ANIMAL)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ANIMAL)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_UNDEAD)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_UNDEAD)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_DEVIL)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DEVIL)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_HUMAN)) + iAtk += (iAtk * SumAttResBonus(pkAttacker, pkVictim, POINT_ATTBONUS_HUMAN, POINT_RESIST_HUMAN)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ORC)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ORC)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_MILGYO)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MILGYO)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_INSECT)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_INSECT)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_FIRE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_FIRE)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ICE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ICE)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_DESERT)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DESERT)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_TREE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_TREE)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_CZ)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_CZ)) / 100; + + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MONSTER)) / 100; + } + else if (pkVictim->IsPC()) + { + iAtk += (iAtk * SumAttResBonus(pkAttacker, pkVictim, POINT_ATTBONUS_HUMAN, POINT_RESIST_HUMAN)) / 100; + + switch (pkVictim->GetJob()) + { + case JOB_WARRIOR: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_WARRIOR)) / 100; + break; + + case JOB_ASSASSIN: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ASSASSIN)) / 100; + break; + + case JOB_SURA: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SURA)) / 100; + break; + + case JOB_SHAMAN: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SHAMAN)) / 100; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_WOLFMAN)) / 100; + break; +#endif + } + } + + if (pkAttacker->IsPC()) + { + switch (pkAttacker->GetJob()) + { + case JOB_WARRIOR: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_WARRIOR)) / 100; + break; + + case JOB_ASSASSIN: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_ASSASSIN)) / 100; + break; + + case JOB_SURA: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SURA)) / 100; + break; + + case JOB_SHAMAN: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SHAMAN)) / 100; + break; + + #ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_WOLFMAN)) / 100; + break; + #endif + } + } + + if (pkAttacker->IsNPC() && pkVictim->IsPC()) + { + if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ELEC)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ELEC)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_FIRE)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_FIRE)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ICE)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ICE)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_WIND)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_WIND)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_EARTH)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_EARTH)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_DARK)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_DARK)) / 10000; + } + + if (pkAttacker->IsPC() && pkVictim->IsNPC()) + { + if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_ELEC)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_ELECT)) / 10000; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_FIRE)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_FIRE)) / 10000; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_ICE)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_ICE)) / 10000; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_WIND)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_WIND)) / 10000; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_EARTH)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_EARTH)) / 10000; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_DARK)) + iAtk += (iAtk * 30 * pkAttacker->GetPoint(POINT_ENCHANT_DARK)) / 10000; + } + + return iAtk; +} + +void Item_GetDamage(LPITEM pkItem, int* pdamMin, int* pdamMax) +{ + *pdamMin = 0; + *pdamMax = 1; + + if (!pkItem) + return; + + switch (pkItem->GetType()) + { + case ITEM_ROD: + case ITEM_PICK: + return; + } + + if (pkItem->GetType() != ITEM_WEAPON) + sys_err("Item_GetDamage - !ITEM_WEAPON vnum=%d, type=%d", pkItem->GetOriginalVnum(), pkItem->GetType()); + + *pdamMin = pkItem->GetValue(3); + *pdamMax = pkItem->GetValue(4); +} + +int CalcMeleeDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreDefense, bool bIgnoreTargetRating) +{ + LPITEM pWeapon = pkAttacker->GetWear(WEAR_WEAPON); + bool bPolymorphed = pkAttacker->IsPolymorphed(); + + if (pWeapon && !(bPolymorphed && !pkAttacker->IsPolyMaintainStat())) + { + if (pWeapon->GetType() != ITEM_WEAPON) + return 0; + + switch (pWeapon->GetSubType()) + { + case WEAPON_SWORD: + case WEAPON_DAGGER: + case WEAPON_TWO_HANDED: + case WEAPON_BELL: + case WEAPON_FAN: + case WEAPON_MOUNT_SPEAR: +#ifdef ENABLE_WOLFMAN_CHARACTER + case WEAPON_CLAW: +#endif + break; + + case WEAPON_BOW: + sys_err("CalcMeleeDamage should not handle bows (name: %s)", pkAttacker->GetName()); + return 0; + + default: + return 0; + } + } + + int iDam = 0; + float fAR = CalcAttackRating(pkAttacker, pkVictim, bIgnoreTargetRating); + int iDamMin = 0, iDamMax = 0; + + // TESTSERVER_SHOW_ATTACKINFO + int DEBUG_iDamCur = 0; + int DEBUG_iDamBonus = 0; + // END_OF_TESTSERVER_SHOW_ATTACKINFO + + if (bPolymorphed && !pkAttacker->IsPolyMaintainStat()) + { + // MONKEY_ROD_ATTACK_BUG_FIX + Item_GetDamage(pWeapon, &iDamMin, &iDamMax); + // END_OF_MONKEY_ROD_ATTACK_BUG_FIX + + DWORD dwMobVnum = pkAttacker->GetPolymorphVnum(); + const CMob * pMob = CMobManager::instance().Get(dwMobVnum); + + if (pMob) + { + int iPower = pkAttacker->GetPolymorphPower(); + iDamMin += pMob->m_table.dwDamageRange[0] * iPower / 100; + iDamMax += pMob->m_table.dwDamageRange[1] * iPower / 100; + } + } + else if (pWeapon) + { + // MONKEY_ROD_ATTACK_BUG_FIX + Item_GetDamage(pWeapon, &iDamMin, &iDamMax); + // END_OF_MONKEY_ROD_ATTACK_BUG_FIX + } + else if (pkAttacker->IsNPC()) + { + iDamMin = pkAttacker->GetMobDamageMin(); + iDamMax = pkAttacker->GetMobDamageMax(); + } + + iDam = number(iDamMin, iDamMax) * 2; + + // TESTSERVER_SHOW_ATTACKINFO + DEBUG_iDamCur = iDam; + // END_OF_TESTSERVER_SHOW_ATTACKINFO + + int iAtk = 0; + + // level must be ignored when multiply by fAR, so subtract it before calculation. + iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2); + iAtk = (int) (iAtk * fAR); + iAtk += pkAttacker->GetLevel() * 2; // and add again + + if (pWeapon) + { + iAtk += pWeapon->GetValue(5) * 2; + + // 2004.11.12.myevan.TESTSERVER_SHOW_ATTACKINFO + DEBUG_iDamBonus = pWeapon->GetValue(5) * 2; + /////////////////////////////////////////////// + } + + iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); // party attacker role bonus + iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100); + + iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk); + + int iDef = 0; + + if (!bIgnoreDefense) + { + iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkVictim->GetPoint(POINT_DEF_BONUS)) / 100); + + if (!pkAttacker->IsPC()) + iDef += pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_DEFENSE_BONUS); + } + + if (pkAttacker->IsNPC()) + iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply()); + + iDam = MAX(0, iAtk - iDef); + + if (test_server) + { + int DEBUG_iLV = pkAttacker->GetLevel()*2; + int DEBUG_iST = int((pkAttacker->GetPoint(POINT_ATT_GRADE) - DEBUG_iLV) * fAR); + int DEBUG_iPT = pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + int DEBUG_iWP = 0; + int DEBUG_iPureAtk = 0; + int DEBUG_iPureDam = 0; + char szRB[32] = ""; + char szGradeAtkBonus[32] = ""; + + DEBUG_iWP = int(DEBUG_iDamCur * fAR); + DEBUG_iPureAtk = DEBUG_iLV + DEBUG_iST + DEBUG_iWP+DEBUG_iDamBonus; + DEBUG_iPureDam = iAtk - iDef; + + if (pkAttacker->IsNPC()) + { + snprintf(szGradeAtkBonus, sizeof(szGradeAtkBonus), "=%d*%.1f", DEBUG_iPureAtk, pkAttacker->GetMobDamageMultiply()); + DEBUG_iPureAtk = int(DEBUG_iPureAtk * pkAttacker->GetMobDamageMultiply()); + } + + if (DEBUG_iDamBonus != 0) + snprintf(szRB, sizeof(szRB), "+RB(%d)", DEBUG_iDamBonus); + + char szPT[32] = ""; + + if (DEBUG_iPT != 0) + snprintf(szPT, sizeof(szPT), ", PT=%d", DEBUG_iPT); + + char szUnknownAtk[32] = ""; + + if (iAtk != DEBUG_iPureAtk) + snprintf(szUnknownAtk, sizeof(szUnknownAtk), "+?(%d)", iAtk-DEBUG_iPureAtk); + + char szUnknownDam[32] = ""; + + if (iDam != DEBUG_iPureDam) + snprintf(szUnknownDam, sizeof(szUnknownDam), "+?(%d)", iDam-DEBUG_iPureDam); + + char szMeleeAttack[128]; + + snprintf(szMeleeAttack, sizeof(szMeleeAttack), + "%s(%d)-%s(%d)=%d%s, ATK=LV(%d)+ST(%d)+WP(%d)%s%s%s, AR=%.3g%s", + pkAttacker->GetName(), + iAtk, + pkVictim->GetName(), + iDef, + iDam, + szUnknownDam, + DEBUG_iLV, + DEBUG_iST, + DEBUG_iWP, + szRB, + szUnknownAtk, + szGradeAtkBonus, + fAR, + szPT); + + pkAttacker->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack); + pkVictim->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack); + } + + return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel()); +} + +int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense) +{ + if (!pkBow || pkBow->GetType() != ITEM_WEAPON || pkBow->GetSubType() != WEAPON_BOW) + return 0; + + if (!pkArrow) + return 0; + + int iDist = (int) (DISTANCE_SQRT(pkAttacker->GetX() - pkVictim->GetX(), pkAttacker->GetY() - pkVictim->GetY())); + //int iGap = (iDist / 100) - 5 - pkBow->GetValue(5) - pkAttacker->GetPoint(POINT_BOW_DISTANCE); + int iGap = (iDist / 100) - 5 - pkAttacker->GetPoint(POINT_BOW_DISTANCE); + int iPercent = 100 - (iGap * 5); + +#ifdef ENABLE_QUIVER_SYSTEM + if (pkArrow->GetSubType() == WEAPON_QUIVER) + iPercent = 100; +#endif + + if (iPercent <= 0) + return 0; + else if (iPercent > 100) + iPercent = 100; + + int iDam = 0; + + float fAR = CalcAttackRating(pkAttacker, pkVictim, false); + iDam = number(pkBow->GetValue(3), pkBow->GetValue(4)) * 2 + pkArrow->GetValue(3); + int iAtk; + + // level must be ignored when multiply by fAR, so subtract it before calculation. + iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2); + iAtk = (int) (iAtk * fAR); + iAtk += pkAttacker->GetLevel() * 2; // and add again + + // Refine Grade + iAtk += pkBow->GetValue(5) * 2; + + iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100); + + iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk); + + int iDef = 0; + + if (!bIgnoreDefense) + iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkAttacker->GetPoint(POINT_DEF_BONUS)) / 100); + + if (pkAttacker->IsNPC()) + iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply()); + + iDam = MAX(0, iAtk - iDef); + + int iPureDam = iDam; + + iPureDam = (iPureDam * iPercent) / 100; + + if (test_server) + { + pkAttacker->ChatPacket(CHAT_TYPE_INFO, "ARROW %s -> %s, DAM %d DIST %d GAP %d %% %d", + pkAttacker->GetName(), + pkVictim->GetName(), + iPureDam, + iDist, iGap, iPercent); + } + + return iPureDam; + //return iDam; +} + +void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + if (pkAttacker->GetPoint(POINT_POISON_PCT) && !pkVictim->IsAffectFlag(AFF_POISON)) + { + if (number(1, 100) <= pkAttacker->GetPoint(POINT_POISON_PCT)) + pkVictim->AttackedByPoison(pkAttacker); + } +#ifdef ENABLE_WOLFMAN_CHARACTER + if (pkAttacker->GetPoint(POINT_BLEEDING_PCT) && !pkVictim->IsAffectFlag(AFF_BLEEDING)) + { + if (number(1, 100) <= pkAttacker->GetPoint(POINT_BLEEDING_PCT)) + pkVictim->AttackedByBleeding(pkAttacker); + } +#endif + int iStunDuration = 2; + if (pkAttacker->IsPC() && !pkVictim->IsPC()) + iStunDuration = 4; + + AttackAffect(pkAttacker, pkVictim, POINT_STUN_PCT, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iStunDuration, "STUN"); + AttackAffect(pkAttacker, pkVictim, POINT_SLOW_PCT, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 20, "SLOW"); +} + +int battle_hit(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int & iRetDam) +{ + if (test_server) + sys_log(0, "battle_hit : [%s] attack to [%s] : dam :%d type :%d", pkAttacker->GetName(), pkVictim->GetName(), iRetDam); + + int iDam = CalcMeleeDamage(pkAttacker, pkVictim); + + if (iDam <= 0) + return (BATTLE_DAMAGE); + + NormalAttackAffect(pkAttacker, pkVictim); + + iDam = CalcDamBonus(pkAttacker, pkVictim, iDam); + + float attMul = pkAttacker->GetAttMul(); + float tempIDam = iDam; + iDam = attMul * tempIDam + 0.5f; + + iRetDam = iDam; + + if (pkVictim->Damage(pkAttacker, iDam, DAMAGE_TYPE_NORMAL)) + return (BATTLE_DEAD); + + return (BATTLE_DAMAGE); +} + +DWORD GET_ATTACK_SPEED(LPCHARACTER ch) +{ + if (NULL == ch) + return 1000; + + LPITEM item = ch->GetWear(WEAR_WEAPON); + DWORD default_bonus = SPEEDHACK_LIMIT_BONUS; + DWORD riding_bonus = 0; + + if (ch->IsRiding()) + { + riding_bonus = 50; + } + + DWORD ani_speed = ani_attack_speed(ch); + DWORD real_speed = (ani_speed * 100) / (default_bonus + ch->GetPoint(POINT_ATT_SPEED) + riding_bonus); + + if (item && item->GetSubType() == WEAPON_DAGGER) + real_speed /= 2; +#ifdef ENABLE_WOLFMAN_CHARACTER + else if (item && item->GetSubType() == WEAPON_CLAW) + real_speed /= 2; +#endif + + return real_speed; +} + +void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + if (NULL == ch || NULL == victim) + return; + + if (!ch->IsPC()) + return; + + ch->m_kAttackLog.dwVID = victim->GetVID(); + ch->m_kAttackLog.dwTime = current_time; +} + +void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + if (NULL == ch || NULL == victim) + return; + + if (!ch->IsPC()) + return; + + victim->m_AttackedLog.dwPID = ch->GetPlayerID(); + victim->m_AttackedLog.dwAttackedTime= current_time; +} + +bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + if(!gHackCheckEnable) return false; + + if (ch->m_kAttackLog.dwVID == victim->GetVID()) + { + if (current_time - ch->m_kAttackLog.dwTime < GET_ATTACK_SPEED(ch)) + { + INCREASE_SPEED_HACK_COUNT(ch); + + if (test_server) + { + sys_log(0, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", + ch->GetName(), + current_time - ch->m_kAttackLog.dwTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", + ch->GetName(), + current_time - ch->m_kAttackLog.dwTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + } + + SET_ATTACK_TIME(ch, victim, current_time); + SET_ATTACKED_TIME(ch, victim, current_time); + return true; + } + } + + SET_ATTACK_TIME(ch, victim, current_time); + + if (victim->m_AttackedLog.dwPID == ch->GetPlayerID()) + { + if (current_time - victim->m_AttackedLog.dwAttackedTime < GET_ATTACK_SPEED(ch)) + { + INCREASE_SPEED_HACK_COUNT(ch); + + if (test_server) + { + sys_log(0, "%s Attack Speed HACK! time (delta, limit)=(%u, %u), hack_count = %d", + ch->GetName(), + current_time - victim->m_AttackedLog.dwAttackedTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + + ch->ChatPacket(CHAT_TYPE_INFO, "Attack Speed Hack(%s), (delta, limit)=(%u, %u)", + ch->GetName(), + current_time - victim->m_AttackedLog.dwAttackedTime, + GET_ATTACK_SPEED(ch)); + } + + SET_ATTACKED_TIME(ch, victim, current_time); + return true; + } + } + + SET_ATTACKED_TIME(ch, victim, current_time); + return false; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/battle.h b/source-server/Srcs/Server/game/src/battle.h new file mode 100644 index 000000000..449b1e6e6 --- /dev/null +++ b/source-server/Srcs/Server/game/src/battle.h @@ -0,0 +1,105 @@ +#ifndef __INC_METIN_II_GAME_BATTLE_H__ +#define __INC_METIN_II_GAME_BATTLE_H__ + +#include "char.h" + +enum EBattleTypes +{ + BATTLE_NONE, + BATTLE_DAMAGE, + BATTLE_DEFENSE, + BATTLE_DEAD +}; + +extern int SumAttResBonus(int attbonus, int resbonus); +extern int SumBreakBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int attBonusID, int resistBonusID); +extern int CalcDamBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iDam); +extern int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk); +extern int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev); +extern int CalcMeleeDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim, bool bIgnoreDefense = false, bool bIgnoreTargetRating = false); +extern int CalcMagicDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim); +extern int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense = false); +extern float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating = false); + +#ifdef NEW_ICEDAMAGE_SYSTEM +extern bool battle_is_icedamage(LPCHARACTER pAttacker, LPCHARACTER pVictim); +#endif +extern bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim); +extern int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim); +extern void battle_end(LPCHARACTER ch); + +extern bool battle_distance_valid_by_xy(long x, long y, long tx, long ty); +extern bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim); +extern int battle_count_attackers(LPCHARACTER ch); + +extern void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim); + +inline void AttackAffect(LPCHARACTER pkAttacker, + LPCHARACTER pkVictim, + BYTE att_point, + DWORD immune_flag, + DWORD affect_idx, + BYTE affect_point, + long affect_amount, + DWORD affect_flag, + int time, + const char* name) +{ + if (pkAttacker->GetPoint(att_point) && !pkVictim->IsAffectFlag(affect_flag)) + { + if (number(1, 100) <= pkAttacker->GetPoint(att_point) && !pkVictim->IsImmune(immune_flag)) + { + pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true); + + if (test_server) + { + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) SUCCESS", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point)); + } + } + else if (test_server) + { + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) FAIL", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point)); + } + } +} + +inline void SkillAttackAffect(LPCHARACTER pkVictim, + int success_pct, + DWORD immune_flag, + DWORD affect_idx, + BYTE affect_point, + long affect_amount, + DWORD affect_flag, + int time, + const char* name) +{ + if (success_pct && !pkVictim->IsAffectFlag(affect_flag)) + { + if (number(1, 1000) <= success_pct && !pkVictim->IsImmune(immune_flag)) + { + pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true); + + // SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX + if (test_server) + pkVictim->ChatPacket(CHAT_TYPE_PARTY, + "%s(%d%%) -> %s SUCCESS", name, success_pct, name); + // END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX + } + else if (test_server) + { + // SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s(%d%%) -> %s FAIL", name, success_pct, name); + // END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX + } + } +} + +#define GET_SPEED_HACK_COUNT(ch) ((ch)->m_speed_hack_count) +#define INCREASE_SPEED_HACK_COUNT(ch) (++GET_SPEED_HACK_COUNT(ch)) +DWORD GET_ATTACK_SPEED(LPCHARACTER ch); +void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); +void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); +bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/belt_inventory_helper.h b/source-server/Srcs/Server/game/src/belt_inventory_helper.h new file mode 100644 index 000000000..09ddb798d --- /dev/null +++ b/source-server/Srcs/Server/game/src/belt_inventory_helper.h @@ -0,0 +1,91 @@ +#ifndef __HEADER_BELT_INVENTORY_HELPER__ +#define __HEADER_BELT_INVENTORY_HELPER__ + +#include "char.h" +#include "item.h" + +class CBeltInventoryHelper +{ +public: + typedef BYTE TGradeUnit; + + static TGradeUnit GetBeltGradeByRefineLevel(DWORD level) + { + static TGradeUnit beltGradeByLevelTable[] = + { + 0, + 1, // +1 + 1, // +2 + 2, // +3 + 2, // +4, + 3, // +5 + 4, // +6, + 5, // +7, + 6, // +8, + 7, // +9 + }; + + if (level >= _countof(beltGradeByLevelTable)) + { + sys_err("CBeltInventoryHelper::GetBeltGradeByRefineLevel - Overflow level (%u)", level); + return 0; + } + + return beltGradeByLevelTable[level]; + } + + static const TGradeUnit* GetAvailableRuleTableByGrade() + { + static TGradeUnit availableRuleByGrade[BELT_INVENTORY_SLOT_COUNT] = { + 1, 2, 4, 6, + 3, 3, 4, 6, + 5, 5, 5, 6, + 7, 7, 7, 7 + }; + + return availableRuleByGrade; + } + + static bool IsAvailableCell(WORD cell, int beltGrade /*int beltLevel*/) + { + //const TGradeUnit beltGrade = GetBeltGradeByRefineLevel(beltLevel); + const TGradeUnit* ruleTable = GetAvailableRuleTableByGrade(); + + return ruleTable[cell] <= beltGrade; + } + + static bool IsExistItemInBeltInventory(LPCHARACTER pc) + { + for (WORD i = BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END; ++i) + { + LPITEM beltInventoryItem = pc->GetInventoryItem(i); + + if (NULL != beltInventoryItem) + return true; + } + + return false; + } + + static bool CanMoveIntoBeltInventory(LPITEM item) + { + bool canMove = false; + + if (item->GetType() == ITEM_USE) + { + switch (item->GetSubType()) + { + case USE_POTION: + case USE_POTION_NODELAY: + case USE_ABILITY_UP: + canMove = true; + break; + } + } + + return canMove; + } +}; + +#endif //__HEADER_BELT_INVENTORY_HELPER__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/blend_item.cpp b/source-server/Srcs/Server/game/src/blend_item.cpp new file mode 100644 index 000000000..01a59c1ea --- /dev/null +++ b/source-server/Srcs/Server/game/src/blend_item.cpp @@ -0,0 +1,226 @@ +#include "stdafx.h" +#include "constants.h" +#include "log.h" +#include "locale_service.h" +#include "item.h" +#include "blend_item.h" + +#define DO_ALL_BLEND_INFO(iter) for (iter=s_blend_info.begin(); iter!=s_blend_info.end(); ++iter) + +struct BLEND_ITEM_INFO +{ + DWORD item_vnum; + int apply_type; + int apply_value[MAX_BLEND_ITEM_VALUE]; + int apply_duration[MAX_BLEND_ITEM_VALUE]; +}; + +typedef std::vector T_BLEND_ITEM_INFO; +T_BLEND_ITEM_INFO s_blend_info; + +bool Blend_Item_init() +{ + BLEND_ITEM_INFO *blend_item_info = NULL; + T_BLEND_ITEM_INFO::iterator iter; + char file_name[256]; + snprintf (file_name, sizeof(file_name), "%s/blend.txt", LocaleService_GetBasePath().c_str()); + + sys_log(0, "Blend_Item_init %s ", file_name); + + DO_ALL_BLEND_INFO(iter) + { + blend_item_info = *iter; + M2_DELETE(blend_item_info); + } + s_blend_info.clear(); + + if (false==Blend_Item_load(file_name)) + { + sys_err(" fail"); + return false; + } + return true; +} + +bool Blend_Item_load(char *file) +{ + FILE *fp; + char one_line[256]; + const char *delim = " \t\r\n"; + char *v; + + BLEND_ITEM_INFO *blend_item_info{}; + + if (0==file || 0==file[0]) + return false; + + if ((fp = fopen(file, "r"))==0) + return false; + + while (fgets(one_line, 256, fp)) + { + if (one_line[0]=='#') + continue; + + const char* token_string = strtok(one_line, delim); + + if (NULL==token_string) + continue; + + TOKEN("section") + { + blend_item_info = M2_NEW BLEND_ITEM_INFO; + memset(blend_item_info, 0x00, sizeof(BLEND_ITEM_INFO)); + } + else TOKEN("item_vnum") + { + v = strtok(NULL, delim); + + if (NULL==v) + { + fclose(fp); + return false; + } + + str_to_number(blend_item_info->item_vnum, v); + } + else TOKEN("apply_type") + { + v = strtok(NULL, delim); + + if (NULL==v) + { + fclose(fp); + return false; + } + + blend_item_info->apply_type = FN_get_apply_type(v); + } + else TOKEN("apply_value") + { + for (int i=0; iapply_value[i], v); + } + } + else TOKEN("apply_duration") + { + for (int i=0; iapply_duration[i], v); + } + } + else TOKEN("end") + { + s_blend_info.emplace_back(blend_item_info); + } + } + + fclose(fp); + + return true; +} + +static int FN_random_index() +{ + int percent = number(1,100); + + if (percent<=10) // level 1 :10% + return 0; + else if (percent<=30) // level 2 : 20% + return 1; + else if (percent<=70) // level 3 : 40% + return 2; + else if (percent<=90) // level 4 : 20% + return 3; + else + return 4; // level 5 : 10% + + return 0; +} + +static int FN_ECS_random_index() +{ + int percent = number(1,100); + + if (percent<=5) // level 1 : 5% + return 0; + else if (percent<=15) // level 2 : 10% + return 1; + else if (percent<=60) // level 3 : 45% + return 2; + else if (percent<=85) // level 4 : 25% + return 3; + else + return 4; // level 5 : 15% + + return 0; +} + +bool Blend_Item_set_value(LPITEM item) +{ + BLEND_ITEM_INFO *blend_info; + T_BLEND_ITEM_INFO::iterator iter; + + DO_ALL_BLEND_INFO(iter) + { + blend_info = *iter; + if (blend_info->item_vnum == item->GetVnum()) + { + int apply_type; + int apply_value; + int apply_duration; + + if (item->GetVnum() == 51002) + { + apply_type = blend_info->apply_type; + apply_value = blend_info->apply_value [FN_ECS_random_index()]; + apply_duration = blend_info->apply_duration [FN_ECS_random_index()]; + } + else + { + apply_type = blend_info->apply_type; + apply_value = blend_info->apply_value [FN_random_index()]; + apply_duration = blend_info->apply_duration [FN_random_index()]; + } + sys_log (0, "blend_item : type : %d, value : %d, du : %d", apply_type, apply_value, apply_duration); + item->SetSocket(0, apply_type); + item->SetSocket(1, apply_value); + item->SetSocket(2, apply_duration); + return true; + } + + } + return false; +} + +bool Blend_Item_find(DWORD item_vnum) +{ + BLEND_ITEM_INFO *blend_info; + T_BLEND_ITEM_INFO::iterator iter; + + DO_ALL_BLEND_INFO(iter) + { + blend_info = *iter; + if (blend_info->item_vnum == item_vnum) + return true; + } + return false; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/blend_item.h b/source-server/Srcs/Server/game/src/blend_item.h new file mode 100644 index 000000000..d0134b30d --- /dev/null +++ b/source-server/Srcs/Server/game/src/blend_item.h @@ -0,0 +1,19 @@ +/********************************************************************* + * date : 2007.02.24 + * file : blend_item.h + * author : mhh + * description : + */ + +#ifndef _blend_item_h_ +#define _blend_item_h_ + +#define MAX_BLEND_ITEM_VALUE 5 + +bool Blend_Item_init(); +bool Blend_Item_load(char *file); +bool Blend_Item_set_value(LPITEM item); +bool Blend_Item_find(DWORD item_vnum); + +#endif /* _blend_item_h_ */ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/buff_on_attributes.cpp b/source-server/Srcs/Server/game/src/buff_on_attributes.cpp new file mode 100644 index 000000000..679f841b3 --- /dev/null +++ b/source-server/Srcs/Server/game/src/buff_on_attributes.cpp @@ -0,0 +1,164 @@ +#include "stdafx.h" +#include "../../common/tables.h" +#include "item.h" +#include "char.h" +#include "buff_on_attributes.h" +#include + +CBuffOnAttributes::CBuffOnAttributes(LPCHARACTER pOwner, BYTE point_type, std::vector * p_vec_buff_wear_targets) +: m_pBuffOwner(pOwner), m_bPointType(point_type), m_p_vec_buff_wear_targets(p_vec_buff_wear_targets) +{ + Initialize(); +} + +CBuffOnAttributes::~CBuffOnAttributes() +{ +} + +void CBuffOnAttributes::Initialize() +{ + m_bBuffValue = 0; + m_map_additional_attrs.clear(); +} + +void CBuffOnAttributes::RemoveBuffFromItem(LPITEM pItem) +{ + if (0 == m_bBuffValue) + return ; + if (NULL != pItem) + { + if (pItem->GetCell() < INVENTORY_MAX_NUM) + return; + std::vector ::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM); + if (m_p_vec_buff_wear_targets->end() == it) + return; + + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + if (it != m_map_additional_attrs.end()) + { + int& sum_of_attr_value = it->second; + int old_value = sum_of_attr_value * m_bBuffValue / 100; + int new_value = (sum_of_attr_value - attr.sValue) * m_bBuffValue / 100; + m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value); + sum_of_attr_value -= attr.sValue; + } + else + { + sys_err ("Buff ERROR(type %d). This item(%d) attr_type(%d) was not in buff pool", m_bPointType, pItem->GetVnum(), attr.bType); + return; + } + } + } +} + +void CBuffOnAttributes::AddBuffFromItem(LPITEM pItem) +{ + if (0 == m_bBuffValue) + return ; + if (NULL != pItem) + { + if (pItem->GetCell() < INVENTORY_MAX_NUM) + return; + std::vector ::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM); + if (m_p_vec_buff_wear_targets->end() == it) + return; + + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + + if (it == m_map_additional_attrs.end()) + { + m_pBuffOwner->ApplyPoint(attr.bType, attr.sValue * m_bBuffValue / 100); + m_map_additional_attrs.emplace(attr.bType, attr.sValue); + } + + else + { + int& sum_of_attr_value = it->second; + int old_value = sum_of_attr_value * m_bBuffValue / 100; + int new_value = (sum_of_attr_value + attr.sValue) * m_bBuffValue / 100; + m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value); + sum_of_attr_value += attr.sValue; + } + } + } +} + +void CBuffOnAttributes::ChangeBuffValue(BYTE bNewValue) +{ + if (0 == m_bBuffValue) + On(bNewValue); + else if (0 == bNewValue) + Off(); + else + { + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + int& sum_of_attr_value = it->second; + //int old_value = sum_of_attr_value * m_bBuffValue / 100; + //int new_value = sum_of_attr_value * bNewValue / 100; + + m_pBuffOwner->ApplyPoint(it->first, -sum_of_attr_value * m_bBuffValue / 100); + } + m_bBuffValue = bNewValue; + } +} + +bool CBuffOnAttributes::On(BYTE bValue) +{ + if (0 != m_bBuffValue || 0 == bValue) + return false; + + int n = m_p_vec_buff_wear_targets->size(); + m_map_additional_attrs.clear(); + for (int i = 0; i < n; i++) + { + LPITEM pItem = m_pBuffOwner->GetWear(m_p_vec_buff_wear_targets->at(i)); + if (NULL != pItem) + { + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + if (it != m_map_additional_attrs.end()) + { + it->second += attr.sValue; + } + else + { + m_map_additional_attrs.emplace(attr.bType, attr.sValue); + } + } + } + } + + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + m_pBuffOwner->ApplyPoint(it->first, it->second * bValue / 100); + } + + m_bBuffValue = bValue; + + return true; +} + +void CBuffOnAttributes::Off() +{ + if (0 == m_bBuffValue) + return ; + + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + m_pBuffOwner->ApplyPoint(it->first, -it->second * m_bBuffValue / 100); + } + Initialize(); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/buff_on_attributes.h b/source-server/Srcs/Server/game/src/buff_on_attributes.h new file mode 100644 index 000000000..82bf43d8b --- /dev/null +++ b/source-server/Srcs/Server/game/src/buff_on_attributes.h @@ -0,0 +1,31 @@ +#ifndef __METIN2_BUFF_ON_ATTRIBUTES_H +#define __METIN2_BUFF_ON_ATTRIBUTES_H + +class CHARACTER; + +class CBuffOnAttributes +{ +public: + CBuffOnAttributes(LPCHARACTER pOwner, BYTE m_point_type, std::vector * vec_buff_targets); + ~CBuffOnAttributes(); + + void RemoveBuffFromItem(LPITEM pItem); + void AddBuffFromItem(LPITEM pItem); + void ChangeBuffValue(BYTE bNewValue); + + bool On(BYTE bValue); + void Off(); + + void Initialize(); +private: + LPCHARACTER m_pBuffOwner; + BYTE m_bPointType; + BYTE m_bBuffValue; + std::vector * m_p_vec_buff_wear_targets; + + typedef std::map TMapAttr; // apply_type, apply_value + TMapAttr m_map_additional_attrs; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/buffer_manager.cpp b/source-server/Srcs/Server/game/src/buffer_manager.cpp new file mode 100644 index 000000000..ff8902c30 --- /dev/null +++ b/source-server/Srcs/Server/game/src/buffer_manager.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "buffer_manager.h" + +TEMP_BUFFER::TEMP_BUFFER(int Size, bool bForceDelete) +{ + forceDelete = bForceDelete; + + if (forceDelete) + Size = MAX(Size, 1024 * 128); + + buf = buffer_new(Size); +} + +TEMP_BUFFER::~TEMP_BUFFER() +{ + buffer_delete(buf); +} + +const void * TEMP_BUFFER::read_peek() +{ + return (buffer_read_peek(buf)); +} + +void TEMP_BUFFER::write(const void * data, int size) +{ + buffer_write(buf, data, size); +} + +int TEMP_BUFFER::size() +{ + return buffer_size(buf); +} + +void TEMP_BUFFER::reset() +{ + buffer_reset(buf); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/buffer_manager.h b/source-server/Srcs/Server/game/src/buffer_manager.h new file mode 100644 index 000000000..6304f7488 --- /dev/null +++ b/source-server/Srcs/Server/game/src/buffer_manager.h @@ -0,0 +1,34 @@ +#ifndef __INC_METIN_II_GAME_BUFFER_MANAGER_H__ +#define __INC_METIN_II_GAME_BUFFER_MANAGER_H__ +#include "../../common/stl.h" + +class TEMP_BUFFER +{ + public: + TEMP_BUFFER(int Size = 8192, bool ForceDelete = false ); + ~TEMP_BUFFER(); + + const void * read_peek(); + + template>* = nullptr> + void write(const T& c_pvData) { + write(&c_pvData, sizeof(T)); + } + template>* = nullptr> + void write(const C& v) { + write(v.data(), v.size() * sizeof(typename C::value_type)); + } + + void write(const void * data, int size); + int size(); + void reset(); + + LPBUFFER getptr() { return buf; } + + protected: + LPBUFFER buf; + bool forceDelete; +}; + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/build_locale_string.py b/source-server/Srcs/Server/game/src/build_locale_string.py new file mode 100644 index 000000000..745f50e1d --- /dev/null +++ b/source-server/Srcs/Server/game/src/build_locale_string.py @@ -0,0 +1,70 @@ +#!/usr/local/bin/python2.7 +import sys +import glob + +def IsHangulInLine(line): + for ch in line: + if ord(ch) >= 128: + return True + return False + +hanList = [] + +for fileName in glob.glob("*.cpp"): + isComment = False + + lines = open(fileName).readlines() + for line in lines: + linePos = lines.index(line) + if isComment: + commentEnd = line.find("*/") + if commentEnd < 0: + continue + else: + line = line[commentEnd+2:] + isComment = False + + + else: + commentBegin = line.find("/*") + if commentBegin >= 0: + commentEnd = line.find("*/") + if commentEnd >= 0: + line = line[:commentBegin] + line[commentEnd+2:] + else: + isComment = True + + while True: + pos = line.find("TEXT") + + if pos < 0: + break + + if len(line) < pos + 5: + break + + + if line[pos+4] != "(": + break + + pos += 5 + if line[pos] != '"': + break + + endPos = line[pos+1:].find('"') + if endPos < 0: + raise + + endPos += pos + endPos += 2 + + han = line[pos+1:endPos-1] + if not han in hanList: + hanList.append(han) + + line = line[endPos:] + +out = open("locale_string.txt", "w") +for han in hanList: + out.write("%s\n" % (han)) + print han diff --git a/source-server/Srcs/Server/game/src/building.cpp b/source-server/Srcs/Server/game/src/building.cpp new file mode 100644 index 000000000..ade16bc52 --- /dev/null +++ b/source-server/Srcs/Server/game/src/building.cpp @@ -0,0 +1,1256 @@ +#include "stdafx.h" +#include "constants.h" +#include "sectree_manager.h" +#include "item_manager.h" +#include "buffer_manager.h" +#include "config.h" +#include "packet.h" +#include "char.h" +#include "char_manager.h" +#include "guild.h" +#include "guild_manager.h" +#include "desc.h" +#include "desc_manager.h" +#include "desc_client.h" +#include "questmanager.h" +#include "building.h" + +enum +{ + // ADD_SUPPLY_BUILDING + BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL = 14061, + BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM = 14062, + BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE = 14063, + // END_OF_ADD_SUPPLY_BUILDING + + FLAG_VNUM = 14200, + WALL_DOOR_VNUM = 14201, + WALL_BACK_VNUM = 14202, + WALL_LEFT_VNUM = 14203, + WALL_RIGHT_VNUM = 14204, +}; + +using namespace building; + +CObject::CObject(TObject * pData, TObjectProto * pProto) + : m_pProto(pProto), m_dwVID(0), m_chNPC(NULL) +{ + CEntity::Initialize(ENTITY_OBJECT); + + thecore_memcpy(&m_data, pData, sizeof(TObject)); +} + +CObject::~CObject() +{ + Destroy(); +} + +void CObject::Destroy() +{ + if (m_pProto) + { + SECTREE_MANAGER::instance().ForAttrRegion(GetMapIndex(), + GetX() + m_pProto->lRegion[0], + GetY() + m_pProto->lRegion[1], + GetX() + m_pProto->lRegion[2], + GetY() + m_pProto->lRegion[3], + (long)m_data.zRot, // ADD_BUILDING_ROTATION + ATTR_OBJECT, + ATTR_REGION_MODE_REMOVE); + } + + CEntity::Destroy(); + + if (GetSectree()) + GetSectree()->RemoveEntity(this); + + // NPC should be destroyed in CHARACTER_MANAGER + // BUILDING_NPC + /* + if (m_chNPC) { + M2_DESTROY_CHARACTER(m_chNPC); + } + */ + + RemoveSpecialEffect(); + // END_OF_BUILDING_NPC +} + +// BUILDING_NPC +void CObject::Reconstruct(DWORD dwVnum) +{ + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(m_data.lMapIndex); + if (!r) + return; + + CLand* pLand = GetLand(); + pLand->RequestDeleteObject(GetID()); + pLand->RequestCreateObject(dwVnum, m_data.lMapIndex, m_data.x - r->sx, m_data.y - r->sy, m_data.xRot, m_data.yRot, m_data.zRot, false); +} +// END_OF_BUILDING_NPC + +void CObject::EncodeInsertPacket(LPENTITY entity) +{ + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + sys_log(0, "ObjectInsertPacket vid %u vnum %u rot %f %f %f", + m_dwVID, m_data.dwVnum, m_data.xRot, m_data.yRot, m_data.zRot); + + TPacketGCCharacterAdd pack; + + memset(&pack, 0, sizeof(TPacketGCCharacterAdd)); + + pack.header = HEADER_GC_CHARACTER_ADD; + pack.dwVID = m_dwVID; + pack.bType = CHAR_TYPE_BUILDING; + pack.angle = m_data.zRot; + pack.x = GetX(); + pack.y = GetY(); + pack.z = GetZ(); + pack.wRaceNum = m_data.dwVnum; + + pack.dwAffectFlag[0] = unsigned(m_data.xRot); + pack.dwAffectFlag[1] = unsigned(m_data.yRot); + + if (GetLand()) + { + // pack.dwGuild = GetLand()->GetOwner(); + } + + d->Packet(&pack, sizeof(pack)); +} + +void CObject::EncodeRemovePacket(LPENTITY entity) +{ + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + sys_log(0, "ObjectRemovePacket vid %u", m_dwVID); + + TPacketGCCharacterDelete pack; + + pack.header = HEADER_GC_CHARACTER_DEL; + pack.id = m_dwVID; + + d->Packet(&pack, sizeof(TPacketGCCharacterDelete)); +} + +void CObject::SetVID(DWORD dwVID) +{ + m_dwVID = dwVID; +} + +bool CObject::Show(long lMapIndex, long x, long y) +{ + LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!tree) + { + sys_err("cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex); + return false; + } + + if (GetSectree()) + { + GetSectree()->RemoveEntity(this); + ViewCleanup(); + } + + m_data.lMapIndex = lMapIndex; + m_data.x = x; + m_data.y = y; + + Save(); + + SetMapIndex(lMapIndex); + SetXYZ(x, y, 0); + + tree->InsertEntity(this); + UpdateSectree(); + + SECTREE_MANAGER::instance().ForAttrRegion(lMapIndex, + x + m_pProto->lRegion[0], + y + m_pProto->lRegion[1], + x + m_pProto->lRegion[2], + y + m_pProto->lRegion[3], + (long)m_data.zRot, + ATTR_OBJECT, + ATTR_REGION_MODE_SET); + + return true; +} + +void CObject::Save() +{ +} + +void CObject::ApplySpecialEffect() +{ + if (m_pProto) + { + // ADD_SUPPLY_BUILDING + if (m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE) + { + CLand* pLand = GetLand(); + DWORD guild_id = 0; + if (pLand) + guild_id = pLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(guild_id); + if (pGuild) + { + switch (m_pProto->dwVnum) + { + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL: + pGuild->SetMemberCountBonus(6); + break; + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM: + pGuild->SetMemberCountBonus(12); + break; + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE: + pGuild->SetMemberCountBonus(18); + break; + } + if (map_allow_find(pLand->GetMapIndex())) + { + pGuild->BroadcastMemberCountBonus(); + } + } + } + // END_OF_ADD_SUPPLY_BUILDING + } +} + +void CObject::RemoveSpecialEffect() +{ + if (m_pProto) + { + // ADD_SUPPLY_BUILDING + if (m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE) + { + CLand* pLand = GetLand(); + DWORD guild_id = 0; + if (pLand) + guild_id = pLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(guild_id); + if (pGuild) + { + pGuild->SetMemberCountBonus(0); + if (map_allow_find(pLand->GetMapIndex())) + pGuild->BroadcastMemberCountBonus(); + } + } + // END_OF_ADD_SUPPLY_BUILDING + } +} + +// BUILDING_NPC +void CObject::RegenNPC() +{ + if (!m_pProto) + return; + + if (!m_pProto->dwNPCVnum) + return; + + if (!m_pkLand) + return; + + DWORD dwGuildID = m_pkLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(dwGuildID); + + if (!pGuild) + return; + + int x = m_pProto->lNPCX; + int y = m_pProto->lNPCY; + int newX, newY; + + float rot = m_data.zRot * 2.0f * M_PI / 360.0f; + + newX = (int)(( x * cosf(rot)) + ( y * sinf(rot))); + newY = (int)(( y * cosf(rot)) - ( x * sinf(rot))); + + m_chNPC = CHARACTER_MANAGER::instance().SpawnMob(m_pProto->dwNPCVnum, + GetMapIndex(), + GetX() + newX, + GetY() + newY, + GetZ(), + false, + (int)m_data.zRot); + + if (!m_chNPC) + { + sys_err("Cannot create guild npc"); + return; + } + + m_chNPC->SetGuild(pGuild); + + if ( m_pProto->dwVnum == 14061 || m_pProto->dwVnum == 14062 || m_pProto->dwVnum == 14063 ) + { + quest::PC* pPC = quest::CQuestManager::instance().GetPC(pGuild->GetMasterPID()); + + if ( pPC != NULL ) + { + pPC->SetFlag("alter_of_power.build_level", pGuild->GetLevel()); + } + } +} +// END_OF_BUILDING_NPC + +//////////////////////////////////////////////////////////////////////////////////// + +CLand::CLand(TLand * pData) +{ + thecore_memcpy(&m_data, pData, sizeof(TLand)); +} + +CLand::~CLand() +{ + Destroy(); +} + +void CLand::Destroy() +{ + itertype(m_map_pkObject) it = m_map_pkObject.begin(); + + while (it != m_map_pkObject.end()) + { + LPOBJECT pkObj = (it++)->second; + CManager::instance().UnregisterObject(pkObj); + M2_DELETE(pkObj); + } + + m_map_pkObject.clear(); + m_map_pkObjectByVID.clear(); +} + +const TLand & CLand::GetData() +{ + return m_data; +} + +void CLand::PutData(const TLand * data) +{ + memcpy(&m_data, data, sizeof(TLand)); + + if (m_data.dwGuildID) + { + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(m_data.lMapIndex); + + if (r) + { + CharacterVectorInteractor i; + + if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(20040, i)) + { + CharacterVectorInteractor::iterator it = i.begin(); + + while (it != i.end()) + { + LPCHARACTER ch = *(it++); + + if (ch->GetMapIndex() != m_data.lMapIndex) + continue; + + int x = ch->GetX() - r->sx; + int y = ch->GetY() - r->sy; + + if (x > m_data.x + m_data.width || x < m_data.x) + continue; + + if (y > m_data.y + m_data.height || y < m_data.y) + continue; + + M2_DESTROY_CHARACTER(ch); + } + } + } + } +} + +void CLand::InsertObject(LPOBJECT pkObj) +{ + m_map_pkObject.emplace(pkObj->GetID(), pkObj); + m_map_pkObjectByVID.emplace(pkObj->GetVID(), pkObj); + + pkObj->SetLand(this); +} + +LPOBJECT CLand::FindObject(DWORD dwID) +{ + std::map::iterator it = m_map_pkObject.find(dwID); + + if (it == m_map_pkObject.end()) + return NULL; + + return it->second; +} + +LPOBJECT CLand::FindObjectByGroup(DWORD dwGroupVnum) +{ + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetGroup() == dwGroupVnum) + return pObj; + } + + return NULL; +} + +LPOBJECT CLand::FindObjectByVnum(DWORD dwVnum) +{ + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetVnum() == dwVnum) + return pObj; + } + + return NULL; +} + +// BUILDING_NPC +LPOBJECT CLand::FindObjectByNPC(LPCHARACTER npc) +{ + if (!npc) + return NULL; + + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetNPC() == npc) + return pObj; + } + + return NULL; +} +// END_OF_BUILDING_NPC + +LPOBJECT CLand::FindObjectByVID(DWORD dwVID) +{ + std::map::iterator it = m_map_pkObjectByVID.find(dwVID); + + if (it == m_map_pkObjectByVID.end()) + return NULL; + + return it->second; +} + +void CLand::DeleteObject(DWORD dwID) +{ + LPOBJECT pkObj; + + if (!(pkObj = FindObject(dwID))) + return; + + sys_log(0, "Land::DeleteObject %u", dwID); + CManager::instance().UnregisterObject(pkObj); + M2_DESTROY_CHARACTER (pkObj->GetNPC()); + + m_map_pkObject.erase(dwID); + m_map_pkObjectByVID.erase(dwID); + + M2_DELETE(pkObj); +} + +struct FIsIn +{ + long sx, sy; + long ex, ey; + + bool bIn; + FIsIn ( long sx_, long sy_, long ex_, long ey_) + : sx(sx_), sy(sy_), ex(ex_), ey(ey_), bIn(false) + {} + + void operator () (LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsMonster()) + { + return; + } + if (sx <= ch->GetX() && ch->GetX() <= ex + && sy <= ch->GetY() && ch->GetY() <= ey) + { + bIn = true; + } + } + } +}; + +bool CLand::RequestCreateObject(DWORD dwVnum, long lMapIndex, long x, long y, float xRot, float yRot, float zRot, bool checkAnother) +{ + SECTREE_MANAGER& rkSecTreeMgr = SECTREE_MANAGER::instance(); + TObjectProto * pkProto = CManager::instance().GetObjectProto(dwVnum); + + if (!pkProto) + { + sys_err("Invalid Object vnum %u", dwVnum); + return false; + } + const TMapRegion * r = rkSecTreeMgr.GetMapRegion(lMapIndex); + if (!r) + return false; + + sys_log(0, "RequestCreateObject(vnum=%u, map=%d, pos=(%d,%d), rot=(%.1f,%.1f,%.1f) region(%d,%d ~ %d,%d)", + dwVnum, lMapIndex, x, y, xRot, yRot, zRot, r->sx, r->sy, r->ex, r->ey); + + x += r->sx; + y += r->sy; + + int sx = r->sx + m_data.x; + int ex = sx + m_data.width; + int sy = r->sy + m_data.y; + int ey = sy + m_data.height; + + int osx = x + pkProto->lRegion[0]; + int osy = y + pkProto->lRegion[1]; + int oex = x + pkProto->lRegion[2]; + int oey = y + pkProto->lRegion[3]; + + float rad = zRot * 2.0f * M_PI / 360.0f; + + int tsx = (int)(pkProto->lRegion[0] * cosf(rad) + pkProto->lRegion[1] * sinf(rad) + x); + int tsy = (int)(pkProto->lRegion[0] * -sinf(rad) + pkProto->lRegion[1] * cosf(rad) + y); + + int tex = (int)(pkProto->lRegion[2] * cosf(rad) + pkProto->lRegion[3] * sinf(rad) + x); + int tey = (int)(pkProto->lRegion[2] * -sinf(rad) + pkProto->lRegion[3] * cosf(rad) + y); + + if (tsx < sx || tex > ex || tsy < sy || tey > ey) + { + sys_err("invalid position: object is outside of land region\nLAND: %d %d ~ %d %d\nOBJ: %d %d ~ %d %d", sx, sy, ex, ey, osx, osy, oex, oey); + return false; + } + + // ADD_BUILDING_ROTATION + if ( checkAnother ) + { + if (rkSecTreeMgr.ForAttrRegion(lMapIndex, osx, osy, oex, oey, (long)zRot, ATTR_OBJECT, ATTR_REGION_MODE_CHECK)) + { + sys_err("another object already exist"); + return false; + } + FIsIn f (osx, osy, oex, oey); + rkSecTreeMgr.GetMap(lMapIndex)->for_each(f); + + if (f.bIn) + { + sys_err("another object already exist"); + return false; + } + } + // END_OF_BUILDING_NPC + + TPacketGDCreateObject p; + + p.dwVnum = dwVnum; + p.dwLandID = m_data.dwID; + p.lMapIndex = lMapIndex; + p.x = x; + p.y = y; + p.xRot = xRot; + p.yRot = yRot; + p.zRot = zRot; + + db_clientdesc->DBPacket(HEADER_GD_CREATE_OBJECT, 0, &p, sizeof(TPacketGDCreateObject)); + return true; +} + +void CLand::RequestDeleteObject(DWORD dwID) +{ + if (!FindObject(dwID)) + { + sys_err("no object by id %u", dwID); + return; + } + + db_clientdesc->DBPacket(HEADER_GD_DELETE_OBJECT, 0, &dwID, sizeof(DWORD)); + sys_log(0, "RequestDeleteObject id %u", dwID); +} + +void CLand::RequestDeleteObjectByVID(DWORD dwVID) +{ + LPOBJECT pkObj; + + if (!(pkObj = FindObjectByVID(dwVID))) + { + sys_err("no object by vid %u", dwVID); + return; + } + + DWORD dwID = pkObj->GetID(); + db_clientdesc->DBPacket(HEADER_GD_DELETE_OBJECT, 0, &dwID, sizeof(DWORD)); + sys_log(0, "RequestDeleteObject vid %u id %u", dwVID, dwID); +} + +void CLand::SetOwner(DWORD dwGuild) +{ + if (m_data.dwGuildID != dwGuild) + { + m_data.dwGuildID = dwGuild; + RequestUpdate(dwGuild); + } +} + +void CLand::RequestUpdate(DWORD dwGuild) +{ + DWORD a[2]; + + a[0] = GetID(); + a[1] = dwGuild; + + db_clientdesc->DBPacket(HEADER_GD_UPDATE_LAND, 0, &a[0], sizeof(DWORD) * 2); + sys_log(0, "RequestUpdate id %u guild %u", a[0], a[1]); +} + +//////////////////////////////////////////////////////////////////////////////////// + +CManager::CManager() +{ +} + +CManager::~CManager() +{ + Destroy(); +} + +void CManager::Destroy() +{ + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + for ( ; it != m_map_pkLand.end(); ++it) { + M2_DELETE(it->second); + } + m_map_pkLand.clear(); +} + +bool CManager::LoadObjectProto(const TObjectProto * pProto, int size) // from DB +{ + m_vec_kObjectProto.resize(size); + thecore_memcpy(&m_vec_kObjectProto[0], pProto, sizeof(TObjectProto) * size); + + for (int i = 0; i < size; ++i) + { + TObjectProto & r = m_vec_kObjectProto[i]; + + // BUILDING_NPC + sys_log(0, "ObjectProto %u price %u upgrade %u upg_limit %u life %d NPC %u", + r.dwVnum, r.dwPrice, r.dwUpgradeVnum, r.dwUpgradeLimitTime, r.lLife, r.dwNPCVnum); + // END_OF_BUILDING_NPC + + for (int j = 0; j < OBJECT_MATERIAL_MAX_NUM; ++j) + { + if (!r.kMaterials[j].dwItemVnum) + break; + + if (NULL == ITEM_MANAGER::instance().GetTable(r.kMaterials[j].dwItemVnum)) + { + sys_err(" mat: ERROR!! no item by vnum %u", r.kMaterials[j].dwItemVnum); + return false; + } + + sys_log(0, " mat: %u %u", r.kMaterials[j].dwItemVnum, r.kMaterials[j].dwCount); + } + + m_map_pkObjectProto.emplace(r.dwVnum, &m_vec_kObjectProto[i]); + } + + return true; +} + +TObjectProto * CManager::GetObjectProto(DWORD dwVnum) +{ + itertype(m_map_pkObjectProto) it = m_map_pkObjectProto.find(dwVnum); + + if (it == m_map_pkObjectProto.end()) + return NULL; + + return it->second; +} + +bool CManager::LoadLand(TLand * pTable) // from DB +{ + CLand * pkLand = M2_NEW CLand(pTable); + m_map_pkLand.emplace(pkLand->GetID(), pkLand); + + sys_log(0, "LAND: %u map %d %dx%d w %u h %u", + pTable->dwID, pTable->lMapIndex, pTable->x, pTable->y, pTable->width, pTable->height); + + return true; +} + +void CManager::UpdateLand(TLand * pTable) +{ + CLand * pkLand = FindLand(pTable->dwID); + + if (!pkLand) + { + sys_err("cannot find land by id %u", pTable->dwID); + return; + } + + pkLand->PutData(pTable); + + const DESC_MANAGER::DESC_SET & cont = DESC_MANAGER::instance().GetClientSet(); + + itertype(cont) it = cont.begin(); + + TPacketGCLandList p; + + p.header = HEADER_GC_LAND_LIST; + p.size = sizeof(TPacketGCLandList) + sizeof(TLandPacketElement); + + TLandPacketElement e; + + e.dwID = pTable->dwID; + e.x = pTable->x; + e.y = pTable->y; + e.width = pTable->width; + e.height = pTable->height; + e.dwGuildID = pTable->dwGuildID; + + sys_log(0, "BUILDING: UpdateLand %u pos %dx%d guild %u", e.dwID, e.x, e.y, e.dwGuildID); + + CGuild *guild = CGuildManager::instance().FindGuild(pTable->dwGuildID); + while (it != cont.end()) + { + LPDESC d = *(it++); + + if (d->GetCharacter() && d->GetCharacter()->GetMapIndex() == pTable->lMapIndex) + { + // we must send the guild name first + d->GetCharacter()->SendGuildName(guild); + + d->BufferedPacket(&p, sizeof(TPacketGCLandList)); + d->Packet(&e, sizeof(TLandPacketElement)); + } + } +} + +CLand * CManager::FindLand(DWORD dwID) +{ + std::map::iterator it = m_map_pkLand.find(dwID); + + if (it == m_map_pkLand.end()) + return NULL; + + return it->second; +} + +CLand * CManager::FindLand(long lMapIndex, long x, long y) +{ + sys_log(0, "BUILDING: FindLand %d %d %d", lMapIndex, x, y); + + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex); + + if (!r) + return NULL; + + x -= r->sx; + y -= r->sy; + + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + const TLand & r = pkLand->GetData(); + + if (r.lMapIndex != lMapIndex) + continue; + + if (x < r.x || y < r.y) + continue; + + if (x > r.x + r.width || y > r.y + r.height) + continue; + + return pkLand; + } + + return NULL; +} + +CLand * CManager::FindLandByGuild(DWORD GID) +{ + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + + if (pkLand->GetData().dwGuildID == GID) + return pkLand; + } + + return NULL; +} + +bool CManager::LoadObject(TObject * pTable, bool isBoot) // from DB +{ + if (!map_allow_find(pTable->lMapIndex)) // @fixme315 + return false; + + CLand * pkLand = FindLand(pTable->dwLandID); + if (!pkLand) + { + sys_log(0, "Cannot find land by id %u", pTable->dwLandID); + return false; + } + + TObjectProto * pkProto = GetObjectProto(pTable->dwVnum); + + if (!pkProto) + { + sys_err("Cannot find object %u in prototype (id %u)", pTable->dwVnum, pTable->dwID); + return false; + } + + sys_log(0, "OBJ: id %u vnum %u map %d pos %dx%d", pTable->dwID, pTable->dwVnum, pTable->lMapIndex, pTable->x, pTable->y); + + LPOBJECT pkObj = M2_NEW CObject(pTable, pkProto); + + DWORD dwVID = CHARACTER_MANAGER::instance().AllocVID(); + pkObj->SetVID(dwVID); + + m_map_pkObjByVID.emplace(dwVID, pkObj); + m_map_pkObjByID.emplace(pTable->dwID, pkObj); + + pkLand->InsertObject(pkObj); + + if (!isBoot) + pkObj->Show(pTable->lMapIndex, pTable->x, pTable->y); + else + { + pkObj->SetMapIndex(pTable->lMapIndex); + pkObj->SetXYZ(pTable->x, pTable->y, 0); + } + + // BUILDING_NPC + if (!isBoot) + { + if (pkProto->dwNPCVnum) + pkObj->RegenNPC(); + + pkObj->ApplySpecialEffect(); + } + // END_OF_BUILDING_NPC + + return true; +} + +void CManager::FinalizeBoot() +{ + itertype(m_map_pkObjByID) it = m_map_pkObjByID.begin(); + + while (it != m_map_pkObjByID.end()) + { + LPOBJECT pkObj = (it++)->second; + + pkObj->Show(pkObj->GetMapIndex(), pkObj->GetX(), pkObj->GetY()); + // BUILDING_NPC + pkObj->RegenNPC(); + pkObj->ApplySpecialEffect(); + // END_OF_BUILDING_NPC + } + + // BUILDING_NPC + sys_log(0, "FinalizeBoot"); + // END_OF_BUILDING_NPC + + itertype(m_map_pkLand) it2 = m_map_pkLand.begin(); + + while (it2 != m_map_pkLand.end()) + { + CLand * pkLand = (it2++)->second; + + const TLand & r = pkLand->GetData(); + + // LAND_MASTER_LOG + sys_log(0, "LandMaster map_index=%d pos=(%d, %d)", r.lMapIndex, r.x, r.y); + // END_OF_LAND_MASTER_LOG + + if (r.dwGuildID != 0) + continue; + + if (!map_allow_find(r.lMapIndex)) + continue; + + const TMapRegion * region = SECTREE_MANAGER::instance().GetMapRegion(r.lMapIndex); + if (!region) + continue; + + CHARACTER_MANAGER::instance().SpawnMob(20040, r.lMapIndex, region->sx + r.x + (r.width / 2), region->sy + r.y + (r.height / 2), 0); + } +} + +void CManager::DeleteObject(DWORD dwID) // from DB +{ + sys_log(0, "OBJ_DEL: %u", dwID); + + itertype(m_map_pkObjByID) it = m_map_pkObjByID.find(dwID); + + if (it == m_map_pkObjByID.end()) + return; + + it->second->GetLand()->DeleteObject(dwID); +} + +LPOBJECT CManager::FindObjectByVID(DWORD dwVID) +{ + itertype(m_map_pkObjByVID) it = m_map_pkObjByVID.find(dwVID); + + if (it == m_map_pkObjByVID.end()) + return NULL; + + return it->second; +} + +void CManager::UnregisterObject(LPOBJECT pkObj) +{ + m_map_pkObjByID.erase(pkObj->GetID()); + m_map_pkObjByVID.erase(pkObj->GetVID()); +} + +void CManager::SendLandList(LPDESC d, long lMapIndex) +{ + TLandPacketElement e; + + TEMP_BUFFER buf; + + WORD wCount = 0; + + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + const TLand & r = pkLand->GetData(); + + if (r.lMapIndex != lMapIndex) + continue; + + LPCHARACTER ch = d->GetCharacter(); + if (ch) + { + CGuild *guild = CGuildManager::instance().FindGuild(r.dwGuildID); + ch->SendGuildName(guild); + } + + e.dwID = r.dwID; + e.x = r.x; + e.y = r.y; + e.width = r.width; + e.height = r.height; + e.dwGuildID = r.dwGuildID; + + buf.write(&e, sizeof(TLandPacketElement)); + ++wCount; + } + + sys_log(0, "SendLandList map %d count %u elem_size: %d", lMapIndex, wCount, buf.size()); + + if (wCount != 0) + { + TPacketGCLandList p; + + p.header = HEADER_GC_LAND_LIST; + p.size = sizeof(TPacketGCLandList) + buf.size(); + + d->BufferedPacket(&p, sizeof(TPacketGCLandList)); + d->Packet(buf.read_peek(), buf.size()); + } +} + +// LAND_CLEAR +void CManager::ClearLand(DWORD dwLandID) +{ + CLand* pLand = FindLand(dwLandID); + + if ( pLand == NULL ) + { + sys_log(0, "LAND_CLEAR: there is no LAND id like %d", dwLandID); + return; + } + + pLand->ClearLand(); + + sys_log(0, "LAND_CLEAR: request Land Clear. LandID: %d", pLand->GetID()); +} + +void CManager::ClearLandByGuildID(DWORD dwGuildID) +{ + CLand* pLand = FindLandByGuild(dwGuildID); + + if ( pLand == NULL ) + { + sys_log(0, "LAND_CLEAR: there is no GUILD id like %d", dwGuildID); + return; + } + + pLand->ClearLand(); + + sys_log(0, "LAND_CLEAR: request Land Clear. LandID: %d", pLand->GetID()); +} + +void CLand::ClearLand() +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + while ( iter != m_map_pkObject.end() ) + { + RequestDeleteObject(iter->second->GetID()); + iter++; + } + + SetOwner(0); + + const TLand & r = GetData(); + const TMapRegion * region = SECTREE_MANAGER::instance().GetMapRegion(r.lMapIndex); + + CHARACTER_MANAGER::instance().SpawnMob(20040, r.lMapIndex, region->sx + r.x + (r.width / 2), region->sy + r.y + (r.height / 2), 0); +} +// END_LAND_CLEAR + +// BUILD_WALL +void CLand::DrawWall(DWORD dwVnum, long nMapIndex, long& x, long& y, char length, float zRot) +{ + int rot = (int)zRot; + rot = ((rot%360) / 90) * 90; + + int dx=0, dy=0; + + switch ( rot ) + { + case 0 : + dx = -500; + dy = 0; + break; + + case 90 : + dx = 0; + dy = 500; + break; + + case 180 : + dx = 500; + dy = 0; + break; + + case 270 : + dx = 0; + dy = -500; + break; + } + + for ( int i=0; i < length; i++ ) + { + this->RequestCreateObject(dwVnum, nMapIndex, x, y, 0, 0, rot, false); + x += dx; + y += dy; + } +} + +bool CLand::RequestCreateWall(long nMapIndex, float rot) +{ + const bool WALL_ANOTHER_CHECKING_ENABLE = false; + + const TLand& land = GetData(); + + int center_x = land.x + land.width / 2; + int center_y = land.y + land.height / 2; + + int wall_x = center_x; + int wall_y = center_y; + int wall_half_w = 1000; + int wall_half_h = 1362; + + if (rot == 0.0f) + { + int door_x = wall_x; + int door_y = wall_y + wall_half_h; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x, wall_y + wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x, wall_y - wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x - wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x + wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 180.0f) + { + int door_x = wall_x; + int door_y = wall_y - wall_half_h; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x, wall_y - wall_half_h, door_x, door_y, 180.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x, wall_y + wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x - wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x + wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 90.0f) + { + int door_x = wall_x + wall_half_h; + int door_y = wall_y; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x + wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x - wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x, wall_y - wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x, wall_y + wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 270.0f) + { + int door_x = wall_x - wall_half_h; + int door_y = wall_y; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x - wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x + wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x, wall_y - wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x, wall_y + wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + + if (test_server) + { + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + 50, land.y + 50, 0, 0, 0.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + land.width - 50, land.y + 50, 0, 0, 90.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + land.width - 50, land.y + land.height - 50, 0, 0, 180.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + 50, land.y + land.height - 50, 0, 0, 270.0, WALL_ANOTHER_CHECKING_ENABLE); + } + return true; +} + +void CLand::RequestDeleteWall() +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + while (iter != m_map_pkObject.end()) + { + unsigned id = iter->second->GetID(); + unsigned vnum = iter->second->GetVnum(); + + switch (vnum) + { + case WALL_DOOR_VNUM: + case WALL_BACK_VNUM: + case WALL_LEFT_VNUM: + case WALL_RIGHT_VNUM: + RequestDeleteObject(id); + break; + } + + if (test_server) + { + if (FLAG_VNUM == vnum) + RequestDeleteObject(id); + + } + + iter++; + } +} + +bool CLand::RequestCreateWallBlocks(DWORD dwVnum, long nMapIndex, char wallSize, bool doorEast, bool doorWest, bool doorSouth, bool doorNorth) +{ + const TLand & r = GetData(); + + long startX = r.x + (r.width / 2) - (1300 + wallSize*500); + long startY = r.y + (r.height / 2) + (1300 + wallSize*500); + + DWORD corner = dwVnum - 4; + DWORD wall = dwVnum - 3; + DWORD door = dwVnum - 1; + + bool checkAnother = false; + long* ptr = NULL; + int delta = 1; + int rot = 270; + + bool doorOpen[4]; + doorOpen[0] = doorWest; + doorOpen[1] = doorNorth; + doorOpen[2] = doorEast; + doorOpen[3] = doorSouth; + + if ( wallSize > 3 ) wallSize = 3; + else if ( wallSize < 0 ) wallSize = 0; + + for ( int i=0; i < 4; i++, rot -= 90 ) + { + switch ( i ) + { + case 0 : + delta = -1; + ptr = &startY; + break; + case 1 : + delta = 1; + ptr = &startX; + break; + case 2 : + ptr = &startY; + delta = 1; + break; + case 3 : + ptr = &startX; + delta = -1; + break; + } + + this->RequestCreateObject(corner, nMapIndex, startX, startY, 0, 0, rot, checkAnother); + + *ptr = *ptr + ( 700 * delta ); + + if ( doorOpen[i] ) + { + this->DrawWall(wall, nMapIndex, startX, startY, wallSize, rot); + + *ptr = *ptr + ( 700 * delta ); + + this->RequestCreateObject(door, nMapIndex, startX, startY, 0, 0, rot, checkAnother); + + *ptr = *ptr + ( 1300 * delta ); + + this->DrawWall(wall, nMapIndex, startX, startY, wallSize, rot); + } + else + { + this->DrawWall(wall, nMapIndex, startX, startY, wallSize*2 + 4, rot); + } + + *ptr = *ptr + ( 100 * delta ); + } + + return true; +} + +void CLand::RequestDeleteWallBlocks(DWORD dwID) +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + DWORD corner = dwID - 4; + DWORD wall = dwID - 3; + DWORD door = dwID - 1; + DWORD dwVnum = 0; + + while ( iter != m_map_pkObject.end() ) + { + dwVnum = iter->second->GetVnum(); + + if ( dwVnum == corner || dwVnum == wall || dwVnum == door ) + { + RequestDeleteObject(iter->second->GetID()); + } + iter++; + } +} +// END_BUILD_WALL +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/building.h b/source-server/Srcs/Server/game/src/building.h new file mode 100644 index 000000000..9568cbccb --- /dev/null +++ b/source-server/Srcs/Server/game/src/building.h @@ -0,0 +1,153 @@ +#ifndef __INC_METIN_II_BUILDING_H__ +#define __INC_METIN_II_BUILDING_H__ + +#include "../../common/building.h" + +namespace building +{ + class CLand; + + class CObject : public CEntity + { + public: + CObject(TObject * pData, TObjectProto * pProto); + virtual ~CObject(); + + void Destroy(); + + virtual void EncodeInsertPacket(LPENTITY entity); + virtual void EncodeRemovePacket(LPENTITY entity); + + DWORD GetID() { return m_data.dwID; } + + void SetVID(DWORD dwVID); + DWORD GetVID() { return m_dwVID; } + + bool Show(long lMapIndex, long x, long y); + + void Save(); + + void SetLand(CLand * pkLand) { m_pkLand = pkLand; } + CLand * GetLand() { return m_pkLand; } + + DWORD GetVnum() { return m_pProto ? m_pProto->dwVnum : 0; } + DWORD GetGroup() { return m_pProto ? m_pProto->dwGroupVnum : 0; } + + void RegenNPC(); + + // BUILDING_NPC + void ApplySpecialEffect(); + void RemoveSpecialEffect(); + + void Reconstruct(DWORD dwVnum); + + LPCHARACTER GetNPC() { return m_chNPC; } + // END_OF_BUILDING_NPC + + protected: + TObjectProto * m_pProto; + TObject m_data; + DWORD m_dwVID; + CLand * m_pkLand; + + LPCHARACTER m_chNPC; + }; + + class CLand + { + public: + CLand(TLand * pData); + ~CLand(); + + void Destroy(); + + const TLand & GetData(); + void PutData(const TLand * data); + + DWORD GetID() const { return m_data.dwID; } + void SetOwner(DWORD dwGID); + DWORD GetOwner() const { return m_data.dwGuildID; } + + void InsertObject(LPOBJECT pkObj); + LPOBJECT FindObject(DWORD dwID); + LPOBJECT FindObjectByVID(DWORD dwVID); + LPOBJECT FindObjectByVnum(DWORD dwVnum); + LPOBJECT FindObjectByGroup(DWORD dwGroupVnum); + LPOBJECT FindObjectByNPC(LPCHARACTER npc); + void DeleteObject(DWORD dwID); + + bool RequestCreateObject(DWORD dwVnum, long lMapIndex, long x, long y, float xRot, float yRot, float zRot, bool checkAnother); + void RequestDeleteObject(DWORD dwID); + void RequestDeleteObjectByVID(DWORD dwVID); + + void RequestUpdate(DWORD dwGuild); + + // LAND_CLEAR + void ClearLand(); + // END_LAND_CLEAR + + // BUILD_WALL + bool RequestCreateWall(long nMapIndex, float rot); + void RequestDeleteWall(); + + bool RequestCreateWallBlocks(DWORD dwVnum, long nMapIndex, char wallSize, bool doorEast, bool doorWest, bool doorSouth, bool doorNorth); + void RequestDeleteWallBlocks(DWORD dwVnum); + // END_BUILD_WALL + + DWORD GetMapIndex() { return m_data.lMapIndex; } + + protected: + TLand m_data; + std::map m_map_pkObject; + std::map m_map_pkObjectByVID; + + // BUILD_WALL + private : + void DrawWall(DWORD dwVnum, long nMapIndex, long& centerX, long& centerY, char length, float zRot); + // END_BUILD_WALL + }; + + class CManager : public singleton + { + public: + CManager(); + virtual ~CManager(); + + void Destroy(); + + void FinalizeBoot(); + + bool LoadObjectProto(const TObjectProto * pProto, int size); + TObjectProto * GetObjectProto(DWORD dwVnum); + + bool LoadLand(TLand * pTable); + CLand * FindLand(DWORD dwID); + CLand * FindLand(long lMapIndex, long x, long y); + CLand * FindLandByGuild(DWORD GID); + void UpdateLand(TLand * pTable); + + bool LoadObject(TObject * pTable, bool isBoot=false); + void DeleteObject(DWORD dwID); + void UnregisterObject(LPOBJECT pkObj); + + LPOBJECT FindObjectByVID(DWORD dwVID); + + void SendLandList(LPDESC d, long lMapIndex); + + // LAND_CLEAR + void ClearLand(DWORD dwLandID); + void ClearLandByGuildID(DWORD dwGuildID); + // END_LAND_CLEAR + + protected: + std::vector m_vec_kObjectProto; + std::map m_map_pkObjectProto; + + std::map m_map_pkLand; + std::map m_map_pkObjByID; + std::map m_map_pkObjByVID; + }; +} + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/castle.cpp b/source-server/Srcs/Server/game/src/castle.cpp new file mode 100644 index 000000000..199d69260 --- /dev/null +++ b/source-server/Srcs/Server/game/src/castle.cpp @@ -0,0 +1,1029 @@ +#define _castle_cpp_ + +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "char_manager.h" +#include "castle.h" +#include "start_position.h" +#include "monarch.h" +#include "questlua.h" +#include "log.h" +#include "char.h" +#include "sectree_manager.h" + +#define EMPIRE_NONE 0 +#define EMPIRE_RED 1 +#define EMPIRE_YELLOW 2 +#define EMPIRE_BLUE 3 + +#define SIEGE_EVENT_PULSE PASSES_PER_SEC(60*5) + +#define GET_CAHR_MANAGER() CHARACTER_MANAGER::instance() +#define GET_CASTLE(empire) (s_castle+(empire)) +#define GET_GUARD(empire, region_index, guard_index) (GET_CASTLE(empire)->guard[region_index][guard_index]) +#define GET_GUARD_REGION(empire, region_index) (s_guard_region[empire][region_index]) +#define GET_GUARD_GROUP(empire, region_index, guard_index) (GET_CASTLE(empire)->guard_group[region_index][guard_index]) +#define GET_FROG(empire, index) (GET_CASTLE(empire)->frog[index]) +#define GET_FROG_POS(empire, index) (s_frog_pos[empire][index]) +#define GET_TOWER(empire, index) (GET_CASTLE(empire)->tower[index]) + +#define DO_ALL_EMPIRE(empire) for (int empire = 1; empire < 4; ++empire) +#define DO_ALL_TOWER(i) for (int i = 0; i < MAX_CASTLE_TOWER; ++i) +#define DO_ALL_FROG(i) for (int i = 0; i < MAX_CASTLE_FROG; ++i) + +#define GET_SIEGE_STATE() s_siege_state +#define GET_SIEGE_EMPIRE() s_sige_empire +#define GET_SIEGE_EVENT(empire) (GET_CASTLE(empire)->siege_event) +#define GET_STONE_EVENT(empire) (GET_CASTLE(empire)->stone_event) + +#define GET_TOWER_REGION(empire) s_tower_region[empire] +#define GET_STONE_REGION(empire) s_tower_region[empire] + +static CASTLE_DATA *s_castle = NULL; +static CASTLE_STATE s_siege_state = CASTLE_SIEGE_NONE; +static int s_sige_empire = EMPIRE_NONE; + +struct POSITION +{ + int x, y; +}; + +static POSITION s_frog_pos[4][MAX_CASTLE_FROG] = { + // EMPIRE_NONE + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + // EMPIRE_RED + { + { 225, 45 }, + { 231, 45 }, + { 237, 45 }, + { 243, 45 }, + { 249, 45 }, + + { 225, 50 }, + { 231, 50 }, + { 237, 50 }, + { 243, 50 }, + { 249, 50 }, + + { 261, 45 }, + { 267, 45 }, + { 273, 45 }, + { 279, 45 }, + { 285, 45 }, + + { 261, 50 }, + { 267, 50 }, + { 273, 50 }, + { 279, 50 }, + { 285, 50 } + }, + // EMPIRE_YELLOW + { + { 221, 36 }, + { 227, 36 }, + { 233, 36 }, + { 239, 36 }, + { 245, 36 }, + + { 269, 36 }, + { 275, 36 }, + { 281, 36 }, + { 287, 36 }, + { 293, 36 }, + + { 221, 41 }, + { 227, 41 }, + { 233, 41 }, + { 239, 41 }, + { 245, 41 }, + + { 269, 41 }, + { 275, 41 }, + { 281, 41 }, + { 287, 41 }, + { 293, 41 } + }, + // EMPIRE_BLUE + { + { 225, 45 }, + { 231, 45 }, + { 237, 45 }, + { 243, 45 }, + { 249, 45 }, + + { 225, 50 }, + { 231, 50 }, + { 237, 50 }, + { 243, 50 }, + { 249, 50 }, + + { 261, 45 }, + { 267, 45 }, + { 273, 45 }, + { 279, 45 }, + { 285, 45 }, + + { 261, 50 }, + { 267, 50 }, + { 273, 50 }, + { 279, 50 }, + { 285, 50 } + } +}; + +struct GUARD_REGION +{ + int sx, sy, ex, ey; +}; + +static GUARD_REGION s_guard_region[4][4] = { + // NULL_EMPIRE + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 } + }, + // EMPIRE_RED + { + { 74, 170, 96, 180 }, + { 237, 135, 270, 146 }, + { 235, 260, 278, 273 }, + { 421, 167, 435, 205 } + }, + // EMPIRE_YELLOW + { + { 109, 172, 128, 202 }, + { 237, 140, 282, 153 }, + { 232, 261, 279, 276 }, + { 390, 173, 403, 205 }, + }, + // EMPIRE_BLUE + { + { 74, 170, 96, 120 }, + { 237, 135, 270, 146 }, + { 235, 260, 278, 273 }, + { 421, 167, 435, 205 } + } +}; + +static GUARD_REGION s_tower_region[4] = { + // NULL_EMPIRE + { 0, 0, 0, 0 }, + // EMPIRE_RED + { 85, 135, 420, 265 }, + // EMPIRE_YELLOW + { 120, 130, 405, 276 }, + // EMPIRE_BLUE + { 85, 135, 420, 265 } +}; + +static long FN_castle_map_index(int empire); + +EVENTINFO(castle_event_info) +{ + int empire; + int pulse; + + castle_event_info() + : empire( 0 ) + , pulse( 0 ) + { + } +}; + +EVENTFUNC(castle_siege_event) +{ + char buf[1024] = {0}; + struct castle_event_info *info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "castle_siege_event> Null pointer" ); + return 0; + } + + info->pulse += SIEGE_EVENT_PULSE; + + if (info->pulse < PASSES_PER_SEC(30*60)) + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ѷΰ Դϴ."), + EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + return SIEGE_EVENT_PULSE; + } + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + break; + + case CASTLE_SIEGE_STRUGGLE: + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ߽ϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + snprintf(buf, sizeof(buf), LC_TEXT("ݺ %s 30а ȭ ıϿ ȹ ֽϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + GET_SIEGE_STATE() = CASTLE_SIEGE_END; + + return PASSES_PER_SEC(60*30); + } + break; + case CASTLE_SIEGE_END: + BroadcastNotice(LC_TEXT("30 ߽ϴ.. ȭ ϴ.")); + castle_end_siege(); + break; + } + return 0; +} + +static DWORD FN_random_stone() +{ + DWORD vnum[7] = { + 8012, + 8013, + 8014, + 8024, + 8025, + 8026, + 8027 + }; + + int index = number(0, 6); + + return vnum[index]; +} + +EVENTINFO(castle_stone_event_info) +{ + int empire; + int spawn_count; + + castle_stone_event_info() + : empire( 0 ) + , spawn_count( 0 ) + { + } +}; + +EVENTFUNC(castle_stone_event) +{ + struct castle_stone_event_info *info = dynamic_cast( event->info ); + + if (info == NULL) + { + sys_err( "castle_stone_event> Null pointer" ); + return 0; + } + + long map_index = FN_castle_map_index(GET_SIEGE_EMPIRE()); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + + if (NULL == sectree_map) + return 0; + + const int SPAWN_COUNT = 15; + + if (info->spawn_count < (SPAWN_COUNT * 2)) + { + for (int i = 0; i < SPAWN_COUNT; ++i) + { + DWORD sx = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(info->empire).sx; + DWORD sy = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(info->empire).sy; + DWORD ex = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(info->empire).ex; + DWORD ey = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(info->empire).ey; + + CHARACTER_MANAGER::instance().SpawnMobRange(FN_random_stone(), + FN_castle_map_index(info->empire), + sx, sy, ex, ey); + } + + info->spawn_count += SPAWN_COUNT; + + if (info->spawn_count < (SPAWN_COUNT * 2)) + return PASSES_PER_SEC(30 * 60); + else + return 0; + } + + return 0; +} + +LPCHARACTER castle_spawn_frog_force(int empire, int empty_index); + +static long FN_castle_map_index(int empire) +{ + switch (empire) + { + case EMPIRE_RED: return 181; + case EMPIRE_YELLOW: return 183; + case EMPIRE_BLUE: return 182; + } + return 0; +} + +static int FN_empty_frog_index(int empire) +{ + DO_ALL_FROG(i) + { + if (NULL == GET_FROG(empire, i)) + return i; + } + return (-1); +} + +static POSITION* FN_empty_frog_pos(int empire) +{ + int frog_index = FN_empty_frog_index(empire); + + if (frog_index < 0) + return NULL; + + switch (empire) + { + case EMPIRE_RED: + case EMPIRE_YELLOW: + case EMPIRE_BLUE: + return &GET_FROG_POS(empire, frog_index); + } + + return NULL; +} + +static int FN_empty_guard_pos(int empire, int region_index) +{ + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if (NULL == GET_GUARD(empire, region_index, i)) + { + return i; + } + } + + return -1; +} + +static bool FN_is_castle_map(int map_index) +{ + switch (map_index) + { + case 181: + case 182: + case 183: + return true; + } + return false; +} + +bool castle_boot() +{ + FILE *fp; + char one_line[256]; + const char *delim = " \t\r\n"; + char *v; + int empire = 0; + int guard_region = 0; + + CREATE(s_castle, CASTLE_DATA, 4); + + const char *castle_file = "castle_data.txt"; + + if ((fp = fopen(castle_file, "r")) == 0) + return false; + + while (fgets(one_line, 256, fp)) + { + int value = 0; + + if (one_line[0] == '#') + continue; + + const char* token_string = strtok(one_line, delim); + + if (NULL == token_string) + continue; + + TOKEN("section") + { + continue; + } + else TOKEN("empire") + { + v = strtok(NULL, delim); + if (v) + { + str_to_number(empire, v); + } + else + { + fclose(fp); + sys_err("wrong empire number is null"); + return false; + } + } + else TOKEN("frog") + { + int pos = 0; + + while ((v = strtok(NULL, delim))) + { + str_to_number(value, v); + if (value) + { + castle_spawn_frog_force(empire, pos); + } + ++pos; + } + } + else TOKEN("guard") + { + int group_vnum = 0; + + while ((v = strtok(NULL, delim))) + { + str_to_number(group_vnum, v); + if (group_vnum) + { + castle_spawn_guard(empire, group_vnum, guard_region); + } + } + + ++guard_region; + } + else TOKEN("end") + { + guard_region = 0; + } + } + + fclose(fp); + + return true; +} + +void castle_save() +{ + if (NULL == s_castle) + return; + + const char *castle_file = "castle_data.txt"; + FILE *fp; + + fp = fopen(castle_file, "w"); + + if (NULL == fp) + { + sys_err(" fopen(%s)", castle_file); + return; + } + + // write castle data + DO_ALL_EMPIRE(empire) + { + fprintf(fp, "section\n"); + + // write empire + fprintf(fp, "\tempire %d\n", empire); + + // write frog + fprintf(fp, "\tfrog "); + for (int i = 0; i < MAX_CASTLE_FROG; ++i) + { + fprintf(fp, " %d", GET_FROG(empire, i) ? 1 : 0); + } + fprintf(fp, "\n"); + + // write guard group + for (int region_index = 0; region_index < MAX_CASTLE_GUARD_REGION; ++region_index) + { + fprintf(fp, "\tguard "); + for (int guard_index = 0; guard_index < MAX_CASTLE_GUARD_PER_REGION; ++guard_index) + { + fprintf(fp, " %u", GET_GUARD_GROUP(empire, region_index, guard_index)); + } + fprintf(fp, "\n"); + } + fprintf(fp, "end\n"); + } + + fclose(fp); +} + +int castle_siege(int empire, int tower_count) +{ + // check siege map + { + if (NULL == SECTREE_MANAGER::instance().GetMap(181)) return 0; + if (NULL == SECTREE_MANAGER::instance().GetMap(182)) return 0; + if (NULL == SECTREE_MANAGER::instance().GetMap(183)) return 0; + } + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + castle_start_siege(empire, tower_count); + return 1; + break; + + case CASTLE_SIEGE_STRUGGLE: + case CASTLE_SIEGE_END: + castle_end_siege(); + return 2; + break; + } + + return 0; +} + +void castle_start_siege(int empire, int tower_count) +{ + if (CASTLE_SIEGE_NONE != GET_SIEGE_STATE()) + return; + + GET_SIEGE_STATE() = CASTLE_SIEGE_STRUGGLE; + GET_SIEGE_EMPIRE() = empire; + + castle_spawn_tower(empire, tower_count); + + { + castle_event_info* info = AllocEventInfo(); + + info->empire = empire; + info->pulse = 0; + + GET_SIEGE_EVENT(empire) = event_create(castle_siege_event, info, SIEGE_EVENT_PULSE); + } + + { + castle_stone_event_info* info = AllocEventInfo(); + + info->spawn_count = 0; + info->empire = empire; + + GET_STONE_EVENT(empire) = event_create(castle_stone_event, info, PASSES_PER_SEC(1)); + } +} + +void castle_end_siege() +{ + GET_SIEGE_EMPIRE() = EMPIRE_NONE; + GET_SIEGE_STATE() = CASTLE_SIEGE_NONE; + + DO_ALL_EMPIRE(empire) + { + if (GET_SIEGE_EVENT(empire)) + { + event_cancel(&GET_SIEGE_EVENT(empire)); + } + + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + { + LPCHARACTER npc = GET_TOWER(empire, i); + M2_DESTROY_CHARACTER(npc); + GET_TOWER(empire, i) = NULL; + } + } + } +} + +LPCHARACTER castle_spawn_frog(int empire) +{ + int dir = 1; + long map_index = FN_castle_map_index(empire); + + POSITION *empty_pos = FN_empty_frog_pos(empire); + if (NULL == empty_pos) + return NULL; + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return NULL; + DWORD x = sectree_map->m_setting.iBaseX + 100*empty_pos->x; + DWORD y = sectree_map->m_setting.iBaseY + 100*empty_pos->y; + + LPCHARACTER frog = CHARACTER_MANAGER::instance().SpawnMob(CASTLE_FROG_VNUM, map_index, + x, y, 0 , + false, dir); + if (frog) + { + frog->SetEmpire(empire); + int empty_index = FN_empty_frog_index(empire); + GET_FROG(empire, empty_index) = frog; + return frog; + } + return NULL; +} + +LPCHARACTER castle_spawn_frog_force(int empire, int empty_index) +{ + int dir = 1; + long map_index = FN_castle_map_index(empire); + + POSITION *empty_pos = &GET_FROG_POS(empire, empty_index); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + { + return NULL; + } + DWORD x = sectree_map->m_setting.iBaseX + 100*empty_pos->x; + DWORD y = sectree_map->m_setting.iBaseY + 100*empty_pos->y; + + LPCHARACTER frog = CHARACTER_MANAGER::instance().SpawnMob(CASTLE_FROG_VNUM, map_index, + x, y, 0 , + false, dir); + if (frog) + { + frog->SetEmpire(empire); + GET_FROG(empire, empty_index) = frog; + return frog; + } + return NULL; +} + +LPCHARACTER castle_spawn_guard(int empire, DWORD group_vnum, int region_index) +{ + LPCHARACTER mob; + int sx, sy, ex, ey; + long map_index = FN_castle_map_index(empire); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return NULL; + + if (castle_guard_count(empire, region_index) >= MAX_CASTLE_GUARD_PER_REGION) + return NULL; + + sx = sectree_map->m_setting.iBaseX + 100*GET_GUARD_REGION(empire, region_index).sx; + sy = sectree_map->m_setting.iBaseY + 100*GET_GUARD_REGION(empire, region_index).sy; + ex = sectree_map->m_setting.iBaseX + 100*GET_GUARD_REGION(empire, region_index).ex; + ey = sectree_map->m_setting.iBaseY + 100*GET_GUARD_REGION(empire, region_index).ey; + + mob = CHARACTER_MANAGER::instance().SpawnGroup(group_vnum, map_index, + sx, sy, ex, ey); + if (mob) + { + mob->SetEmpire(empire); + + int pos = FN_empty_guard_pos(empire, region_index); + GET_GUARD(empire, region_index, pos) = mob; + GET_GUARD_GROUP(empire, region_index, pos) = group_vnum; + } + + return mob; +} + +static DWORD FN_random_tower() +{ + DWORD vnum[5] = + { + 11506, + 11507, + 11508, + 11509, + 11510 + }; + + int index = number(0, 4); + return vnum[index]; +} + +static void FN_spawn_tower(int empire, LPSECTREE_MAP sectree_map) +{ + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + continue; + + DWORD sx = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(empire).sx; + DWORD sy = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(empire).sy; + DWORD ex = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(empire).ex; + DWORD ey = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(empire).ey; + + GET_TOWER(empire, i) = + CHARACTER_MANAGER::instance().SpawnMobRange(FN_random_tower(), + FN_castle_map_index(empire), + sx, sy, ex, ey); + GET_TOWER(empire, i)->SetEmpire(empire); + return; + } +} + +bool castle_spawn_tower(int empire, int tower_count) +{ + int map_index = FN_castle_map_index(empire); + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return false; + + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + GET_TOWER(empire, i)->Dead(NULL, true); + GET_TOWER(empire, i) = NULL; + } + + int spawn_count = MINMAX(MIN_CASTLE_TOWER, tower_count, MAX_CASTLE_TOWER); + + for (int j = 0; j < spawn_count; ++j) + { + FN_spawn_tower(empire, sectree_map); + } + + // broad cast + { + char buf[1024]; + snprintf(buf, sizeof(buf), LC_TEXT("%s ˸ ȭ Ÿϴ."), EMPIRE_NAME(empire)); + BroadcastNotice(buf); + } + return true; +} + +void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer) +{ + int empire = ch->GetEmpire(); + + for (int region_index = 0; region_index < MAX_CASTLE_GUARD_REGION; ++region_index) + { + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if (GET_GUARD(empire, region_index, i) == ch) + { + GET_GUARD(empire, region_index, i) = NULL; + GET_GUARD_GROUP(empire, region_index, i) = 0; + } + } + } + + castle_save(); +} + +void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer) +{ + if (NULL == ch || NULL == killer) + return; + + int empire = ch->GetEmpire(); + + DO_ALL_FROG(i) + { + if (ch == GET_FROG(empire, i)) + { + GET_FROG(empire, i) = NULL; + + killer->PointChange(POINT_GOLD, 10000000, true); + castle_save(); + return; + } + } +} + +void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer) +{ + char buf[1024] = {0}; + + if (NULL == ch || NULL == killer) + return; + + int killer_empire = killer->GetEmpire(); + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + break; + + case CASTLE_SIEGE_STRUGGLE: + case CASTLE_SIEGE_END: + { + int siege_end = true; + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ı߽ϴ."), EMPIRE_NAME(killer_empire)); + BroadcastNotice(buf); + + LogManager::instance().CharLog(killer, 0, "CASTLE_TORCH_KILL", ""); + + DO_ALL_TOWER(i) + { + if (ch == GET_TOWER(GET_SIEGE_EMPIRE(), i)) + GET_TOWER(GET_SIEGE_EMPIRE(), i) = NULL; + } + + DO_ALL_TOWER(i) + { + if (GET_TOWER(GET_SIEGE_EMPIRE(), i)) + siege_end = false; + } + + if (siege_end) + { + if (GET_SIEGE_STATE() == CASTLE_SIEGE_STRUGGLE) + { + snprintf(buf, sizeof(buf), LC_TEXT("%s Ͽ £ йϿϴ.."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + } + else + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ıϿϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + } + castle_end_siege(); + } + } + break; + } +} + +int castle_guard_count(int empire, int region_index) +{ + int count = 0; + + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if ( GET_GUARD(empire, region_index, i) ) + ++count; + } + return count; +} + +int castle_frog_count(int empire) +{ + int count = 0; + DO_ALL_FROG(i) + { + if (GET_FROG(empire, i)) + ++count; + } + return count; +} + +bool castle_is_guard_vnum(DWORD vnum) +{ + switch (vnum) + { + case 11112: + case 11114: + case 11116: + case 11106: + case 11108: + case 11110: + case 11100: + case 11102: + case 11104: + case 11113: + case 11115: + case 11117: + case 11107: + case 11109: + case 11111: + case 11101: + case 11103: + case 11105: + return true; + } + + return false; +} + +int castle_cost_of_hiring_guard(DWORD group_vnum) +{ + switch (group_vnum) + { + case 9501: + case 9511: + case 9521: + + case 9502: + case 9512: + case 9522: + return (100*10000); + + case 9503: + case 9513: + case 9523: + + case 9504: + case 9514: + case 9524: + return (300*10000); + + case 9505: + case 9515: + case 9525: + + case 9506: + case 9516: + case 9526: + return (1000*10000); + } + + return 0; +} + +bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim) +{ + if (NULL == ch || NULL == victim) + return false; + + if (false == FN_is_castle_map(ch->GetMapIndex())) + return false; + + if (ch->IsPC() && victim->IsPC()) + return true; + + if (CASTLE_SIEGE_END == GET_SIEGE_STATE()) + { + if (castle_is_tower_vnum(victim->GetRaceNum())) + { + if (ch->GetEmpire() == victim->GetEmpire()) + return true; + else + return false; + } + } + + if (ch->GetEmpire() == victim->GetEmpire()) + return false; + + return true; +} + +bool castle_frog_to_empire_money(LPCHARACTER ch) +{ + if (NULL == ch) + return false; + + int empire = ch->GetEmpire(); + + DO_ALL_FROG(i) + { + if (NULL == GET_FROG(empire, i)) + continue; + + LPCHARACTER npc = GET_FROG(empire, i); + + if (false == CMonarch::instance().SendtoDBAddMoney(CASTLE_FROG_PRICE, empire, ch)) + return false; + + GET_FROG(empire, i) = NULL; + npc->Dead(/*killer*/NULL, /*immediate_dead*/true); + return true; + } + + return false; +} + +bool castle_is_my_castle(int empire, int map_index) +{ + switch (empire) + { + case EMPIRE_RED: return (181 == map_index); + case EMPIRE_YELLOW: return (183 == map_index); + case EMPIRE_BLUE: return (182 == map_index); + } + return false; +} + +bool castle_is_tower_vnum(DWORD vnum) +{ + switch (vnum) + { + case 11506: + case 11507: + case 11508: + case 11509: + case 11510: + return true; + } + return false; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/castle.h b/source-server/Srcs/Server/game/src/castle.h new file mode 100644 index 000000000..10eefe994 --- /dev/null +++ b/source-server/Srcs/Server/game/src/castle.h @@ -0,0 +1,62 @@ +#ifndef _castle_h_ +#define _castle_h_ + +#define MAX_CASTLE_GUARD_REGION 4 +#define MAX_CASTLE_GUARD_PER_REGION 10 +#define MAX_CASTLE_FROG 20 +#define MAX_CASTLE_TOWER 10 +#define MIN_CASTLE_TOWER 5 + +#define CASTLE_FROG_PRICE 100000000 +#define CASTLE_FROG_VNUM 11505 +#define IS_CASTLE_MAP(map) (181==(map)||182==(map)||(183)==(map)) +//#define IS_CASTLE_TOWER(vnum) (11506==(vnum)||11507==(vnum)||11508==(vnum)||11509==(vnum) || 11510==(vnum)) + +enum CASTLE_STATE +{ + CASTLE_SIEGE_NONE, + CASTLE_SIEGE_STRUGGLE, + CASTLE_SIEGE_END +}; + +struct CASTLE_DATA +{ + LPCHARACTER frog[MAX_CASTLE_FROG]; + + LPCHARACTER guard[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; + DWORD guard_group[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; + + LPCHARACTER tower[MAX_CASTLE_TOWER]; + + LPEVENT siege_event; + LPEVENT stone_event; +}; + +/* extern functions */ +bool castle_boot(); +void castle_save(); +int castle_siege(int empire, int tower_count); +void castle_start_siege(int empire, int tower_count); +void castle_end_siege(); + +LPCHARACTER castle_spawn_frog(int empire); +LPCHARACTER castle_spawn_guard(int empire, DWORD group_vnum, int region_index); +bool castle_spawn_tower(int empire, int tower_count); + +void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer); +void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer); +void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer); + +int castle_guard_count(int empire, int region_index); +int castle_frog_count(int empire); + +bool castle_is_guard_vnum(DWORD vnum); +int castle_cost_of_hiring_guard(DWORD vnum); +bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim); + +bool castle_frog_to_empire_money(LPCHARACTER ch); +bool castle_is_my_castle(int empire, int map_index); +bool castle_is_tower_vnum(DWORD vnum); + +#endif /* _castle_h_ */ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char.cpp b/source-server/Srcs/Server/game/src/char.cpp new file mode 100644 index 000000000..74c5c82de --- /dev/null +++ b/source-server/Srcs/Server/game/src/char.cpp @@ -0,0 +1,8189 @@ +#include "stdafx.h" + +#include "../../common/VnumHelper.h" + +#include "char.h" + +#include "config.h" +#include "utils.h" +#include "crc32.h" +#include "char_manager.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "buffer_manager.h" +#include "item_manager.h" +#include "motion.h" +#include "vector.h" +#include "packet.h" +#include "cmd.h" +#include "fishing.h" +#include "exchange.h" +#include "battle.h" +#include "affect.h" +#include "shop.h" +#include "shop_manager.h" +#include "safebox.h" +#include "regen.h" +#include "pvp.h" +#include "party.h" +#include "start_position.h" +#include "questmanager.h" +#include "log.h" +#include "p2p.h" +#include "guild.h" +#include "guild_manager.h" +#include "dungeon.h" +#include "messenger_manager.h" +#include "unique_item.h" +#include "priv_manager.h" +#include "war_map.h" +#include "xmas_event.h" +#include "banword.h" +#include "target.h" +#include "wedding.h" +#include "mob_manager.h" +#include "mining.h" +#include "monarch.h" +#include "castle.h" +#include "arena.h" +#include "horsename_manager.h" +#include "gm.h" +#include "map_location.h" +#include "BlueDragon_Binder.h" +#include "skill_power.h" +#include "buff_on_attributes.h" + +#ifdef __PET_SYSTEM__ +#include "PetSystem.h" +#endif +#include "DragonSoul.h" +#include "../../common/CommonDefines.h" + +extern const BYTE g_aBuffOnAttrPoints; +extern bool RaceToJob(unsigned race, unsigned *ret_job); + +extern bool IS_SUMMONABLE_ZONE(int map_index); // char_item.cpp +bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index); + +bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index) +{ + switch (map_index) + { + case 301: + case 302: + case 303: + case 304: + if (ch->GetLevel() < 90) + return false; + } + return true; +} + +#ifdef NEW_ICEDAMAGE_SYSTEM +const DWORD CHARACTER::GetNoDamageRaceFlag() +{ + return m_dwNDRFlag; +} + +void CHARACTER::SetNoDamageRaceFlag(DWORD dwRaceFlag) +{ + if (dwRaceFlag>=MAIN_RACE_MAX_NUM) return; + if (IS_SET(m_dwNDRFlag, 1<=MAIN_RACE_MAX_NUM) return; + if (!IS_SET(m_dwNDRFlag, 1< & CHARACTER::GetNoDamageAffectFlag() +{ + return m_setNDAFlag; +} + +void CHARACTER::SetNoDamageAffectFlag(DWORD dwAffectFlag) +{ + m_setNDAFlag.emplace(dwAffectFlag); +} + +void CHARACTER::UnsetNoDamageAffectFlag(DWORD dwAffectFlag) +{ + m_setNDAFlag.erase(dwAffectFlag); +} + +void CHARACTER::ResetNoDamageAffectFlag() +{ + m_setNDAFlag.clear(); +} +#endif + +// DynamicCharacterPtr member function definitions + +LPCHARACTER DynamicCharacterPtr::Get() const { + LPCHARACTER p = NULL; + if (is_pc) { + p = CHARACTER_MANAGER::instance().FindByPID(id); + } else { + p = CHARACTER_MANAGER::instance().Find(id); + } + return p; +} + +DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) { + if (character == NULL) { + Reset(); + return *this; + } + if (character->IsPC()) { + is_pc = true; + id = character->GetPlayerID(); + } else { + is_pc = false; + id = character->GetVID(); + } + return *this; +} + +CHARACTER::CHARACTER() +{ + m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty); + m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty); + m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty); + + Initialize(); +} + +CHARACTER::~CHARACTER() +{ + Destroy(); +} + +void CHARACTER::Initialize() +{ + CEntity::Initialize(ENTITY_CHARACTER); + + m_bNoOpenedShop = true; + + m_bOpeningSafebox = false; + + m_fSyncTime = get_float_time()-3; + m_dwPlayerID = 0; + m_dwKillerPID = 0; + + m_iMoveCount = 0; + + m_pkRegen = NULL; + regen_id_ = 0; + m_posRegen.x = m_posRegen.y = m_posRegen.z = 0; + m_posStart.x = m_posStart.y = 0; + m_posDest.x = m_posDest.y = 0; + m_fRegenAngle = 0.0f; + + m_pkMobData = NULL; + m_pkMobInst = NULL; + + m_pkShop = NULL; + m_pkChrShopOwner = NULL; + m_pkMyShop = NULL; + m_pkExchange = NULL; + m_pkParty = NULL; + m_pkPartyRequestEvent = NULL; + + m_pGuild = NULL; + + m_pkChrTarget = NULL; + + m_pkMuyeongEvent = NULL; + + m_pkWarpNPCEvent = NULL; + m_pkDeadEvent = NULL; + m_pkStunEvent = NULL; + m_pkSaveEvent = NULL; + m_pkRecoveryEvent = NULL; + m_pkTimedEvent = NULL; + m_pkFishingEvent = NULL; + m_pkWarpEvent = NULL; + + // MINING + m_pkMiningEvent = NULL; + // END_OF_MINING + + m_pkPoisonEvent = NULL; +#ifdef ENABLE_WOLFMAN_CHARACTER + m_pkBleedingEvent = NULL; +#endif + m_pkFireEvent = NULL; + m_pkCheckSpeedHackEvent = NULL; + m_speed_hack_count = 0; + + m_pkAffectEvent = NULL; + m_afAffectFlag = TAffectFlag(0, 0); + + m_pkDestroyWhenIdleEvent = NULL; + + m_pkChrSyncOwner = NULL; + + m_points = {}; + m_pointsInstant = {}; + + m_bCharType = CHAR_TYPE_MONSTER; + + SetPosition(POS_STANDING); + + m_dwPlayStartTime = m_dwLastMoveTime = get_dword_time(); + + GotoState(m_stateIdle); + m_dwStateDuration = 1; + + m_dwLastAttackTime = get_dword_time() - 20000; + + m_bAddChrState = 0; + + m_pkChrStone = NULL; + + m_pkSafebox = NULL; + m_iSafeboxSize = -1; + m_iSafeboxLoadTime = 0; + + m_pkMall = NULL; + m_iMallLoadTime = 0; + + m_posWarp.x = m_posWarp.y = m_posWarp.z = 0; + m_lWarpMapIndex = 0; + + m_posExit.x = m_posExit.y = m_posExit.z = 0; + m_lExitMapIndex = 0; + + m_pSkillLevels = NULL; + + m_dwMoveStartTime = 0; + m_dwMoveDuration = 0; + + m_dwFlyTargetID = 0; + + m_dwNextStatePulse = 0; + + m_dwLastDeadTime = get_dword_time()-180000; + + m_bSkipSave = false; + + m_bItemLoaded = false; + + m_bHasPoisoned = false; +#ifdef ENABLE_WOLFMAN_CHARACTER + m_bHasBled = false; +#endif + m_pkDungeon = NULL; + m_iEventAttr = 0; + + m_kAttackLog.dwVID = 0; + m_kAttackLog.dwTime = 0; + + m_bNowWalking = m_bWalking = false; + ResetChangeAttackPositionTime(); + + m_bDetailLog = false; + m_bMonsterLog = false; + + m_bDisableCooltime = false; + + m_iAlignment = 0; + m_iRealAlignment = 0; + + m_iKillerModePulse = 0; + m_bPKMode = PK_MODE_PEACE; + + m_dwQuestNPCVID = 0; + m_dwQuestByVnum = 0; + m_dwQuestItemVID = 0; +#ifdef ENABLE_QUEST_DND_EVENT + m_dwQuestDNDItemVID = 0; +#endif + + m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000; + + m_bUnderRefine = false; + + // REFINE_NPC + m_dwRefineNPCVID = 0; + // END_OF_REFINE_NPC + + m_dwPolymorphRace = 0; + + m_bStaminaConsume = false; + + ResetChainLightningIndex(); + + m_dwMountVnum = 0; + m_chHorse = NULL; + m_chRider = NULL; + + m_pWarMap = NULL; + m_pWeddingMap = NULL; + m_bChatCounter = 0; + + ResetStopTime(); + + m_dwLastVictimSetTime = get_dword_time() - 3000; + m_iMaxAggro = -100; + + m_bSendHorseLevel = 0; + m_bSendHorseHealthGrade = 0; + m_bSendHorseStaminaGrade = 0; + + m_dwLoginPlayTime = 0; + + m_pkChrMarried = NULL; + + m_posSafeboxOpen.x = -1000; + m_posSafeboxOpen.y = -1000; + + // EQUIP_LAST_SKILL_DELAY + m_dwLastSkillTime = get_dword_time(); + // END_OF_EQUIP_LAST_SKILL_DELAY + + // MOB_SKILL_COOLTIME + memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime)); + // END_OF_MOB_SKILL_COOLTIME + + // ARENA + m_pArena = NULL; + m_nPotionLimit = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count"); + // END_ARENA + + //PREVENT_TRADE_WINDOW + m_isOpenSafebox = 0; + //END_PREVENT_TRADE_WINDOW + + //PREVENT_REFINE_HACK + m_iRefineTime = 0; + //END_PREVENT_REFINE_HACK + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + m_iSeedTime = 0; + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + //PREVENT_PORTAL_AFTER_EXCHANGE + m_iExchangeTime = 0; + //END_PREVENT_PORTAL_AFTER_EXCHANGE + + m_iSafeboxLoadTime = 0; + + m_iMyShopTime = 0; + + InitMC(); + + m_deposit_pulse = 0; + + SET_OVER_TIME(this, OT_NONE); + + m_strNewName = ""; + + m_known_guild.clear(); + + m_dwLogOffInterval = 0; + + m_bComboSequence = 0; + m_dwLastComboTime = 0; + m_bComboIndex = 0; + m_iComboHackCount = 0; + m_dwSkipComboAttackByTime = 0; + + m_dwMountTime = 0; + + m_bIsLoadedAffect = false; + cannot_dead = false; + +#ifdef __PET_SYSTEM__ + m_petSystem = 0; + m_bIsPet = false; +#endif +#ifdef NEW_ICEDAMAGE_SYSTEM + m_dwNDRFlag = 0; + m_setNDAFlag.clear(); +#endif + + m_fAttMul = 1.0f; + m_fDamMul = 1.0f; + + m_pointsInstant.iDragonSoulActiveDeck = -1; + + memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime)); + m_iSyncHackCount = 0; + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + m_bAcceCombination = false; + m_bAcceAbsorption = false; +#endif +} + +void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC) +{ + static int s_crc = 172814; + + char crc_string[128+1]; + snprintf(crc_string, sizeof(crc_string), "%s%p%d", c_pszName, this, ++s_crc); + m_vid = VID(vid, GetCRC32(crc_string, strlen(crc_string))); + + if (isPC) + m_stName = c_pszName; +} + +void CHARACTER::Destroy() +{ + CloseMyShop(); + + if (m_pkRegen) + { + if (m_pkDungeon) { + // Dungeon regen may not be valid at this point + if (m_pkDungeon->IsValidRegen(m_pkRegen, regen_id_)) { + --m_pkRegen->count; + } + } else { + // Is this really safe? + --m_pkRegen->count; + } + m_pkRegen = NULL; + } + + if (m_pkDungeon) + { + SetDungeon(NULL); + } + +#ifdef __PET_SYSTEM__ + if (m_petSystem) + { + m_petSystem->Destroy(); + delete m_petSystem; + + m_petSystem = 0; + } +#endif + + HorseSummon(false); + + if (GetRider()) + GetRider()->ClearHorseInfo(); + + if (GetDesc()) + { + GetDesc()->BindCharacter(NULL); + } + + if (m_pkExchange) + m_pkExchange->Cancel(); + + SetVictim(NULL); + + if (GetShop()) + { + GetShop()->RemoveGuest(this); + SetShop(NULL); + } + + ClearStone(); + ClearSync(); + ClearTarget(); + + if (NULL == m_pkMobData) + { + DragonSoul_CleanUp(); + ClearItem(); + } + + // m_pkParty becomes NULL after CParty destructor call! + LPPARTY party = m_pkParty; + if (party) + { + if (party->GetLeaderPID() == GetVID() && !IsPC()) + { + M2_DELETE(party); + } + else + { + party->Unlink(this); + + if (!IsPC()) + party->Quit(GetVID()); + } + + SetParty(NULL); + } + + if (m_pkMobInst) + { + M2_DELETE(m_pkMobInst); + m_pkMobInst = NULL; + } + + m_pkMobData = NULL; + + if (m_pkSafebox) + { + M2_DELETE(m_pkSafebox); + m_pkSafebox = NULL; + } + + if (m_pkMall) + { + M2_DELETE(m_pkMall); + m_pkMall = NULL; + } + + for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++) + { + if (NULL != it->second) + { + M2_DELETE(it->second); + } + } + m_map_buff_on_attrs.clear(); + + m_set_pkChrSpawnedBy.clear(); + + StopMuyeongEvent(); + event_cancel(&m_pkWarpNPCEvent); + event_cancel(&m_pkRecoveryEvent); + event_cancel(&m_pkDeadEvent); + event_cancel(&m_pkSaveEvent); + event_cancel(&m_pkTimedEvent); + event_cancel(&m_pkStunEvent); + event_cancel(&m_pkFishingEvent); + event_cancel(&m_pkPoisonEvent); +#ifdef ENABLE_WOLFMAN_CHARACTER + event_cancel(&m_pkBleedingEvent); +#endif + event_cancel(&m_pkFireEvent); + event_cancel(&m_pkPartyRequestEvent); + //DELAYED_WARP + event_cancel(&m_pkWarpEvent); + event_cancel(&m_pkCheckSpeedHackEvent); + //END_DELAYED_WARP + + // MINING + event_cancel(&m_pkMiningEvent); + // END_OF_MINING + + for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it) + { + LPEVENT pkEvent = it->second; + event_cancel(&pkEvent); + } + m_mapMobSkillEvent.clear(); + + ClearAffect(); + + event_cancel(&m_pkDestroyWhenIdleEvent); + + if (m_pSkillLevels) + { + M2_DELETE_ARRAY(m_pSkillLevels); + m_pSkillLevels = NULL; + } + + CEntity::Destroy(); + + if (GetSectree()) + GetSectree()->RemoveEntity(this); + + if (m_bMonsterLog) + CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this); +} + +const char * CHARACTER::GetName() const +{ + return m_stName.empty() ? (m_pkMobData ? m_pkMobData->m_table.szLocaleName : "") : m_stName.c_str(); +} + +void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount) +{ + if (!CanHandleItem()) // @fixme149 + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ŷ(â,ȯ,) λ ϴ.")); + return; + } + +#ifndef ENABLE_OPEN_SHOP_WITH_ARMOR + if (GetPart(PART_MAIN) > 2) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֽϴ.")); + return; + } +#endif + + if (GetMyShop()) + { + CloseMyShop(); + return; + } + + quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID()); + if (pPC->IsRunning()) + return; + + if (bItemCount == 0) + return; + + int64_t nTotalMoney = 0; +#ifdef ENABLE_CHEQUE_SYSTEM + int64_t nTotalCheque = 0; +#endif + + for (int n = 0; n < bItemCount; ++n) + { + nTotalMoney += static_cast((pTable+n)->price); +#ifdef ENABLE_CHEQUE_SYSTEM + nTotalCheque += static_cast((pTable + n)->cheque); +#endif + } + + nTotalMoney += static_cast(GetGold()); +#ifdef ENABLE_CHEQUE_SYSTEM + nTotalCheque += static_cast(GetCheque()); +#endif + + if (GOLD_MAX <= nTotalMoney) + { + sys_err("[OVERFLOW_GOLD] Overflow (GOLD_MAX) id %u name %s", GetPlayerID(), GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20 ʰϿ ϴ")); + return; + } + +#ifdef ENABLE_CHEQUE_SYSTEM + if (CHEQUE_MAX <= nTotalCheque) + { + sys_err("[OVERFLOW_CHEQUE] Overflow (CHEQUE_MAX) id %u name %s", GetPlayerID(), GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't create a shop with more than %d won."), CHEQUE_MAX - 1); + return; + } +#endif + + char szSign[SHOP_SIGN_MAX_LEN+1]; + strlcpy(szSign, c_pszSign, sizeof(szSign)); + + m_stShopSign = szSign; + + if (m_stShopSign.length() == 0) + return; + + if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӿ  Ե ̸ ϴ.")); + return; + } + + // MYSHOP_PRICE_LIST + std::map itemkind; + // END_OF_MYSHOP_PRICE_LIST + + std::set cont; + for (BYTE i = 0; i < bItemCount; ++i) + { + if (cont.find((pTable + i)->pos) != cont.end()) + { + sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName()); + return; + } + + // ANTI_GIVE, ANTI_MYSHOP check + LPITEM pkItem = GetItem((pTable + i)->pos); + + if (pkItem) + { + const TItemTable * item_table = pkItem->GetProto(); + + if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ λ Ǹ ϴ.")); + return; + } + + if (pkItem->IsEquipped() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" λ Ǹ ϴ.")); + return; + } + + if (true == pkItem->isLocked()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" λ Ǹ ϴ.")); + return; + } + + // MYSHOP_PRICE_LIST + itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount(); + // END_OF_MYSHOP_PRICE_LIST + } + + cont.emplace((pTable + i)->pos); + } + + // MYSHOP_PRICE_LIST + + if (CountSpecifyItem(71049)) { + // @fixme403 BEGIN + TItemPriceListTable header; + memset(&header, 0, sizeof(TItemPriceListTable)); + + header.dwOwnerID = GetPlayerID(); + header.byCount = itemkind.size(); + + size_t idx=0; + for (itertype(itemkind) it = itemkind.begin(); it != itemkind.end(); ++it) + { + header.aPriceInfo[idx].dwVnum = it->first; + header.aPriceInfo[idx].dwPrice = it->second; + idx++; + } + + db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_UPDATE, GetDesc()->GetHandle(), &header, sizeof(TItemPriceListTable)); + // @fixme403 END + } + // END_OF_MYSHOP_PRICE_LIST + else if (CountSpecifyItem(50200)) + RemoveSpecifyItem(50200, 1); + else + return; + + if (m_pkExchange) + m_pkExchange->Cancel(); + + TPacketGCShopSign p; + + p.bHeader = HEADER_GC_SHOP_SIGN; + p.dwVID = GetVID(); + strlcpy(p.szSign, c_pszSign, sizeof(p.szSign)); + + PacketAround(p); + + m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount); + + if (IsPolymorphed() == true) + { + RemoveAffect(AFFECT_POLYMORPH); + } + + if (GetHorse()) + { + HorseSummon( false, true ); + } + else if (GetMountVnum()) + { + RemoveAffect(AFFECT_MOUNT); + RemoveAffect(AFFECT_MOUNT_BONUS); + } + + SetPolymorph(30000, true); +} + +void CHARACTER::CloseMyShop() +{ + if (GetMyShop()) + { + m_stShopSign.clear(); + CShopManager::instance().DestroyPCShop(this); + m_pkMyShop = NULL; + + TPacketGCShopSign p; + + p.bHeader = HEADER_GC_SHOP_SIGN; + p.dwVID = GetVID(); + p.szSign[0] = '\0'; + + PacketAround(p); +#ifdef ENABLE_WOLFMAN_CHARACTER + SetPolymorph(m_points.job, true); + // SetPolymorph(0, true); +#else + SetPolymorph(GetJob(), true); +#endif + } +} + +void EncodeMovePacket(TPacketGCMove & pack, DWORD dwVID, BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, BYTE bRot) +{ + pack.bHeader = HEADER_GC_MOVE; + pack.bFunc = bFunc; + pack.bArg = bArg; + pack.dwVID = dwVID; + pack.dwTime = dwTime ? dwTime : get_dword_time(); + pack.bRot = bRot; + pack.lX = x; + pack.lY = y; + pack.dwDuration = dwDuration; +} + +void CHARACTER::RestartAtSamePos() +{ + if (m_bIsObserver) + return; + + EncodeRemovePacket(this); + EncodeInsertPacket(this); + + ENTITY_MAP::iterator it = m_map_view.begin(); + + while (it != m_map_view.end()) + { + LPENTITY entity = (it++)->first; + + EncodeRemovePacket(entity); + if (!m_bIsObserver) + EncodeInsertPacket(entity); + + if( entity->IsType(ENTITY_CHARACTER) ) + { + LPCHARACTER lpChar = (LPCHARACTER)entity; + if( lpChar->IsPC() || lpChar->IsNPC() || lpChar->IsMonster() ) + { + if (!entity->IsObserverMode()) + entity->EncodeInsertPacket(this); + } + } + else + { + if( !entity->IsObserverMode()) + { + entity->EncodeInsertPacket(this); + } + } + } +} + +// #define ENABLE_SHOWNPCLEVEL +void CHARACTER::EncodeInsertPacket(LPENTITY entity) +{ + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + LPCHARACTER ch = (LPCHARACTER) entity; + ch->SendGuildName(GetGuild()); + + TPacketGCCharacterAdd pack; + + pack.header = HEADER_GC_CHARACTER_ADD; + pack.dwVID = m_vid; + pack.bType = GetCharType(); + pack.angle = GetRotation(); + pack.x = GetX(); + pack.y = GetY(); + pack.z = GetZ(); + pack.wRaceNum = GetRaceNum(); + if (IsPet()) + { + pack.bMovingSpeed = 150; + } + else + { + pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED); + } + pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED); + pack.dwAffectFlag[0] = m_afAffectFlag.bits[0]; + pack.dwAffectFlag[1] = m_afAffectFlag.bits[1]; + + pack.bStateFlag = m_bAddChrState; + + int iDur = 0; + + if (m_posDest.x != pack.x || m_posDest.y != pack.y) + { + iDur = (m_dwMoveStartTime + m_dwMoveDuration) - get_dword_time(); + + if (iDur <= 0) + { + pack.x = m_posDest.x; + pack.y = m_posDest.y; + } + } + + d->Packet(pack); + + if (IsPC() == true || m_bCharType == CHAR_TYPE_NPC) + { + TPacketGCCharacterAdditionalInfo addPacket; + memset(&addPacket, 0, sizeof(TPacketGCCharacterAdditionalInfo)); + + addPacket.header = HEADER_GC_CHAR_ADDITIONAL_INFO; + addPacket.dwVID = m_vid; + + addPacket.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN); + addPacket.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON); + addPacket.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD); + addPacket.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + addPacket.awPart[CHR_EQUIPPART_ACCE] = GetPart(PART_ACCE); +#endif + addPacket.bPKMode = m_bPKMode; + addPacket.dwMountVnum = GetMountVnum(); +#ifdef ENABLE_QUIVER_SYSTEM + addPacket.dwArrow = (IsPC() && GetWear(WEAR_ARROW)) ? GetWear(WEAR_ARROW)->GetOriginalVnum() : 0; +#endif + addPacket.bEmpire = m_bEmpire; + +#ifdef ENABLE_SHOWNPCLEVEL + if (1) +#else + if (IsPC() == true) +#endif + { + addPacket.dwLevel = GetLevel(); + } + else + { + addPacket.dwLevel = 0; + } + + if (false) + { + LPCHARACTER ch = (LPCHARACTER) entity; + + if (GetEmpire() == ch->GetEmpire() || ch->GetGMLevel() > GM_PLAYER || m_bCharType == CHAR_TYPE_NPC) + { + goto show_all_info; + } + else + { + memset(addPacket.name, 0, CHARACTER_NAME_MAX_LEN); + addPacket.dwGuildID = 0; + addPacket.sAlignment = 0; + } + } + else + { + show_all_info: + strlcpy(addPacket.name, GetName(), sizeof(addPacket.name)); + + if (GetGuild() != NULL) + { + addPacket.dwGuildID = GetGuild()->GetID(); + } + else + { + addPacket.dwGuildID = 0; + } + + addPacket.sAlignment = m_iAlignment / 10; + } + + d->Packet(addPacket); + } + + if (iDur) + { + TPacketGCMove pack; + EncodeMovePacket(pack, GetVID(), FUNC_MOVE, 0, m_posDest.x, m_posDest.y, iDur, 0, (BYTE) (GetRotation() / 5)); + d->Packet(pack); + + TPacketGCWalkMode p; + p.vid = GetVID(); + p.header = HEADER_GC_WALK_MODE; + p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN; + + d->Packet(p); + } + + if (entity->IsType(ENTITY_CHARACTER) && GetDesc()) + { + LPCHARACTER ch = (LPCHARACTER) entity; + if (ch->IsWalking()) + { + TPacketGCWalkMode p; + p.vid = ch->GetVID(); + p.header = HEADER_GC_WALK_MODE; + p.mode = ch->m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN; + GetDesc()->Packet(p); + } + } + + if (GetMyShop()) + { + TPacketGCShopSign p; + + p.bHeader = HEADER_GC_SHOP_SIGN; + p.dwVID = GetVID(); + strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign)); + + d->Packet(p); + } + + if (entity->IsType(ENTITY_CHARACTER)) + { + sys_log(3, "EntityInsert %s (RaceNum %d) (%d %d) TO %s", + GetName(), GetRaceNum(), GetX() / SECTREE_SIZE, GetY() / SECTREE_SIZE, ((LPCHARACTER)entity)->GetName()); + } +} + +void CHARACTER::EncodeRemovePacket(LPENTITY entity) +{ + if (entity->GetType() != ENTITY_CHARACTER) + return; + + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + TPacketGCCharacterDelete pack; + + pack.header = HEADER_GC_CHARACTER_DEL; + pack.id = m_vid; + + d->Packet(pack); + + if (entity->IsType(ENTITY_CHARACTER)) + sys_log(3, "EntityRemove %s(%d) FROM %s", GetName(), (DWORD) m_vid, ((LPCHARACTER) entity)->GetName()); +} + +void CHARACTER::UpdatePacket() +{ + if (GetSectree() == NULL) return; + + TPacketGCCharacterUpdate pack; + TPacketGCCharacterUpdate pack2; + + pack.header = HEADER_GC_CHARACTER_UPDATE; + pack.dwVID = m_vid; + + pack.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN); + pack.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON); + pack.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD); + pack.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + pack.awPart[CHR_EQUIPPART_ACCE] = GetPart(PART_ACCE); +#endif + pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED); + pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED); + pack.bStateFlag = m_bAddChrState; + pack.dwAffectFlag[0] = m_afAffectFlag.bits[0]; + pack.dwAffectFlag[1] = m_afAffectFlag.bits[1]; + pack.dwGuildID = 0; + pack.sAlignment = m_iAlignment / 10; + pack.bPKMode = m_bPKMode; + + if (GetGuild()) + pack.dwGuildID = GetGuild()->GetID(); + + pack.dwMountVnum = GetMountVnum(); +#ifdef ENABLE_QUIVER_SYSTEM + pack.dwArrow = (GetWear(WEAR_ARROW)) ? GetWear(WEAR_ARROW)->GetOriginalVnum() : 0; +#endif + + pack2 = pack; + pack2.dwGuildID = 0; + pack2.sAlignment = 0; + + if (false) + { + if (m_bIsObserver != true) + { + for (ENTITY_MAP::iterator iter = m_map_view.begin(); iter != m_map_view.end(); iter++) + { + LPENTITY pEntity = iter->first; + + if (pEntity != NULL) + { + if (pEntity->IsType(ENTITY_CHARACTER) == true) + { + if (pEntity->GetDesc() != NULL) + { + LPCHARACTER pChar = (LPCHARACTER)pEntity; + + if (GetEmpire() == pChar->GetEmpire() || pChar->GetGMLevel() > GM_PLAYER) + { + pEntity->GetDesc()->Packet(pack); + } + else + { + pEntity->GetDesc()->Packet(pack2); + } + } + } + else + { + if (pEntity->GetDesc() != NULL) + { + pEntity->GetDesc()->Packet(pack); + } + } + } + } + } + + if (GetDesc() != NULL) + { + GetDesc()->Packet(pack); + } + } + else + { + PacketAround(pack); + } +} + +LPCHARACTER CHARACTER::FindCharacterInView(const char * c_pszName, bool bFindPCOnly) +{ + ENTITY_MAP::iterator it = m_map_view.begin(); + + for (; it != m_map_view.end(); ++it) + { + if (!it->first->IsType(ENTITY_CHARACTER)) + continue; + + LPCHARACTER tch = (LPCHARACTER) it->first; + + if (bFindPCOnly && tch->IsNPC()) + continue; + + if (!strcasecmp(tch->GetName(), c_pszName)) + return (tch); + } + + return NULL; +} + +void CHARACTER::SetPosition(int pos) +{ + if (pos == POS_STANDING) + { + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD); + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); + + event_cancel(&m_pkDeadEvent); + event_cancel(&m_pkStunEvent); + } + else if (pos == POS_DEAD) + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD); + + if (!IsStone()) + { + switch (pos) + { + case POS_FIGHTING: + if (!IsState(m_stateBattle)) + MonsterLog("[BATTLE] ο "); + + GotoState(m_stateBattle); + break; + + default: + if (!IsState(m_stateIdle)) + MonsterLog("[IDLE] "); + + GotoState(m_stateIdle); + break; + } + } + + m_pointsInstant.position = pos; +} + +void CHARACTER::Save() +{ + if (!m_bSkipSave) + CHARACTER_MANAGER::instance().DelayedSave(this); +} + +void CHARACTER::CreatePlayerProto(TPlayerTable & tab) +{ + memset(&tab, 0, sizeof(TPlayerTable)); + + if (GetNewName().empty()) + { + strlcpy(tab.name, GetName(), sizeof(tab.name)); + } + else + { + strlcpy(tab.name, GetNewName().c_str(), sizeof(tab.name)); + } + + strlcpy(tab.ip, GetDesc()->GetHostName(), sizeof(tab.ip)); + + tab.id = m_dwPlayerID; + tab.voice = GetPoint(POINT_VOICE); + tab.level = GetLevel(); + tab.level_step = GetPoint(POINT_LEVEL_STEP); + tab.exp = GetExp(); + tab.gold = GetGold(); + tab.job = m_points.job; + tab.part_base = m_pointsInstant.bBasePart; + tab.skill_group = m_points.skill_group; +#ifdef ENABLE_CHEQUE_SYSTEM + tab.cheque = GetCheque(); +#endif + + DWORD dwPlayedTime = (get_dword_time() - m_dwPlayStartTime); + + if (dwPlayedTime > 60000) + { + if (GetSectree() && !GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK)) + { + if (GetRealAlignment() < 0) + { + if (IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_TIME)) + UpdateAlignment(120 * (dwPlayedTime / 60000)); + else + UpdateAlignment(60 * (dwPlayedTime / 60000)); + } + else + UpdateAlignment(5 * (dwPlayedTime / 60000)); + } + + SetRealPoint(POINT_PLAYTIME, GetRealPoint(POINT_PLAYTIME) + dwPlayedTime / 60000); + ResetPlayTime(dwPlayedTime % 60000); + } + + tab.playtime = GetRealPoint(POINT_PLAYTIME); + tab.lAlignment = m_iRealAlignment; + + if (m_posWarp.x != 0 || m_posWarp.y != 0) + { + tab.x = m_posWarp.x; + tab.y = m_posWarp.y; + tab.z = 0; + tab.lMapIndex = m_lWarpMapIndex; + } + else + { + tab.x = GetX(); + tab.y = GetY(); + tab.z = GetZ(); + tab.lMapIndex = GetMapIndex(); + } + + if (m_lExitMapIndex == 0) + { + tab.lExitMapIndex = tab.lMapIndex; + tab.lExitX = tab.x; + tab.lExitY = tab.y; + } + else + { + tab.lExitMapIndex = m_lExitMapIndex; + tab.lExitX = m_posExit.x; + tab.lExitY = m_posExit.y; + } + + sys_log(0, "SAVE: %s %dx%d", GetName(), tab.x, tab.y); + + tab.st = GetRealPoint(POINT_ST); + tab.ht = GetRealPoint(POINT_HT); + tab.dx = GetRealPoint(POINT_DX); + tab.iq = GetRealPoint(POINT_IQ); + + tab.stat_point = GetPoint(POINT_STAT); + tab.skill_point = GetPoint(POINT_SKILL); + tab.sub_skill_point = GetPoint(POINT_SUB_SKILL); + tab.horse_skill_point = GetPoint(POINT_HORSE_SKILL); + + tab.stat_reset_count = GetPoint(POINT_STAT_RESET_COUNT); + + tab.hp = GetHP(); + tab.sp = GetSP(); + + tab.stamina = GetStamina(); + + tab.sRandomHP = m_points.iRandomHP; + tab.sRandomSP = m_points.iRandomSP; + + if (m_PlayerSlots) + { + for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i) + tab.quickslot[i] = m_PlayerSlots->pQuickslot[i]; + } + + thecore_memcpy(tab.parts, m_pointsInstant.parts, sizeof(tab.parts)); + + // REMOVE_REAL_SKILL_LEVLES + thecore_memcpy(tab.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + // END_OF_REMOVE_REAL_SKILL_LEVLES + + tab.horse = GetHorseData(); +} + +void CHARACTER::SaveReal() +{ + if (m_bSkipSave) + return; + + if (!GetDesc()) + { + sys_err("Character::Save : no descriptor when saving (name: %s)", GetName()); + return; + } + + TPlayerTable table; + CreatePlayerProto(table); + + db_clientdesc->DBPacket(HEADER_GD_PLAYER_SAVE, GetDesc()->GetHandle(), &table, sizeof(TPlayerTable)); + + quest::PC * pkQuestPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID()); + + if (!pkQuestPC) + sys_err("CHARACTER::Save : null quest::PC pointer! (name %s)", GetName()); + else + { + pkQuestPC->Save(); + } + + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID()); + if (pMarriage) + pMarriage->Save(); +} + +void CHARACTER::FlushDelayedSaveItem() +{ + LPITEM item; + + for (int i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + if ((item = GetInventoryItem(i))) + ITEM_MANAGER::instance().FlushDelayedSave(item); +} + +void CHARACTER::Disconnect(const char * c_pszReason) +{ + assert(GetDesc() != NULL); + + sys_log(0, "DISCONNECT: %s (%s)", GetName(), c_pszReason ? c_pszReason : "unset" ); + + if (GetShop()) + { + GetShop()->RemoveGuest(this); + SetShop(NULL); + } + + if (GetArena() != NULL) + { + GetArena()->OnDisconnect(GetPlayerID()); + } + + if (GetParty() != NULL) + { + GetParty()->UpdateOfflineState(GetPlayerID()); + } + + marriage::CManager::instance().Logout(this); + + // P2P Logout + TPacketGGLogout p; + p.bHeader = HEADER_GG_LOGOUT; + strlcpy(p.szName, GetName(), sizeof(p.szName)); + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogout)); + LogManager::instance().CharLog(this, 0, "LOGOUT", ""); + + if (m_pWarMap) + SetWarMap(NULL); + + if (m_pWeddingMap) + { + SetWeddingMap(NULL); + } + + if (GetGuild()) + GetGuild()->LogoutMember(this); + + quest::CQuestManager::instance().LogoutPC(this); + + if (GetParty()) + GetParty()->Unlink(this); + + if (IsStun() || IsDead()) + { + DeathPenalty(0); + PointChange(POINT_HP, 50 - GetHP()); + } + + if (!CHARACTER_MANAGER::instance().FlushDelayedSave(this)) + { + SaveReal(); + } + + FlushDelayedSaveItem(); + + SaveAffect(); + m_bIsLoadedAffect = false; + + m_bSkipSave = true; + + quest::CQuestManager::instance().DisconnectPC(this); + + CloseSafebox(); + + CloseMall(); + + CPVPManager::instance().Disconnect(this); + + CTargetManager::instance().Logout(GetPlayerID()); + + MessengerManager::instance().Logout(GetName()); + + if (GetDesc()) + GetDesc()->BindCharacter(NULL); + + M2_DESTROY_CHARACTER(this); +} + +bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMotion/* = false */) +{ + LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!sectree) + { + sys_log(0, "cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex); + return false; + } + + SetMapIndex(lMapIndex); + + bool bChangeTree = false; + + if (!GetSectree() || GetSectree() != sectree) + bChangeTree = true; + + if (bChangeTree) + { + if (GetSectree()) + GetSectree()->RemoveEntity(this); + + ViewCleanup( + #ifdef ENABLE_GOTO_LAG_FIX + IsPC() + #endif + ); + } + + if (!IsNPC()) + { + sys_log(0, "SHOW: %s %dx%dx%d", GetName(), x, y, z); + if (GetStamina() < GetMaxStamina()) + StartAffectEvent(); + } + else if (m_pkMobData) + { + m_pkMobInst->m_posLastAttacked.x = x; + m_pkMobInst->m_posLastAttacked.y = y; + m_pkMobInst->m_posLastAttacked.z = z; + } + + if (bShowSpawnMotion) + { + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN); + m_afAffectFlag.Set(AFF_SPAWN); + } + + SetXYZ(x, y, z); + + m_posDest.x = x; + m_posDest.y = y; + m_posDest.z = z; + + m_posStart.x = x; + m_posStart.y = y; + m_posStart.z = z; + + if (bChangeTree) + { + EncodeInsertPacket(this); + sectree->InsertEntity(this); + + UpdateSectree(); + } + else + { + ViewReencode(); + sys_log(0, " in same sectree"); + } + + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN); + + SetValidComboInterval(0); + return true; +} + +// BGM_INFO +struct BGMInfo +{ + std::string name; + float vol; +}; + +typedef std::map BGMInfoMap; + +static BGMInfoMap gs_bgmInfoMap; +static bool gs_bgmVolEnable = false; + +void CHARACTER_SetBGMVolumeEnable() +{ + gs_bgmVolEnable = true; + sys_log(0, "bgm_info.set_bgm_volume_enable"); +} + +void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol) +{ + BGMInfo newInfo; + newInfo.name = name; + newInfo.vol = vol; + + gs_bgmInfoMap[mapIndex] = newInfo; + + sys_log(0, "bgm_info.add_info(%d, '%s', %f)", mapIndex, name, vol); +} + +const BGMInfo& CHARACTER_GetBGMInfo(unsigned mapIndex) +{ + BGMInfoMap::iterator f = gs_bgmInfoMap.find(mapIndex); + if (gs_bgmInfoMap.end() == f) + { + static BGMInfo s_empty = {"", 0.0f}; + return s_empty; + } + return f->second; +} + +bool CHARACTER_IsBGMVolumeEnable() +{ + return gs_bgmVolEnable; +} +// END_OF_BGM_INFO + +void CHARACTER::MainCharacterPacket() +{ + const unsigned mapIndex = GetMapIndex(); + const BGMInfo& bgmInfo = CHARACTER_GetBGMInfo(mapIndex); + + // SUPPORT_BGM + if (!bgmInfo.name.empty()) + { + if (CHARACTER_IsBGMVolumeEnable()) + { + sys_log(1, "bgm_info.play_bgm_vol(%d, name='%s', vol=%f)", mapIndex, bgmInfo.name.c_str(), bgmInfo.vol); + TPacketGCMainCharacter4_BGM_VOL mainChrPacket; + mainChrPacket.header = HEADER_GC_MAIN_CHARACTER4_BGM_VOL; + mainChrPacket.dwVID = m_vid; + mainChrPacket.wRaceNum = GetRaceNum(); + mainChrPacket.lx = GetX(); + mainChrPacket.ly = GetY(); + mainChrPacket.lz = GetZ(); + mainChrPacket.empire = GetDesc()->GetEmpire(); + mainChrPacket.skill_group = GetSkillGroup(); + strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName)); + + mainChrPacket.fBGMVol = bgmInfo.vol; + strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName)); + GetDesc()->Packet(mainChrPacket); + } + else + { + sys_log(1, "bgm_info.play(%d, '%s')", mapIndex, bgmInfo.name.c_str()); + TPacketGCMainCharacter3_BGM mainChrPacket; + mainChrPacket.header = HEADER_GC_MAIN_CHARACTER3_BGM; + mainChrPacket.dwVID = m_vid; + mainChrPacket.wRaceNum = GetRaceNum(); + mainChrPacket.lx = GetX(); + mainChrPacket.ly = GetY(); + mainChrPacket.lz = GetZ(); + mainChrPacket.empire = GetDesc()->GetEmpire(); + mainChrPacket.skill_group = GetSkillGroup(); + strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName)); + strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName)); + GetDesc()->Packet(mainChrPacket); + } + } + // END_OF_SUPPORT_BGM + else + { + sys_log(0, "bgm_info.play(%d, DEFAULT_BGM_NAME)", mapIndex); + + TPacketGCMainCharacter pack; + pack.header = HEADER_GC_MAIN_CHARACTER; + pack.dwVID = m_vid; + pack.wRaceNum = GetRaceNum(); + pack.lx = GetX(); + pack.ly = GetY(); + pack.lz = GetZ(); + pack.empire = GetDesc()->GetEmpire(); + pack.skill_group = GetSkillGroup(); + strlcpy(pack.szName, GetName(), sizeof(pack.szName)); + GetDesc()->Packet(pack); + } +} + +void CHARACTER::PointsPacket() +{ + if (!GetDesc()) + return; + + TPacketGCPoints pack; + + pack.header = HEADER_GC_CHARACTER_POINTS; + + pack.points[POINT_LEVEL] = GetLevel(); + pack.points[POINT_EXP] = GetExp(); + pack.points[POINT_NEXT_EXP] = GetNextExp(); + pack.points[POINT_HP] = GetHP(); + pack.points[POINT_MAX_HP] = GetMaxHP(); + pack.points[POINT_SP] = GetSP(); + pack.points[POINT_MAX_SP] = GetMaxSP(); + pack.points[POINT_GOLD] = GetGold(); + pack.points[POINT_STAMINA] = GetStamina(); + pack.points[POINT_MAX_STAMINA] = GetMaxStamina(); + + for (int i = POINT_ST; i < POINT_MAX_NUM; ++i) + pack.points[i] = GetPoint(i); + +#ifdef ENABLE_CHEQUE_SYSTEM + pack.points[POINT_CHEQUE] = GetCheque(); +#endif + + GetDesc()->Packet(pack); +} + +bool CHARACTER::ChangeSex() +{ + int src_race = GetRaceNum(); + + switch (src_race) + { + case MAIN_RACE_WARRIOR_M: + m_points.job = MAIN_RACE_WARRIOR_W; + break; + + case MAIN_RACE_WARRIOR_W: + m_points.job = MAIN_RACE_WARRIOR_M; + break; + + case MAIN_RACE_ASSASSIN_M: + m_points.job = MAIN_RACE_ASSASSIN_W; + break; + + case MAIN_RACE_ASSASSIN_W: + m_points.job = MAIN_RACE_ASSASSIN_M; + break; + + case MAIN_RACE_SURA_M: + m_points.job = MAIN_RACE_SURA_W; + break; + + case MAIN_RACE_SURA_W: + m_points.job = MAIN_RACE_SURA_M; + break; + + case MAIN_RACE_SHAMAN_M: + m_points.job = MAIN_RACE_SHAMAN_W; + break; + + case MAIN_RACE_SHAMAN_W: + m_points.job = MAIN_RACE_SHAMAN_M; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case MAIN_RACE_WOLFMAN_M: + m_points.job = MAIN_RACE_WOLFMAN_M; + break; +#endif + default: + sys_err("CHANGE_SEX: %s unknown race %d", GetName(), src_race); + return false; + } + + sys_log(0, "CHANGE_SEX: %s (%d -> %d)", GetName(), src_race, m_points.job); + return true; +} + +DWORD CHARACTER::GetRaceNum() const // @fixme501 +{ + if (m_dwPolymorphRace) + return m_dwPolymorphRace; + + if (m_pkMobData) + return m_pkMobData->m_table.dwVnum; + + return m_points.job; +} + +void CHARACTER::SetRace(BYTE race) +{ + if (race >= MAIN_RACE_MAX_NUM) + { + sys_err("CHARACTER::SetRace(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race); + return; + } + + m_points.job = race; +} + +BYTE CHARACTER::GetJob() const +{ + unsigned race = m_points.job; + unsigned job; + + if (RaceToJob(race, &job)) + return job; + + sys_err("CHARACTER::GetJob(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race); + return JOB_WARRIOR; +} + +void CHARACTER::SetLevel(BYTE level) +{ + m_points.level = level; + + if (IsPC()) + { + if (level < PK_PROTECT_LEVEL) + SetPKMode(PK_MODE_PROTECT); + else if (GetGMLevel() != GM_PLAYER) + SetPKMode(PK_MODE_PROTECT); + else if (m_bPKMode == PK_MODE_PROTECT) + SetPKMode(PK_MODE_PEACE); + } +} + +void CHARACTER::SetEmpire(BYTE bEmpire) +{ + m_bEmpire = bEmpire; +} + +#define ENABLE_GM_FLAG_IF_TEST_SERVER +#define ENABLE_GM_FLAG_FOR_LOW_WIZARD +void CHARACTER::SetPlayerProto(const TPlayerTable * t) +{ + if (!GetDesc() || !*GetDesc()->GetHostName()) + sys_err("cannot get desc or hostname"); + else + SetGMLevel(); + + m_PlayerSlots = std::make_unique(); // @fixme199 + m_bCharType = CHAR_TYPE_PC; + + m_dwPlayerID = t->id; + + m_iAlignment = t->lAlignment; + m_iRealAlignment = t->lAlignment; + + m_points.voice = t->voice; + + m_points.skill_group = t->skill_group; + + m_pointsInstant.bBasePart = t->part_base; + SetPart(PART_HAIR, t->parts[PART_HAIR]); + + m_points.iRandomHP = t->sRandomHP; + m_points.iRandomSP = t->sRandomSP; + + // REMOVE_REAL_SKILL_LEVLES + if (m_pSkillLevels) + M2_DELETE_ARRAY(m_pSkillLevels); + + m_pSkillLevels = M2_NEW TPlayerSkill[SKILL_MAX_NUM]; + thecore_memcpy(m_pSkillLevels, t->skills, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + // END_OF_REMOVE_REAL_SKILL_LEVLES + + if (t->lMapIndex >= 10000) + { + m_posWarp.x = t->lExitX; + m_posWarp.y = t->lExitY; + m_lWarpMapIndex = t->lExitMapIndex; + } + + SetRealPoint(POINT_PLAYTIME, t->playtime); + m_dwLoginPlayTime = t->playtime; + SetRealPoint(POINT_ST, t->st); + SetRealPoint(POINT_HT, t->ht); + SetRealPoint(POINT_DX, t->dx); + SetRealPoint(POINT_IQ, t->iq); + + SetPoint(POINT_ST, t->st); + SetPoint(POINT_HT, t->ht); + SetPoint(POINT_DX, t->dx); + SetPoint(POINT_IQ, t->iq); + + SetPoint(POINT_STAT, t->stat_point); + SetPoint(POINT_SKILL, t->skill_point); + SetPoint(POINT_SUB_SKILL, t->sub_skill_point); + SetPoint(POINT_HORSE_SKILL, t->horse_skill_point); + + SetPoint(POINT_STAT_RESET_COUNT, t->stat_reset_count); + + SetPoint(POINT_LEVEL_STEP, t->level_step); + SetRealPoint(POINT_LEVEL_STEP, t->level_step); + + SetRace(t->job); + + SetLevel(t->level); + SetExp(t->exp); + SetGold(t->gold); +#ifdef ENABLE_CHEQUE_SYSTEM + SetCheque(t->cheque); +#endif + + SetMapIndex(t->lMapIndex); + SetXYZ(t->x, t->y, t->z); + + ComputePoints(); + + SetHP(t->hp); + SetSP(t->sp); + SetStamina(t->stamina); + +#ifndef ENABLE_GM_FLAG_IF_TEST_SERVER + if (!test_server) +#endif + { +#ifdef ENABLE_GM_FLAG_FOR_LOW_WIZARD + if (GetGMLevel() > GM_PLAYER) +#else + if (GetGMLevel() > GM_LOW_WIZARD) +#endif + { + m_afAffectFlag.Set(AFF_YMIR); + m_bPKMode = PK_MODE_PROTECT; + } + } + + if (GetLevel() < PK_PROTECT_LEVEL) + m_bPKMode = PK_MODE_PROTECT; + + SetHorseData(t->horse); + + if (GetHorseLevel() > 0) + UpdateHorseDataByLogoff(t->logoff_interval); + + thecore_memcpy(m_aiPremiumTimes, t->aiPremiumTimes, sizeof(t->aiPremiumTimes)); + + m_dwLogOffInterval = t->logoff_interval; + + sys_log(0, "PLAYER_LOAD: %s PREMIUM %d %d, LOGGOFF_INTERVAL %u PTR: %p", t->name, m_aiPremiumTimes[0], m_aiPremiumTimes[1], t->logoff_interval, this); + + if (GetGMLevel() != GM_PLAYER) + { + LogManager::instance().CharLog(this, GetGMLevel(), "GM_LOGIN", ""); + sys_log(0, "GM_LOGIN(gmlevel=%d, name=%s(%d), pos=(%d, %d)", GetGMLevel(), GetName(), GetPlayerID(), GetX(), GetY()); + } + +#ifdef __PET_SYSTEM__ + if (m_petSystem) + { + m_petSystem->Destroy(); + delete m_petSystem; + } + + m_petSystem = M2_NEW CPetSystem(this); +#endif +} + +EVENTFUNC(kill_ore_load_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "kill_ore_load_even> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + + ch->m_pkMiningEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +void CHARACTER::SetProto(const CMob * pkMob) +{ + if (m_pkMobInst) + M2_DELETE(m_pkMobInst); + + m_pkMobData = pkMob; + m_pkMobInst = M2_NEW CMobInstance; + + m_bPKMode = PK_MODE_FREE; + + const TMobTable * t = &m_pkMobData->m_table; + + m_bCharType = t->bType; + + SetLevel(t->bLevel); + SetEmpire(t->bEmpire); + + SetExp(t->dwExp); + SetRealPoint(POINT_ST, t->bStr); + SetRealPoint(POINT_DX, t->bDex); + SetRealPoint(POINT_HT, t->bCon); + SetRealPoint(POINT_IQ, t->bInt); + + ComputePoints(); + + SetHP(GetMaxHP()); + SetSP(GetMaxSP()); + + //////////////////// + m_pointsInstant.dwAIFlag = t->dwAIFlag; + SetImmuneFlag(t->dwImmuneFlag); + + AssignTriggers(t); + + ApplyMobAttribute(t); + + if (IsStone()) + { + DetermineDropMetinStone(); + } + + if (IsWarp() || IsGoto()) + { + StartWarpNPCEvent(); + } + + CHARACTER_MANAGER::instance().RegisterRaceNumMap(this); + + // XXX X-mas santa hardcoding + if (GetRaceNum() == xmas::MOB_SANTA_VNUM) + { + SetPoint(POINT_ATT_GRADE_BONUS, 10); + SetPoint(POINT_DEF_GRADE_BONUS, 6); + + //m_dwPlayStartTime = get_dword_time() + 10 * 60 * 1000; + m_dwPlayStartTime = get_dword_time() + 30 * 1000; + if (test_server) + m_dwPlayStartTime = get_dword_time() + 30 * 1000; + } + + // XXX CTF GuildWar hardcoding + if (warmap::IsWarFlag(GetRaceNum())) + { + m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty); + m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty); + m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty); + } + + if (warmap::IsWarFlagBase(GetRaceNum())) + { + m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty); + m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty); + m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty); + } + + if (m_bCharType == CHAR_TYPE_HORSE || + GetRaceNum() == 20101 || + GetRaceNum() == 20102 || + GetRaceNum() == 20103 || + GetRaceNum() == 20104 || + GetRaceNum() == 20105 || + GetRaceNum() == 20106 || + GetRaceNum() == 20107 || + GetRaceNum() == 20108 || + GetRaceNum() == 20109 + ) + { + m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty); + m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty); + m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty); + } + + // MINING + if (mining::IsVeinOfOre (GetRaceNum())) + { + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkMiningEvent = event_create(kill_ore_load_event, info, PASSES_PER_SEC(number(7 * 60, 15 * 60))); + } + // END_OF_MINING +} + +const TMobTable & CHARACTER::GetMobTable() const +{ + return m_pkMobData->m_table; +} + +bool CHARACTER::IsRaceFlag(DWORD dwBit) const +{ + return m_pkMobData ? IS_SET(m_pkMobData->m_table.dwRaceFlag, dwBit) : 0; +} + +DWORD CHARACTER::GetMobDamageMin() const +{ + return m_pkMobData->m_table.dwDamageRange[0]; +} + +DWORD CHARACTER::GetMobDamageMax() const +{ + return m_pkMobData->m_table.dwDamageRange[1]; +} + +float CHARACTER::GetMobDamageMultiply() const +{ + float fDamMultiply = GetMobTable().fDamMultiply; + + if (IsBerserk()) + fDamMultiply = fDamMultiply * 2.0f; + + return fDamMultiply; +} + +DWORD CHARACTER::GetMobDropItemVnum() const +{ + return m_pkMobData->m_table.dwDropItemVnum; +} + +bool CHARACTER::IsSummonMonster() const +{ + return GetSummonVnum() != 0; +} + +DWORD CHARACTER::GetSummonVnum() const +{ + return m_pkMobData ? m_pkMobData->m_table.dwSummonVnum : 0; +} + +DWORD CHARACTER::GetPolymorphItemVnum() const +{ + return m_pkMobData ? m_pkMobData->m_table.dwPolymorphItemVnum : 0; +} + +DWORD CHARACTER::GetMonsterDrainSPPoint() const +{ + return m_pkMobData ? m_pkMobData->m_table.dwDrainSP : 0; +} + +BYTE CHARACTER::GetMobRank() const +{ + if (!m_pkMobData) + return MOB_RANK_KNIGHT; + + return m_pkMobData->m_table.bRank; +} + +BYTE CHARACTER::GetMobSize() const +{ + if (!m_pkMobData) + return MOBSIZE_MEDIUM; + + return m_pkMobData->m_table.bSize; +} + +WORD CHARACTER::GetMobAttackRange() const +{ + switch (GetMobBattleType()) + { + case BATTLE_TYPE_RANGE: + case BATTLE_TYPE_MAGIC: + return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE); + default: + return m_pkMobData->m_table.wAttackRange; + } +} + +BYTE CHARACTER::GetMobBattleType() const +{ + if (!m_pkMobData) + return BATTLE_TYPE_MELEE; + + return (m_pkMobData->m_table.bBattleType); +} + +void CHARACTER::ComputeBattlePoints() +{ + if (IsPolymorphed()) + { + DWORD dwMobVnum = GetPolymorphVnum(); + const CMob * pMob = CMobManager::instance().Get(dwMobVnum); + int iAtt = 0; + int iDef = 0; + + if (pMob) + { + iAtt = GetLevel() * 2 + GetPolymorphPoint(POINT_ST) * 2; + // lev + con + iDef = GetLevel() + GetPolymorphPoint(POINT_HT) + pMob->m_table.wDef; + } + + SetPoint(POINT_ATT_GRADE, iAtt); + SetPoint(POINT_DEF_GRADE, iDef); + SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE)); + SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE)); + } + else if (IsPC()) + { + SetPoint(POINT_ATT_GRADE, 0); + SetPoint(POINT_DEF_GRADE, 0); + SetPoint(POINT_CLIENT_DEF_GRADE, 0); + SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE)); + SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE)); + + // ATK = 2lev + 2str + + int iAtk = GetLevel() * 2; + int iStatAtk = 0; + + switch (GetJob()) + { + case JOB_WARRIOR: + case JOB_SURA: + iStatAtk = (2 * GetPoint(POINT_ST)); + break; + + case JOB_ASSASSIN: + iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_DX)) / 3; + break; + + case JOB_SHAMAN: + iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_IQ)) / 3; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + iStatAtk = (2 * GetPoint(POINT_ST)); + break; +#endif + default: + sys_err("invalid job %d", GetJob()); + iStatAtk = (2 * GetPoint(POINT_ST)); + break; + } + + if (GetMountVnum() && iStatAtk < 2 * GetPoint(POINT_ST)) + iStatAtk = (2 * GetPoint(POINT_ST)); + + iAtk += iStatAtk; + + if (GetMountVnum()) + { + if (GetJob() == JOB_SURA && GetSkillGroup() == 1) + { + iAtk += (iAtk * GetHorseLevel()) / 60; + } + else + { + iAtk += (iAtk * GetHorseLevel()) / 30; + } + } + + // ATK Setting + + iAtk += GetPoint(POINT_ATT_GRADE_BONUS); + + PointChange(POINT_ATT_GRADE, iAtk); + + // DEF = LEV + CON + ARMOR + int iShowDef = GetLevel() + GetPoint(POINT_HT); + int iDef = GetLevel() + (int) (GetPoint(POINT_HT) / 1.25); // For Other + int iArmor = 0; + + LPITEM pkItem; + + for (int i = 0; i < WEAR_MAX_NUM; ++i) + if ((pkItem = GetWear(i)) && pkItem->GetType() == ITEM_ARMOR) + { + if (pkItem->GetSubType() == ARMOR_BODY || pkItem->GetSubType() == ARMOR_HEAD || pkItem->GetSubType() == ARMOR_FOOTS || pkItem->GetSubType() == ARMOR_SHIELD) + { + iArmor += pkItem->GetValue(1); + iArmor += (2 * pkItem->GetValue(5)); + } + } + + if( true == IsHorseRiding() ) + { + if (iArmor < GetHorseArmor()) + iArmor = GetHorseArmor(); + + const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID()); + + if (pHorseName != NULL && strlen(pHorseName)) + { + iArmor += 20; + } + } + + iArmor += GetPoint(POINT_DEF_GRADE_BONUS); + iArmor += GetPoint(POINT_PARTY_DEFENDER_BONUS); + + // INTERNATIONAL_VERSION + PointChange(POINT_DEF_GRADE, iDef + iArmor); + PointChange(POINT_CLIENT_DEF_GRADE, (iShowDef + iArmor) - GetPoint(POINT_DEF_GRADE)); + // END_OF_INTERNATIONAL_VERSION + + PointChange(POINT_MAGIC_ATT_GRADE, GetLevel() * 2 + GetPoint(POINT_IQ) * 2 + GetPoint(POINT_MAGIC_ATT_GRADE_BONUS)); + PointChange(POINT_MAGIC_DEF_GRADE, GetLevel() + (GetPoint(POINT_IQ) * 3 + GetPoint(POINT_HT)) / 3 + iArmor / 2 + GetPoint(POINT_MAGIC_DEF_GRADE_BONUS)); + } + else + { + // 2lev + str * 2 + int iAtt = GetLevel() * 2 + GetPoint(POINT_ST) * 2; + // lev + con + int iDef = GetLevel() + GetPoint(POINT_HT) + GetMobTable().wDef; + + SetPoint(POINT_ATT_GRADE, iAtt); + SetPoint(POINT_DEF_GRADE, iDef); + SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE)); + SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE)); + } +} + +void CHARACTER::ComputePoints() +{ + long lStat = GetPoint(POINT_STAT); + long lStatResetCount = GetPoint(POINT_STAT_RESET_COUNT); + long lSkillActive = GetPoint(POINT_SKILL); + long lSkillSub = GetPoint(POINT_SUB_SKILL); + long lSkillHorse = GetPoint(POINT_HORSE_SKILL); + long lLevelStep = GetPoint(POINT_LEVEL_STEP); + + long lAttackerBonus = GetPoint(POINT_PARTY_ATTACKER_BONUS); + long lTankerBonus = GetPoint(POINT_PARTY_TANKER_BONUS); + long lBufferBonus = GetPoint(POINT_PARTY_BUFFER_BONUS); + long lSkillMasterBonus = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS); + long lHasteBonus = GetPoint(POINT_PARTY_HASTE_BONUS); + long lDefenderBonus = GetPoint(POINT_PARTY_DEFENDER_BONUS); + + long lHPRecovery = GetPoint(POINT_HP_RECOVERY); + long lSPRecovery = GetPoint(POINT_SP_RECOVERY); +#ifdef ENABLE_CHEQUE_SYSTEM + int iCheque = GetPoint(POINT_CHEQUE); +#endif + memset(m_pointsInstant.points, 0, sizeof(m_pointsInstant.points)); + BuffOnAttr_ClearAll(); + m_SkillDamageBonus.clear(); + + SetPoint(POINT_STAT, lStat); + SetPoint(POINT_SKILL, lSkillActive); + SetPoint(POINT_SUB_SKILL, lSkillSub); + SetPoint(POINT_HORSE_SKILL, lSkillHorse); + SetPoint(POINT_LEVEL_STEP, lLevelStep); + SetPoint(POINT_STAT_RESET_COUNT, lStatResetCount); + + SetPoint(POINT_ST, GetRealPoint(POINT_ST)); + SetPoint(POINT_HT, GetRealPoint(POINT_HT)); + SetPoint(POINT_DX, GetRealPoint(POINT_DX)); + SetPoint(POINT_IQ, GetRealPoint(POINT_IQ)); + + SetPart(PART_MAIN, GetOriginalPart(PART_MAIN)); + SetPart(PART_WEAPON, GetOriginalPart(PART_WEAPON)); + SetPart(PART_HEAD, GetOriginalPart(PART_HEAD)); + SetPart(PART_HAIR, GetOriginalPart(PART_HAIR)); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + SetPart(PART_ACCE, GetOriginalPart(PART_ACCE)); +#endif + + SetPoint(POINT_PARTY_ATTACKER_BONUS, lAttackerBonus); + SetPoint(POINT_PARTY_TANKER_BONUS, lTankerBonus); + SetPoint(POINT_PARTY_BUFFER_BONUS, lBufferBonus); + SetPoint(POINT_PARTY_SKILL_MASTER_BONUS, lSkillMasterBonus); + SetPoint(POINT_PARTY_HASTE_BONUS, lHasteBonus); + SetPoint(POINT_PARTY_DEFENDER_BONUS, lDefenderBonus); + + SetPoint(POINT_HP_RECOVERY, lHPRecovery); + SetPoint(POINT_SP_RECOVERY, lSPRecovery); +#ifdef ENABLE_CHEQUE_SYSTEM + SetPoint(POINT_CHEQUE, iCheque); +#endif + int iMaxHP, iMaxSP; + int iMaxStamina; + + if (IsPC()) + { + iMaxHP = JobInitialPoints[GetJob()].max_hp + m_points.iRandomHP + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].hp_per_ht; + iMaxSP = JobInitialPoints[GetJob()].max_sp + m_points.iRandomSP + GetPoint(POINT_IQ) * JobInitialPoints[GetJob()].sp_per_iq; + iMaxStamina = JobInitialPoints[GetJob()].max_stamina + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].stamina_per_con; + + { + CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_ADD_HP); + + if (NULL != pkSk) + { + pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_ADD_HP) / 100.0f); + + iMaxHP += static_cast(pkSk->kPointPoly.Eval()); + } + } + + SetPoint(POINT_MOV_SPEED, 100); + SetPoint(POINT_ATT_SPEED, 100); + PointChange(POINT_ATT_SPEED, GetPoint(POINT_PARTY_HASTE_BONUS)); + SetPoint(POINT_CASTING_SPEED, 100); + } + else + { + iMaxHP = m_pkMobData->m_table.dwMaxHP; + iMaxSP = 0; + iMaxStamina = 0; + + SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed); + SetPoint(POINT_MOV_SPEED, m_pkMobData->m_table.sMovingSpeed); + SetPoint(POINT_CASTING_SPEED, m_pkMobData->m_table.sAttackSpeed); + } + + if (IsPC()) + { + if (GetMountVnum()) + { + if (GetHorseST() > GetPoint(POINT_ST)) + PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST)); + + if (GetHorseDX() > GetPoint(POINT_DX)) + PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX)); + + if (GetHorseHT() > GetPoint(POINT_HT)) + PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT)); + + if (GetHorseIQ() > GetPoint(POINT_IQ)) + PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ)); + } + + } + + ComputeBattlePoints(); + + if (iMaxHP != GetMaxHP()) + { + SetRealPoint(POINT_MAX_HP, iMaxHP); + } + + PointChange(POINT_MAX_HP, 0); + + if (iMaxSP != GetMaxSP()) + { + SetRealPoint(POINT_MAX_SP, iMaxSP); + } + + PointChange(POINT_MAX_SP, 0); + + SetMaxStamina(iMaxStamina); + // @fixme118 part1 + int iCurHP = this->GetHP(); + int iCurSP = this->GetSP(); + + m_pointsInstant.dwImmuneFlag = 0; + + if (IsPC()) + { + for (int i = 0 ; i < WEAR_MAX_NUM; i++) + { + LPITEM pItem = GetWear(i); + if (pItem) + { + pItem->ModifyPoints(true); + SET_BIT(m_pointsInstant.dwImmuneFlag, GetWear(i)->GetImmuneFlag()); + } + } + + if (DragonSoul_IsDeckActivated()) + { + for (int i = WEAR_MAX_NUM + DS_SLOT_MAX * DragonSoul_GetActiveDeck(); + i < WEAR_MAX_NUM + DS_SLOT_MAX * (DragonSoul_GetActiveDeck() + 1); i++) + { + LPITEM pItem = GetWear(i); + if (pItem) + { + if (DSManager::instance().IsTimeLeftDragonSoul(pItem)) + pItem->ModifyPoints(true); + } + } + } + } + + if (GetHP() > GetMaxHP()) + PointChange(POINT_HP, GetMaxHP() - GetHP()); + + if (GetSP() > GetMaxSP()) + PointChange(POINT_SP, GetMaxSP() - GetSP()); + + ComputeSkillPoints(); + + RefreshAffect(); + + if (IsPC()) + { + CPetSystem * pPetSystem = GetPetSystem(); + if (pPetSystem) + pPetSystem->RefreshBuff(); + + // @fixme118 part2 (after petsystem stuff) + if (this->GetHP() != iCurHP) + this->PointChange(POINT_HP, iCurHP-this->GetHP()); + if (this->GetSP() != iCurSP) + this->PointChange(POINT_SP, iCurSP-this->GetSP()); + } + + UpdatePacket(); +} + +void CHARACTER::ResetPlayTime(DWORD dwTimeRemain) +{ + m_dwPlayStartTime = get_dword_time() - dwTimeRemain; +} + +const int aiRecoveryPercents[10] = { 1, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + +EVENTFUNC(recovery_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "recovery_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->IsPC()) + { + if (ch->IsAffectFlag(AFF_POISON)) + return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle)); +#ifdef ENABLE_WOLFMAN_CHARACTER + if (ch->IsAffectFlag(AFF_BLEEDING)) + return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle)); +#endif + if (2493 == ch->GetMobTable().dwVnum) + { + int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct()); + regenPct += ch->GetMobTable().bRegenPercent; + + for (int i=1 ; i <= 4 ; ++i) + { + if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID ); + + regenPct += (val*cnt); + + break; + } + } + + ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * regenPct) / 100)); + } + else if (!ch->IsDoor()) + { + ch->MonsterLog("HP_REGEN +%d", MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100)); + ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100)); + } + + if (ch->GetHP() >= ch->GetMaxHP()) + { + ch->m_pkRecoveryEvent = NULL; + return 0; + } + + if (2493 == ch->GetMobTable().dwVnum) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID ); + + return PASSES_PER_SEC(MAX(1, (ch->GetMobTable().bRegenCycle - (val*cnt)))); + } + } + } + + return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle)); + } + else + { + ch->CheckTarget(); + ch->UpdateKillerMode(); + + if (ch->IsAffectFlag(AFF_POISON) == true) + { + return 3; + } +#ifdef ENABLE_WOLFMAN_CHARACTER + if (ch->IsAffectFlag(AFF_BLEEDING)) + return 3; +#endif + int iSec = (get_dword_time() - ch->GetLastMoveTime()) / 3000; + + ch->DistributeSP(ch); + + if (ch->GetMaxHP() <= ch->GetHP()) + return PASSES_PER_SEC(3); + + int iPercent = 0; + int iAmount = 0; + + { + iPercent = aiRecoveryPercents[MIN(9, iSec)]; + iAmount = 15 + (ch->GetMaxHP() * iPercent) / 100; + } + + iAmount += (iAmount * ch->GetPoint(POINT_HP_REGEN)) / 100; + + sys_log(1, "RECOVERY_EVENT: %s %d HP_REGEN %d HP +%d", ch->GetName(), iPercent, ch->GetPoint(POINT_HP_REGEN), iAmount); + + ch->PointChange(POINT_HP, iAmount, false); + return PASSES_PER_SEC(3); + } +} + +void CHARACTER::StartRecoveryEvent() +{ + if (m_pkRecoveryEvent) + return; + + if (IsDead() || IsStun()) + return; + + if (IsNPC() && GetHP() >= GetMaxHP()) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + int iSec = IsPC() ? 3 : (MAX(1, GetMobTable().bRegenCycle)); + m_pkRecoveryEvent = event_create(recovery_event, info, PASSES_PER_SEC(iSec)); +} + +void CHARACTER::Standup() +{ + struct packet_position pack_position; + + if (!IsPosition(POS_SITTING)) + return; + + SetPosition(POS_STANDING); + + sys_log(1, "STANDUP: %s", GetName()); + + pack_position.header = HEADER_GC_CHARACTER_POSITION; + pack_position.vid = GetVID(); + pack_position.position = POSITION_GENERAL; + + PacketAround(pack_position); +} + +void CHARACTER::Sitdown(int is_ground) +{ + struct packet_position pack_position; + + if (IsPosition(POS_SITTING)) + return; + + SetPosition(POS_SITTING); + sys_log(1, "SITDOWN: %s", GetName()); + + pack_position.header = HEADER_GC_CHARACTER_POSITION; + pack_position.vid = GetVID(); + pack_position.position = POSITION_SITTING_GROUND; + PacketAround(pack_position); +} + +void CHARACTER::SetRotation(float fRot) +{ + m_pointsInstant.fRot = fRot; +} + +void CHARACTER::SetRotationToXY(long x, long y) +{ + SetRotation(GetDegreeFromPositionXY(GetX(), GetY(), x, y)); +} + +bool CHARACTER::CannotMoveByAffect() const +{ + return (IsAffectFlag(AFF_STUN)); +} + +bool CHARACTER::CanMove() const +{ + if (CannotMoveByAffect()) + return false; + + if (GetMyShop()) + return false; + + return true; +} + +bool CHARACTER::Sync(long x, long y) +{ + if (!GetSectree()) + return false; + + LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y); + + if (!new_tree) + { + if (GetDesc()) + { + sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName()); + // @fixme313 BEGIN + new_tree = GetSectree(); + x = GetX(); + y = GetY(); + if (!new_tree) + { + GetDesc()->SetPhase(PHASE_CLOSE); + sys_err("cannot find old tree at %d %d (name: %s)", x, y, GetName()); + } + // @fixme313 END + } + else + { + sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex()); + Dead(); + } + + return false; + } + + SetRotationToXY(x, y); + SetXYZ(x, y, 0); + + if (GetDungeon()) + { + int iLastEventAttr = m_iEventAttr; + m_iEventAttr = new_tree->GetEventAttribute(x, y); + + if (m_iEventAttr != iLastEventAttr) + { + if (GetParty()) + { + quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr); + quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr); + } + else + { + quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr); + quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr); + } + } + } + + if (GetSectree() != new_tree) + { + if (!IsNPC()) + { + SECTREEID id = new_tree->GetID(); + SECTREEID old_id = GetSectree()->GetID(); + + const float fDist = DISTANCE_SQRT(id.coord.x - old_id.coord.x, id.coord.y - old_id.coord.y); + sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d dist %.1fm", + GetName(), + id.coord.x, + id.coord.y, + old_id.coord.x, + old_id.coord.y, + fDist); + } + + new_tree->InsertEntity(this); + } + + return true; +} + +void CHARACTER::Stop() +{ + if (!IsState(m_stateIdle)) + MonsterLog("[IDLE] "); + + GotoState(m_stateIdle); + + m_posDest.x = m_posStart.x = GetX(); + m_posDest.y = m_posStart.y = GetY(); +} + +bool CHARACTER::Goto(long x, long y) +{ + if (GetX() == x && GetY() == y) + return false; + + if (m_posDest.x == x && m_posDest.y == y) + { + if (!IsState(m_stateMove)) + { + m_dwStateDuration = 4; + GotoState(m_stateMove); + } + return false; + } + + m_posDest.x = x; + m_posDest.y = y; + + CalculateMoveDuration(); + + m_dwStateDuration = 4; + + if (!IsState(m_stateMove)) + { + MonsterLog("[MOVE] %s", GetVictim() ? "" : "׳̵"); + + if (GetVictim()) + { + MonsterChat(MONSTER_CHAT_ATTACK); + } + } + + GotoState(m_stateMove); + + return true; +} + +DWORD CHARACTER::GetMotionMode() const +{ + DWORD dwMode = MOTION_MODE_GENERAL; + + if (IsPolymorphed()) + return dwMode; + + LPITEM pkItem; + + if ((pkItem = GetWear(WEAR_WEAPON))) + { + switch (pkItem->GetProto()->bSubType) + { + case WEAPON_SWORD: + dwMode = MOTION_MODE_ONEHAND_SWORD; + break; + + case WEAPON_TWO_HANDED: + dwMode = MOTION_MODE_TWOHAND_SWORD; + break; + + case WEAPON_DAGGER: + dwMode = MOTION_MODE_DUALHAND_SWORD; + break; + + case WEAPON_BOW: + dwMode = MOTION_MODE_BOW; + break; + + case WEAPON_BELL: + dwMode = MOTION_MODE_BELL; + break; + + case WEAPON_FAN: + dwMode = MOTION_MODE_FAN; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case WEAPON_CLAW: + dwMode = MOTION_MODE_CLAW; + break; +#endif + } + } + return dwMode; +} + +float CHARACTER::GetMoveMotionSpeed() const +{ + DWORD dwMode = GetMotionMode(); + + const CMotion * pkMotion = NULL; + + if (!GetMountVnum()) + pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(dwMode, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN)); + else + { + pkMotion = CMotionManager::instance().GetMotion(GetMountVnum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN)); + + if (!pkMotion) + pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_HORSE, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN)); + } + + if (pkMotion) + return -pkMotion->GetAccumVector().y / pkMotion->GetDuration(); + else + { + sys_err("cannot find motion (name %s race %d mode %d)", GetName(), GetRaceNum(), dwMode); + return 300.0f; + } +} + +float CHARACTER::GetMoveSpeed() const +{ + return GetMoveMotionSpeed() * 10000 / CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), 10000); +} + +void CHARACTER::CalculateMoveDuration() +{ + m_posStart.x = GetX(); + m_posStart.y = GetY(); + + float fDist = DISTANCE_SQRT(m_posStart.x - m_posDest.x, m_posStart.y - m_posDest.y); + + float motionSpeed = GetMoveMotionSpeed(); + + m_dwMoveDuration = CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), + (int) ((fDist / motionSpeed) * 1000.0f)); + + if (IsNPC()) + sys_log(1, "%s: GOTO: distance %f, spd %u, duration %u, motion speed %f pos %d %d -> %d %d", + GetName(), fDist, GetLimitPoint(POINT_MOV_SPEED), m_dwMoveDuration, motionSpeed, + m_posStart.x, m_posStart.y, m_posDest.x, m_posDest.y); + + m_dwMoveStartTime = get_dword_time(); +} + +bool CHARACTER::Move(long x, long y) +{ + if (GetX() == x && GetY() == y) + return true; + + if (test_server) + if (m_bDetailLog) + sys_log(0, "%s position %u %u", GetName(), x, y); + + OnMove(); + return Sync(x, y); +} + +void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot) +{ + TPacketGCMove pack; + + if (bFunc == FUNC_WAIT) + { + x = m_posDest.x; + y = m_posDest.y; + dwDuration = m_dwMoveDuration; + } + + EncodeMovePacket(pack, GetVID(), bFunc, bArg, x, y, dwDuration, dwTime, iRot == -1 ? (int) GetRotation() / 5 : iRot); + PacketView(&pack, sizeof(TPacketGCMove), this); +} + +int CHARACTER::GetRealPoint(BYTE type) const +{ + return m_points.points[type]; +} + +void CHARACTER::SetRealPoint(BYTE type, int val) +{ + m_points.points[type] = val; +} + +int CHARACTER::GetPolymorphPoint(BYTE type) const +{ + if (IsPolymorphed() && !IsPolyMaintainStat()) + { + DWORD dwMobVnum = GetPolymorphVnum(); + const CMob * pMob = CMobManager::instance().Get(dwMobVnum); + int iPower = GetPolymorphPower(); + + if (pMob) + { + switch (type) + { + case POINT_ST: + if ((GetJob() == JOB_SHAMAN) || ((GetJob() == JOB_SURA) && (GetSkillGroup() == 2))) + return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_IQ); + return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_ST); + + case POINT_HT: + return pMob->m_table.bCon * iPower / 100 + GetPoint(POINT_HT); + + case POINT_IQ: + return pMob->m_table.bInt * iPower / 100 + GetPoint(POINT_IQ); + + case POINT_DX: + return pMob->m_table.bDex * iPower / 100 + GetPoint(POINT_DX); + } + } + } + + return GetPoint(type); +} + +int CHARACTER::GetPoint(BYTE type) const +{ + if (type >= POINT_MAX_NUM) + { + sys_err("Point type overflow (type %u)", type); + return 0; + } + + int val = m_pointsInstant.points[type]; + int max_val = INT_MAX; + + switch (type) + { + case POINT_STEAL_HP: + case POINT_STEAL_SP: + max_val = 50; + break; + } + + if (val > max_val) + sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val); + + return (val); +} + +int CHARACTER::GetLimitPoint(BYTE type) const +{ + if (type >= POINT_MAX_NUM) + { + sys_err("Point type overflow (type %u)", type); + return 0; + } + + int val = m_pointsInstant.points[type]; + int max_val = INT_MAX; + int limit = INT_MAX; + int min_limit = -INT_MAX; + + switch (type) + { + case POINT_ATT_SPEED: + min_limit = 0; + + if (IsPC()) + limit = 170; + else + limit = 250; + break; + + case POINT_MOV_SPEED: + min_limit = 0; + + if (IsPC()) + limit = 200; + else + limit = 250; + break; + + case POINT_STEAL_HP: + case POINT_STEAL_SP: + limit = 50; + max_val = 50; + break; + + case POINT_MALL_ATTBONUS: + case POINT_MALL_DEFBONUS: + limit = 20; + max_val = 50; + break; + } + + if (val > max_val) + sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val); + + if (val > limit) + val = limit; + + if (val < min_limit) + val = min_limit; + + return (val); +} + +void CHARACTER::SetPoint(BYTE type, int val) +{ + if (type >= POINT_MAX_NUM) + { + sys_err("Point type overflow (type %u)", type); + return; + } + + m_pointsInstant.points[type] = val; + + if (type == POINT_MOV_SPEED && get_dword_time() < m_dwMoveStartTime + m_dwMoveDuration) + { + CalculateMoveDuration(); + } +} + +INT CHARACTER::GetAllowedGold() const +{ + if (GetLevel() <= 10) + return 100000; + else if (GetLevel() <= 20) + return 500000; + else + return 50000000; +} + +void CHARACTER::CheckMaximumPoints() +{ + if (GetMaxHP() < GetHP()) + PointChange(POINT_HP, GetMaxHP() - GetHP()); + + if (GetMaxSP() < GetSP()) + PointChange(POINT_SP, GetMaxSP() - GetSP()); +} + +void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast) +{ + int val = 0; + + //sys_log(0, "PointChange %d %d | %d -> %d cHP %d mHP %d", type, amount, GetPoint(type), GetPoint(type)+amount, GetHP(), GetMaxHP()); + + switch (type) + { + case POINT_NONE: + return; + + case POINT_LEVEL: + if ((GetLevel() + amount) > gPlayerMaxLevel) + return; + + SetLevel(GetLevel() + amount); + val = GetLevel(); + + sys_log(0, "LEVELUP: %s %d NEXT EXP %d", GetName(), GetLevel(), GetNextExp()); +#ifdef ENABLE_WOLFMAN_CHARACTER + if (GetJob() == JOB_WOLFMAN) + { + if ((5 <= val) && (GetSkillGroup()!=1)) + { + ClearSkill(); + // set skill group + SetSkillGroup(1); + // set skill points + SetRealPoint(POINT_SKILL, GetLevel()-1); + SetPoint(POINT_SKILL, GetRealPoint(POINT_SKILL)); + PointChange(POINT_SKILL, 0); + // update points (not required) + // ComputePoints(); + // PointsPacket(); + } + } +#endif + PointChange(POINT_NEXT_EXP, GetNextExp(), false); + + if (amount) + { + quest::CQuestManager::instance().LevelUp(GetPlayerID()); + + LogManager::instance().LevelLog(this, val, GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000); + + if (GetGuild()) + { + GetGuild()->LevelChange(GetPlayerID(), GetLevel()); + } + + if (GetParty()) + { + GetParty()->RequestSetMemberLevel(GetPlayerID(), GetLevel()); + } + } + break; + + case POINT_NEXT_EXP: + val = GetNextExp(); + bAmount = false; + break; + + case POINT_EXP: + { + DWORD exp = GetExp(); + DWORD next_exp = GetNextExp(); + + if (g_bChinaIntoxicationCheck) + { + if (IsOverTime(OT_NONE)) + { + } + else if (IsOverTime(OT_3HOUR)) + { + amount = (amount / 2); + } + else if (IsOverTime(OT_5HOUR)) + { + amount = 0; + } + } + + if ((amount < 0) && (exp < (DWORD)(-amount))) + { + sys_log(1, "%s AMOUNT < 0 %d, CUR EXP: %d", GetName(), -amount, exp); + amount = -exp; + + SetExp(exp + amount); + val = GetExp(); + } + else + { + if (gPlayerMaxLevel <= GetLevel()) + return; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "You have gained %d exp.", amount); + + DWORD iExpBalance = 0; + + if (exp + amount >= next_exp) + { + iExpBalance = (exp + amount) - next_exp; + amount = next_exp - exp; + + SetExp(0); + exp = next_exp; + } + else + { + SetExp(exp + amount); + exp = GetExp(); + } + + DWORD q = DWORD(next_exp / 4.0f); + int iLevStep = GetRealPoint(POINT_LEVEL_STEP); + + if (iLevStep >= 4) + { + sys_err("%s LEVEL_STEP bigger than 4! (%d)", GetName(), iLevStep); + iLevStep = 4; + } + + if (exp >= next_exp && iLevStep < 4) + { + for (int i = 0; i < 4 - iLevStep; ++i) + PointChange(POINT_LEVEL_STEP, 1, false, true); + } + else if (exp >= q * 3 && iLevStep < 3) + { + for (int i = 0; i < 3 - iLevStep; ++i) + PointChange(POINT_LEVEL_STEP, 1, false, true); + } + else if (exp >= q * 2 && iLevStep < 2) + { + for (int i = 0; i < 2 - iLevStep; ++i) + PointChange(POINT_LEVEL_STEP, 1, false, true); + } + else if (exp >= q && iLevStep < 1) + PointChange(POINT_LEVEL_STEP, 1); + + if (iExpBalance) + { + PointChange(POINT_EXP, iExpBalance); + } + + val = GetExp(); + } + } + break; + + case POINT_LEVEL_STEP: + if (amount > 0) + { + val = GetPoint(POINT_LEVEL_STEP) + amount; + + switch (val) + { + case 1: + case 2: + case 3: + if ((GetLevel() <= g_iStatusPointGetLevelLimit) && + (GetLevel() <= gPlayerMaxLevel) ) // @fixme104 + PointChange(POINT_STAT, 1); + break; + + case 4: + { + int iHP = number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end); + int iSP = number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end); + + m_points.iRandomHP += iHP; + m_points.iRandomSP += iSP; + + if (GetSkillGroup()) + { + if (GetLevel() >= 5) + PointChange(POINT_SKILL, 1); + + if (GetLevel() >= 9) + PointChange(POINT_SUB_SKILL, 1); + } + + PointChange(POINT_MAX_HP, iHP); + PointChange(POINT_MAX_SP, iSP); + PointChange(POINT_LEVEL, 1, false, true); + + val = 0; + } + break; + } + + if (GetLevel() <= 10) + AutoGiveItem(27001, 2); + else if (GetLevel() <= 30) + AutoGiveItem(27002, 2); + else + { + AutoGiveItem(27002, 2); +// AutoGiveItem(27003, 2); + } + + PointChange(POINT_HP, GetMaxHP() - GetHP()); + PointChange(POINT_SP, GetMaxSP() - GetSP()); + PointChange(POINT_STAMINA, GetMaxStamina() - GetStamina()); + + SetPoint(POINT_LEVEL_STEP, val); + SetRealPoint(POINT_LEVEL_STEP, val); + + Save(); + } + else + val = GetPoint(POINT_LEVEL_STEP); + + break; + + case POINT_HP: + { + if (IsDead() || IsStun()) + return; + + int prev_hp = GetHP(); + + amount = MIN(GetMaxHP() - GetHP(), amount); + SetHP(GetHP() + amount); + val = GetHP(); + + BroadcastTargetPacket(); + + if (GetParty() && IsPC() && val != prev_hp) + GetParty()->SendPartyInfoOneToAll(this); + } + break; + + case POINT_SP: + { + if (IsDead() || IsStun()) + return; + + amount = MIN(GetMaxSP() - GetSP(), amount); + SetSP(GetSP() + amount); + val = GetSP(); + } + break; + + case POINT_STAMINA: + { + if (IsDead() || IsStun()) + return; + + int prev_val = GetStamina(); + amount = MIN(GetMaxStamina() - GetStamina(), amount); + SetStamina(GetStamina() + amount); + val = GetStamina(); + + if (val == 0) + { + // Stamina + SetNowWalking(true); + } + else if (prev_val == 0) + { + ResetWalking(); + } + + if (amount < 0 && val != 0) + return; + } + break; + + case POINT_MAX_HP: + { + SetPoint(type, GetPoint(type) + amount); + + //SetMaxHP(GetMaxHP() + amount); + int hp = GetRealPoint(POINT_MAX_HP); + int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100); + add_hp += GetPoint(POINT_MAX_HP); + add_hp += GetPoint(POINT_PARTY_TANKER_BONUS); + + SetMaxHP(hp + add_hp); + + val = GetMaxHP(); + } + break; + + case POINT_MAX_SP: + { + SetPoint(type, GetPoint(type) + amount); + + //SetMaxSP(GetMaxSP() + amount); + int sp = GetRealPoint(POINT_MAX_SP); + int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100); + add_sp += GetPoint(POINT_MAX_SP); + add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS); + + SetMaxSP(sp + add_sp); + + val = GetMaxSP(); + } + break; + + case POINT_MAX_HP_PCT: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + + PointChange(POINT_MAX_HP, 0); + break; + + case POINT_MAX_SP_PCT: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + + PointChange(POINT_MAX_SP, 0); + break; + + case POINT_MAX_STAMINA: + SetMaxStamina(GetMaxStamina() + amount); + val = GetMaxStamina(); + break; + +#ifdef ENABLE_CHEQUE_SYSTEM + case POINT_CHEQUE: + { + const int64_t nTotalCheque = static_cast(GetCheque()) + static_cast(amount); + if (CHEQUE_MAX <= nTotalCheque) + { + sys_err("[OVERFLOW_CHEQUE] OriCheque %d AddedCheque %d id %u Name %s ", GetCheque(), amount, GetPlayerID(), GetName()); + LogManager::instance().CharLog(this, GetCheque() + amount, "OVERFLOW_CHEQUE", ""); + return; + } + SetCheque(GetCheque() + amount); + val = GetCheque(); + } + break; +#endif + + case POINT_GOLD: + { + const int64_t nTotalMoney = static_cast(GetGold()) + static_cast(amount); + + if (GOLD_MAX <= nTotalMoney) + { + sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName()); + LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", ""); + return; + } + + if (g_bChinaIntoxicationCheck && amount > 0) + { + if (IsOverTime(OT_NONE)) + { + sys_log(1, " %s = NONE", GetName()); + } + else if (IsOverTime(OT_3HOUR)) + { + amount = (amount / 2); + sys_log(1, " %s = 3HOUR", GetName()); + } + else if (IsOverTime(OT_5HOUR)) + { + amount = 0; + sys_log(1, " %s = 5HOUR", GetName()); + } + } + + SetGold(GetGold() + amount); + val = GetGold(); + } + break; + + case POINT_SKILL: + case POINT_STAT: + case POINT_SUB_SKILL: + case POINT_STAT_RESET_COUNT: + case POINT_HORSE_SKILL: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + + SetRealPoint(type, val); + break; + + case POINT_DEF_GRADE: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + + PointChange(POINT_CLIENT_DEF_GRADE, amount); + break; + + case POINT_CLIENT_DEF_GRADE: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + + case POINT_ST: + case POINT_HT: + case POINT_DX: + case POINT_IQ: + case POINT_HP_REGEN: + case POINT_SP_REGEN: + case POINT_ATT_SPEED: + case POINT_ATT_GRADE: + case POINT_MOV_SPEED: + case POINT_CASTING_SPEED: + case POINT_MAGIC_ATT_GRADE: + case POINT_MAGIC_DEF_GRADE: + case POINT_BOW_DISTANCE: + case POINT_HP_RECOVERY: + case POINT_SP_RECOVERY: + + case POINT_ATTBONUS_HUMAN: // 42 + case POINT_ATTBONUS_ANIMAL: // 43 + case POINT_ATTBONUS_ORC: // 44 + case POINT_ATTBONUS_MILGYO: // 45 + case POINT_ATTBONUS_UNDEAD: // 46 + case POINT_ATTBONUS_DEVIL: // 47 + + case POINT_ATTBONUS_MONSTER: + case POINT_ATTBONUS_SURA: + case POINT_ATTBONUS_ASSASSIN: + case POINT_ATTBONUS_WARRIOR: + case POINT_ATTBONUS_SHAMAN: +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_ATTBONUS_WOLFMAN: +#endif + + case POINT_POISON_PCT: +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_BLEEDING_PCT: +#endif + case POINT_STUN_PCT: + case POINT_SLOW_PCT: + + case POINT_BLOCK: + case POINT_DODGE: + + case POINT_CRITICAL_PCT: + case POINT_RESIST_CRITICAL: + case POINT_PENETRATE_PCT: + case POINT_RESIST_PENETRATE: + case POINT_CURSE_PCT: + + case POINT_STEAL_HP: // 48 + case POINT_STEAL_SP: // 49 + + case POINT_MANA_BURN_PCT: // 50 + case POINT_DAMAGE_SP_RECOVER: // 51 + case POINT_RESIST_NORMAL_DAMAGE: + case POINT_RESIST_SWORD: + case POINT_RESIST_TWOHAND: + case POINT_RESIST_DAGGER: + case POINT_RESIST_BELL: + case POINT_RESIST_FAN: + case POINT_RESIST_BOW: +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_RESIST_CLAW: +#endif + case POINT_RESIST_FIRE: + case POINT_RESIST_ELEC: + case POINT_RESIST_MAGIC: + case POINT_RESIST_WIND: + case POINT_RESIST_ICE: + case POINT_RESIST_EARTH: + case POINT_RESIST_DARK: + case POINT_REFLECT_MELEE: // 67 + case POINT_REFLECT_CURSE: // 68 + case POINT_POISON_REDUCE: // 69 +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_BLEEDING_REDUCE: +#endif + case POINT_KILL_SP_RECOVER: // 70 + case POINT_KILL_HP_RECOVERY: // 75 + case POINT_HIT_HP_RECOVERY: + case POINT_HIT_SP_RECOVERY: + case POINT_MANASHIELD: + case POINT_ATT_BONUS: + case POINT_DEF_BONUS: + case POINT_SKILL_DAMAGE_BONUS: + case POINT_NORMAL_HIT_DAMAGE_BONUS: + + // DEPEND_BONUS_ATTRIBUTES + case POINT_SKILL_DEFEND_BONUS: + case POINT_NORMAL_HIT_DEFEND_BONUS: +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + case POINT_ACCEDRAIN_RATE: +#endif +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + case POINT_RESIST_MAGIC_REDUCTION: +#endif + case POINT_ENCHANT_ELECT: + case POINT_ENCHANT_FIRE: + case POINT_ENCHANT_ICE: + case POINT_ENCHANT_WIND: + case POINT_ENCHANT_EARTH: + case POINT_ENCHANT_DARK: + case POINT_ATTBONUS_CZ: + case POINT_ATTBONUS_INSECT: + case POINT_ATTBONUS_DESERT: + case POINT_ATTBONUS_SWORD: + case POINT_ATTBONUS_TWOHAND: + case POINT_ATTBONUS_DAGGER: + case POINT_ATTBONUS_BELL: + case POINT_ATTBONUS_FAN: + case POINT_ATTBONUS_BOW: +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_ATTBONUS_CLAW: +#endif + case POINT_RESIST_MOUNT_FALL: + case POINT_RESIST_HUMAN: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + // END_OF_DEPEND_BONUS_ATTRIBUTES + + case POINT_PARTY_ATTACKER_BONUS: + case POINT_PARTY_TANKER_BONUS: + case POINT_PARTY_BUFFER_BONUS: + case POINT_PARTY_SKILL_MASTER_BONUS: + case POINT_PARTY_HASTE_BONUS: + case POINT_PARTY_DEFENDER_BONUS: + + case POINT_RESIST_WARRIOR : + case POINT_RESIST_ASSASSIN : + case POINT_RESIST_SURA : + case POINT_RESIST_SHAMAN : +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_RESIST_WOLFMAN : +#endif + + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + + case POINT_MALL_ATTBONUS: + case POINT_MALL_DEFBONUS: + case POINT_MALL_EXPBONUS: + case POINT_MALL_ITEMBONUS: + case POINT_MALL_GOLDBONUS: + case POINT_MELEE_MAGIC_ATT_BONUS_PER: + if (GetPoint(type) + amount > 100) + { + sys_err("MALL_BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount); + amount = 100 - GetPoint(type); + } + + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + + case POINT_RAMADAN_CANDY_BONUS_EXP: + SetPoint(type, amount); + val = GetPoint(type); + break; + + case POINT_EXP_DOUBLE_BONUS: // 71 + case POINT_GOLD_DOUBLE_BONUS: // 72 + case POINT_ITEM_DROP_BONUS: // 73 + case POINT_POTION_BONUS: // 74 + if (GetPoint(type) + amount > 100) + { + sys_err("BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount); + amount = 100 - GetPoint(type); + } + + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + + case POINT_IMMUNE_STUN: // 76 + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + if (val) + { + // ChatPacket(CHAT_TYPE_INFO, "IMMUNE_STUN SET_BIT type(%u) amount(%d)", type, amount); + SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN); + } + else + { + // ChatPacket(CHAT_TYPE_INFO, "IMMUNE_STUN REMOVE_BIT type(%u) amount(%d)", type, amount); + REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN); + } + break; + + case POINT_IMMUNE_SLOW: // 77 + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + if (val) + { + SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW); + } + else + { + REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW); + } + break; + + case POINT_IMMUNE_FALL: // 78 + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + if (val) + { + SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL); + } + else + { + REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL); + } + break; + + case POINT_ATT_GRADE_BONUS: + SetPoint(type, GetPoint(type) + amount); + PointChange(POINT_ATT_GRADE, amount); + val = GetPoint(type); + break; + + case POINT_DEF_GRADE_BONUS: + SetPoint(type, GetPoint(type) + amount); + PointChange(POINT_DEF_GRADE, amount); + val = GetPoint(type); + break; + + case POINT_MAGIC_ATT_GRADE_BONUS: + SetPoint(type, GetPoint(type) + amount); + PointChange(POINT_MAGIC_ATT_GRADE, amount); + val = GetPoint(type); + break; + + case POINT_MAGIC_DEF_GRADE_BONUS: + SetPoint(type, GetPoint(type) + amount); + PointChange(POINT_MAGIC_DEF_GRADE, amount); + val = GetPoint(type); + break; + + case POINT_VOICE: + case POINT_EMPIRE_POINT: + //sys_err("CHARACTER::PointChange: %s: point cannot be changed. use SetPoint instead (type: %d)", GetName(), type); + val = GetRealPoint(type); + break; + + case POINT_POLYMORPH: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + SetPolymorph(val); + break; + + case POINT_MOUNT: + SetPoint(type, GetPoint(type) + amount); + val = GetPoint(type); + break; + + case POINT_ENERGY: + case POINT_COSTUME_ATTR_BONUS: + { + int old_val = GetPoint(type); + SetPoint(type, old_val + amount); + val = GetPoint(type); + BuffOnAttr_ValueChange(type, old_val, val); + } + break; + + default: + sys_err("CHARACTER::PointChange: %s: unknown point change type %d", GetName(), type); + return; + } + + switch (type) + { + case POINT_LEVEL: + case POINT_ST: + case POINT_DX: + case POINT_IQ: + case POINT_HT: + ComputeBattlePoints(); + break; + case POINT_MAX_HP: + case POINT_MAX_SP: + case POINT_MAX_STAMINA: + break; + } + + if (type == POINT_HP && amount == 0) + return; + + if (GetDesc()) + { + struct packet_point_change pack; + + pack.header = HEADER_GC_CHARACTER_POINT_CHANGE; + pack.dwVID = m_vid; + pack.type = type; + pack.value = val; + + if (bAmount) + pack.amount = amount; + else + pack.amount = 0; + + if (!bBroadcast) + GetDesc()->Packet(pack); + else + PacketAround(pack); + } +} + +void CHARACTER::ApplyPoint(BYTE bApplyType, int iVal) +{ + switch (bApplyType) + { + case APPLY_NONE: // 0 + break; + + case APPLY_CON: + PointChange(POINT_HT, iVal); + PointChange(POINT_MAX_HP, (iVal * JobInitialPoints[GetJob()].hp_per_ht)); + PointChange(POINT_MAX_STAMINA, (iVal * JobInitialPoints[GetJob()].stamina_per_con)); + break; + + case APPLY_INT: + PointChange(POINT_IQ, iVal); + PointChange(POINT_MAX_SP, (iVal * JobInitialPoints[GetJob()].sp_per_iq)); + break; + + case APPLY_SKILL: + // SKILL_DAMAGE_BONUS + { + // 00000000 00000000 00000000 00000000 + + // vnum ^ add change + BYTE bSkillVnum = (BYTE) (((DWORD)iVal) >> 24); + int iAdd = iVal & 0x00800000; + int iChange = iVal & 0x007fffff; + + sys_log(1, "APPLY_SKILL skill %d add? %d change %d", bSkillVnum, iAdd ? 1 : 0, iChange); + + if (0 == iAdd) + iChange = -iChange; + + boost::unordered_map::iterator iter = m_SkillDamageBonus.find(bSkillVnum); + + if (iter == m_SkillDamageBonus.end()) + m_SkillDamageBonus.emplace(bSkillVnum, iChange); + else + iter->second += iChange; + } + // END_OF_SKILL_DAMAGE_BONUS + break; + + case APPLY_MAX_HP: + case APPLY_MAX_HP_PCT: + { + int i = GetMaxHP(); if(i == 0) break; + PointChange(aApplyInfo[bApplyType].bPointType, iVal); + float fRatio = (float)GetMaxHP() / (float)i; + PointChange(POINT_HP, GetHP() * fRatio - GetHP()); + } + break; + + case APPLY_MAX_SP: + case APPLY_MAX_SP_PCT: + { + int i = GetMaxSP(); if(i == 0) break; + PointChange(aApplyInfo[bApplyType].bPointType, iVal); + float fRatio = (float)GetMaxSP() / (float)i; + PointChange(POINT_SP, GetSP() * fRatio - GetSP()); + } + break; + + case APPLY_STR: + case APPLY_DEX: + case APPLY_ATT_SPEED: + case APPLY_MOV_SPEED: + case APPLY_CAST_SPEED: + case APPLY_HP_REGEN: + case APPLY_SP_REGEN: + case APPLY_POISON_PCT: +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_BLEEDING_PCT: +#endif + case APPLY_STUN_PCT: + case APPLY_SLOW_PCT: + case APPLY_CRITICAL_PCT: + case APPLY_PENETRATE_PCT: + case APPLY_ATTBONUS_HUMAN: + case APPLY_ATTBONUS_ANIMAL: + case APPLY_ATTBONUS_ORC: + case APPLY_ATTBONUS_MILGYO: + case APPLY_ATTBONUS_UNDEAD: + case APPLY_ATTBONUS_DEVIL: + case APPLY_ATTBONUS_WARRIOR: // 59 + case APPLY_ATTBONUS_ASSASSIN: // 60 + case APPLY_ATTBONUS_SURA: // 61 + case APPLY_ATTBONUS_SHAMAN: // 62 +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_ATTBONUS_WOLFMAN: +#endif + case APPLY_ATTBONUS_MONSTER: // 63 + case APPLY_STEAL_HP: + case APPLY_STEAL_SP: + case APPLY_MANA_BURN_PCT: + case APPLY_DAMAGE_SP_RECOVER: + case APPLY_BLOCK: + case APPLY_DODGE: + case APPLY_RESIST_SWORD: + case APPLY_RESIST_TWOHAND: + case APPLY_RESIST_DAGGER: + case APPLY_RESIST_BELL: + case APPLY_RESIST_FAN: + case APPLY_RESIST_BOW: +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_RESIST_CLAW: +#endif + case APPLY_RESIST_FIRE: + case APPLY_RESIST_ELEC: + case APPLY_RESIST_MAGIC: + case APPLY_RESIST_WIND: + case APPLY_RESIST_ICE: + case APPLY_RESIST_EARTH: + case APPLY_RESIST_DARK: + case APPLY_REFLECT_MELEE: + case APPLY_REFLECT_CURSE: + case APPLY_ANTI_CRITICAL_PCT: + case APPLY_ANTI_PENETRATE_PCT: + case APPLY_POISON_REDUCE: +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_BLEEDING_REDUCE: +#endif + case APPLY_KILL_SP_RECOVER: + case APPLY_EXP_DOUBLE_BONUS: + case APPLY_GOLD_DOUBLE_BONUS: + case APPLY_ITEM_DROP_BONUS: + case APPLY_POTION_BONUS: + case APPLY_KILL_HP_RECOVER: + case APPLY_IMMUNE_STUN: + case APPLY_IMMUNE_SLOW: + case APPLY_IMMUNE_FALL: + case APPLY_BOW_DISTANCE: + case APPLY_ATT_GRADE_BONUS: + case APPLY_DEF_GRADE_BONUS: + case APPLY_MAGIC_ATT_GRADE: + case APPLY_MAGIC_DEF_GRADE: + case APPLY_CURSE_PCT: + case APPLY_MAX_STAMINA: + case APPLY_MALL_ATTBONUS: + case APPLY_MALL_DEFBONUS: + case APPLY_MALL_EXPBONUS: + case APPLY_MALL_ITEMBONUS: + case APPLY_MALL_GOLDBONUS: + case APPLY_SKILL_DAMAGE_BONUS: + case APPLY_NORMAL_HIT_DAMAGE_BONUS: + + // DEPEND_BONUS_ATTRIBUTES + case APPLY_SKILL_DEFEND_BONUS: + case APPLY_NORMAL_HIT_DEFEND_BONUS: + // END_OF_DEPEND_BONUS_ATTRIBUTES + + case APPLY_RESIST_WARRIOR : + case APPLY_RESIST_ASSASSIN : + case APPLY_RESIST_SURA : + case APPLY_RESIST_SHAMAN : +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_RESIST_WOLFMAN : +#endif + case APPLY_ENERGY: // 82 + case APPLY_DEF_GRADE: // 83 + case APPLY_COSTUME_ATTR_BONUS: // 84 + case APPLY_MAGIC_ATTBONUS_PER: // 85 + case APPLY_MELEE_MAGIC_ATTBONUS_PER:// 86 +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + case APPLY_ACCEDRAIN_RATE: // 97 +#endif +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + case APPLY_RESIST_MAGIC_REDUCTION: // 98 +#endif + case APPLY_ENCHANT_ELECT: + case APPLY_ENCHANT_FIRE: + case APPLY_ENCHANT_ICE: + case APPLY_ENCHANT_WIND: + case APPLY_ENCHANT_EARTH: + case APPLY_ENCHANT_DARK: + case APPLY_ATTBONUS_CZ: + case APPLY_ATTBONUS_INSECT: + case APPLY_ATTBONUS_DESERT: + case APPLY_ATTBONUS_SWORD: + case APPLY_ATTBONUS_TWOHAND: + case APPLY_ATTBONUS_DAGGER: + case APPLY_ATTBONUS_BELL: + case APPLY_ATTBONUS_FAN: + case APPLY_ATTBONUS_BOW: +#ifdef ENABLE_WOLFMAN_CHARACTER + case APPLY_ATTBONUS_CLAW: +#endif + case APPLY_RESIST_MOUNT_FALL: + case APPLY_RESIST_HUMAN: + case APPLY_MOUNT: + PointChange(aApplyInfo[bApplyType].bPointType, iVal); + break; + + default: + sys_err("Unknown apply type %d name %s", bApplyType, GetName()); + break; + } +} + +void CHARACTER::MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet) +{ + packet->header = HEADER_GC_MOTION; + packet->vid = m_vid; + packet->motion = motion; + + if (victim) + packet->victim_vid = victim->GetVID(); + else + packet->victim_vid = 0; +} + +void CHARACTER::Motion(BYTE motion, LPCHARACTER victim) +{ + struct packet_motion pack_motion; + MotionPacketEncode(motion, victim, &pack_motion); + PacketAround(pack_motion); +} + +EVENTFUNC(save_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "save_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + sys_log(1, "SAVE_EVENT: %s", ch->GetName()); + ch->Save(); + ch->FlushDelayedSaveItem(); + return (save_event_second_cycle); +} + +void CHARACTER::StartSaveEvent() +{ + if (m_pkSaveEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle); +} + +void CHARACTER::MonsterLog(const char* format, ...) +{ + if (!test_server) + return; + + if (IsPC()) + return; + + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "%u)", (DWORD)GetVID()); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + va_list args; + + va_start(args, format); + + int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args); + + if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len) + len += (sizeof(chatbuf) - len) - 1; + else + len += len2; + + ++len; + + va_end(args); + + TPacketGCChat pack_chat; + + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_TALKING; + pack_chat.id = (DWORD)GetVID(); + pack_chat.bEmpire = 0; + + TEMP_BUFFER buf; + buf.write(pack_chat); + buf.write(chatbuf, len); + + CHARACTER_MANAGER::instance().PacketMonsterLog(this, buf.read_peek(), buf.size()); +} + +void CHARACTER::ChatPacket(BYTE type, const char * format, ...) +{ + LPDESC d = GetDesc(); + + if (!d || !format) + return; + + char chatbuf[CHAT_MAX_LEN + 1]; + va_list args; + + va_start(args, format); + int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args); + va_end(args); + + struct packet_chat pack_chat; + + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(struct packet_chat) + len; + pack_chat.type = type; + pack_chat.id = 0; + pack_chat.bEmpire = d->GetEmpire(); + + TEMP_BUFFER buf; + buf.write(pack_chat); + buf.write(chatbuf, len); + + d->Packet(buf.read_peek(), buf.size()); + + if (type == CHAT_TYPE_COMMAND && test_server) + sys_log(0, "SEND_COMMAND %s %s", GetName(), chatbuf); +} + +// MINING +void CHARACTER::mining_take() +{ + m_pkMiningEvent = NULL; +} + +void CHARACTER::mining_cancel() +{ + if (m_pkMiningEvent) + { + sys_log(0, "XXX MINING CANCEL"); + event_cancel(&m_pkMiningEvent); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ä ߴϿϴ.")); + } +} + +void CHARACTER::mining(LPCHARACTER chLoad) +{ + if (m_pkMiningEvent) + { + mining_cancel(); + return; + } + + if (!chLoad) + return; + + // @fixme128 + if (GetMapIndex() != chLoad->GetMapIndex() || DISTANCE_APPROX(GetX() - chLoad->GetX(), GetY() - chLoad->GetY()) > 1000) + return; + + if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0) + return; + + LPITEM pick = GetWear(WEAR_WEAPON); + + if (!pick || pick->GetType() != ITEM_PICK) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̸ ϼ.")); + return; + } + + int count = number(5, 15); + + TPacketGCDigMotion p; + p.header = HEADER_GC_DIG_MOTION; + p.vid = GetVID(); + p.target_vid = chLoad->GetVID(); + p.count = count; + + PacketAround(p); + + m_pkMiningEvent = mining::CreateMiningEvent(this, chLoad, count); +} +// END_OF_MINING + +void CHARACTER::fishing() +{ + if (m_pkFishingEvent) + { + fishing_take(); + return; + } + + { + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex()); + + int x = GetX(); + int y = GetY(); + + LPSECTREE tree = pkSectreeMap->Find(x, y); + DWORD dwAttr = tree->GetAttribute(x, y); + + if (IS_SET(dwAttr, ATTR_BLOCK)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ø ִ ƴմϴ")); + return; + } + } + + LPITEM rod = GetWear(WEAR_WEAPON); + + if (!rod || rod->GetType() != ITEM_ROD) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ô븦 ϼ.")); + return; + } + + if (0 == rod->GetSocket(2)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̳ ּ.")); + return; + } + + float fx, fy; + GetDeltaByDegree(GetRotation(), 400.0f, &fx, &fy); + + m_pkFishingEvent = fishing::CreateFishingEvent(this); +} + +void CHARACTER::fishing_take() +{ + LPITEM rod = GetWear(WEAR_WEAPON); + if (rod && rod->GetType() == ITEM_ROD) + { + using fishing::fishing_event_info; + if (m_pkFishingEvent) + { + struct fishing_event_info* info = dynamic_cast(m_pkFishingEvent->info); + + if (info) + fishing::Take(info, this); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ô밡 ƴ ø ϴ!")); + } + + event_cancel(&m_pkFishingEvent); +} + +bool CHARACTER::StartStateMachine(int iNextPulse) +{ + if (CHARACTER_MANAGER::instance().AddToStateList(this)) + { + m_dwNextStatePulse = thecore_heart->pulse + iNextPulse; + return true; + } + + return false; +} + +void CHARACTER::StopStateMachine() +{ + CHARACTER_MANAGER::instance().RemoveFromStateList(this); +} + +void CHARACTER::UpdateStateMachine(DWORD dwPulse) +{ + if (dwPulse < m_dwNextStatePulse) + return; + + if (IsDead()) + return; + + Update(); + m_dwNextStatePulse = dwPulse + m_dwStateDuration; +} + +void CHARACTER::SetNextStatePulse(int iNextPulse) +{ + CHARACTER_MANAGER::instance().AddToStateList(this); + m_dwNextStatePulse = iNextPulse; + + if (iNextPulse < 10) + MonsterLog("·ξ"); +} + +void CHARACTER::UpdateCharacter(DWORD dwPulse) +{ + CFSM::Update(); +} + +void CHARACTER::SetShop(LPSHOP pkShop) +{ + if ((m_pkShop = pkShop)) + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP); + else + { + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP); + SetShopOwner(NULL); + } +} + +void CHARACTER::SetExchange(CExchange * pkExchange) +{ + m_pkExchange = pkExchange; +} + +void CHARACTER::SetPart(BYTE bPartPos, DWORD wVal) // @fixme502 +{ + assert(bPartPos < PART_MAX_NUM); + m_pointsInstant.parts[bPartPos] = wVal; +} + +DWORD CHARACTER::GetPart(BYTE bPartPos) const // @fixme502 +{ + assert(bPartPos < PART_MAX_NUM); + return m_pointsInstant.parts[bPartPos]; +} + +DWORD CHARACTER::GetOriginalPart(BYTE bPartPos) const // @fixme502 +{ + switch (bPartPos) + { + case PART_MAIN: + if (!IsPC()) + return GetPart(PART_MAIN); + else + return m_pointsInstant.bBasePart; + + case PART_HAIR: + return GetPart(PART_HAIR); + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + case PART_ACCE: + return GetPart(PART_ACCE); +#endif + +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + case PART_WEAPON: + return GetPart(PART_WEAPON); +#endif + + default: + return 0; + } +} + +BYTE CHARACTER::GetCharType() const +{ + return m_bCharType; +} + +bool CHARACTER::SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList) +{ + // TRENT_MONSTER + if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + return false; + // END_OF_TRENT_MONSTER + + if (ch) // @fixme131 + { + if (!battle_is_attackable(ch, this)) + { + SendDamagePacket(ch, 0, DAMAGE_BLOCK); + return false; + } + } + + if (ch == this) + { + sys_err("SetSyncOwner owner == this (%p)", this); + return false; + } + + if (!ch) + { + if (bRemoveFromList && m_pkChrSyncOwner) + { + m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this); + } + + if (m_pkChrSyncOwner) + sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName()); + + m_pkChrSyncOwner = NULL; + } + else + { + if (!IsSyncOwner(ch)) + return false; + + if (DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY()) > 250) + { + sys_log(1, "SetSyncOwner distance over than 250 %s %s", GetName(), ch->GetName()); + + if (m_pkChrSyncOwner == ch) + return true; + + return false; + } + + if (m_pkChrSyncOwner != ch) + { + if (m_pkChrSyncOwner) + { + sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName()); + m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this); + } + + m_pkChrSyncOwner = ch; + m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.emplace_back(this); + + static const timeval zero_tv = {0, 0}; + SetLastSyncTime(zero_tv); + + sys_log(1, "SetSyncOwner set %s %p to %s", GetName(), this, ch->GetName()); + } + + m_fSyncTime = get_float_time(); + } + + TPacketGCOwnership pack; + + pack.bHeader = HEADER_GC_OWNERSHIP; + pack.dwOwnerVID = ch ? ch->GetVID() : 0; + pack.dwVictimVID = GetVID(); + + PacketAround(pack); + return true; +} + +struct FuncClearSync +{ + void operator () (LPCHARACTER ch) + { + assert(ch != NULL); + ch->SetSyncOwner(NULL, false); + } +}; + +void CHARACTER::ClearSync() +{ + SetSyncOwner(NULL); + + std::for_each(m_kLst_pkChrSyncOwned.begin(), m_kLst_pkChrSyncOwned.end(), FuncClearSync()); + m_kLst_pkChrSyncOwned.clear(); +} + +bool CHARACTER::IsSyncOwner(LPCHARACTER ch) const +{ + if (m_pkChrSyncOwner == ch) + return true; + + if (get_float_time() - m_fSyncTime >= 3.0f) + return true; + + return false; +} + +void CHARACTER::SetParty(LPPARTY pkParty) +{ + if (pkParty == m_pkParty) + return; + + if (pkParty && m_pkParty) + sys_err("%s is trying to reassigning party (current %p, new party %p)", GetName(), get_pointer(m_pkParty), get_pointer(pkParty)); + + sys_log(1, "PARTY set to %p", get_pointer(pkParty)); + + m_pkParty = pkParty; + + if (IsPC()) + { + if (m_pkParty) + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY); + else + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY); + + UpdatePacket(); + } +} + +// PARTY_JOIN_BUG_FIX +EVENTINFO(TPartyJoinEventInfo) +{ + DWORD dwGuestPID; + DWORD dwLeaderPID; + + TPartyJoinEventInfo() + : dwGuestPID( 0 ) + , dwLeaderPID( 0 ) + { + } +} ; + +EVENTFUNC(party_request_event) +{ + TPartyJoinEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "party_request_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID); + + if (ch) + { + sys_log(0, "PartyRequestEvent %s", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); + ch->SetPartyRequestEvent(NULL); + } + + return 0; +} + +bool CHARACTER::RequestToParty(LPCHARACTER leader) +{ + if (leader->GetParty()) + leader = leader->GetParty()->GetLeaderCharacter(); + + if (!leader) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ƽ ° ƴ϶ û ϴ.")); + return false; + } + + if (m_pkPartyRequestEvent) + return false; + + if (!IsPC() || !leader->IsPC()) + return false; + + if (leader->IsBlockMode(BLOCK_PARTY_REQUEST)) + return false; + + PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this); + + switch (errcode) + { + case PERR_NONE: + break; + + case PERR_SERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return false; + + case PERR_DIFFEMPIRE: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ٸ Ƽ ̷ ϴ.")); + return false; + + case PERR_DUNGEON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); + return false; + + case PERR_OBSERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); + return false; + + case PERR_LVBOUNDARY: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); + return false; + + case PERR_LOWLEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); + return false; + + case PERR_HILEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); + return false; + + case PERR_ALREADYJOIN: + return false; + + case PERR_PARTYISFULL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + return false; + + default: + sys_err("Do not process party join error(%d)", errcode); + return false; + } + + TPartyJoinEventInfo* info = AllocEventInfo(); + + info->dwGuestPID = GetPlayerID(); + info->dwLeaderPID = leader->GetPlayerID(); + + SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10))); + + leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Կ Ƽ û ߽ϴ."), leader->GetName()); + return true; +} + +void CHARACTER::DenyToParty(LPCHARACTER member) +{ + sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent)); + + if (!member->m_pkPartyRequestEvent) + return; + + TPartyJoinEventInfo * info = dynamic_cast(member->m_pkPartyRequestEvent->info); + + if (!info) + { + sys_err( "CHARACTER::DenyToParty> Null pointer" ); + return; + } + + if (info->dwGuestPID != member->GetPlayerID()) + return; + + if (info->dwLeaderPID != GetPlayerID()) + return; + + event_cancel(&member->m_pkPartyRequestEvent); + + member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +void CHARACTER::AcceptToParty(LPCHARACTER member) +{ + sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent)); + + if (!member->m_pkPartyRequestEvent) + return; + + TPartyJoinEventInfo * info = dynamic_cast(member->m_pkPartyRequestEvent->info); + + if (!info) + { + sys_err( "CHARACTER::AcceptToParty> Null pointer" ); + return; + } + + if (info->dwGuestPID != member->GetPlayerID()) + return; + + if (info->dwLeaderPID != GetPlayerID()) + return; + + event_cancel(&member->m_pkPartyRequestEvent); + + if (!GetParty()) + member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ƽ ʽϴ.")); + else + { + if (GetPlayerID() != GetParty()->GetLeaderPID()) + return; + + PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member); + switch (errcode) + { + case PERR_NONE: member->PartyJoin(this); return; + case PERR_SERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); break; + case PERR_DUNGEON: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); break; + case PERR_OBSERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); break; + case PERR_LVBOUNDARY: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); break; + case PERR_LOWLEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); break; + case PERR_HILEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); break; + case PERR_ALREADYJOIN: break; + case PERR_PARTYISFULL: { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ο ʰϿ Ƽ ϴ.")); + break; + } + default: sys_err("Do not process party join error(%d)", errcode); + } + } + + member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +EVENTFUNC(party_invite_event) +{ + TPartyJoinEventInfo * pInfo = dynamic_cast( event->info ); + + if ( pInfo == NULL ) + { + sys_err( "party_invite_event> Null pointer" ); + return 0; + } + + LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID); + + if (pchInviter) + { + sys_log(1, "PartyInviteEvent %s", pchInviter->GetName()); + pchInviter->PartyInviteDeny(pInfo->dwGuestPID); + } + + return 0; +} + +void CHARACTER::PartyInvite(LPCHARACTER pchInvitee) +{ + if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ʴ ִ ϴ.")); + return; + } + else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> %s Ƽ ź Դϴ."), pchInvitee->GetName()); + return; + } + + PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee); + + switch (errcode) + { + case PERR_NONE: + break; + + case PERR_SERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return; + + case PERR_DIFFEMPIRE: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ٸ Ƽ ̷ ϴ.")); + return; + + case PERR_DUNGEON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); + return; + + case PERR_OBSERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); + return; + + case PERR_LVBOUNDARY: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); + return; + + case PERR_LOWLEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); + return; + + case PERR_HILEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); + return; + + case PERR_ALREADYJOIN: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̹ %s Ƽ ֽϴ."), pchInvitee->GetName()); + return; + + case PERR_PARTYISFULL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + return; + + default: + sys_err("Do not process party join error(%d)", errcode); + return; + } + + if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID())) + return; + + TPartyJoinEventInfo* info = AllocEventInfo(); + + info->dwGuestPID = pchInvitee->GetPlayerID(); + info->dwLeaderPID = GetPlayerID(); + + m_PartyInviteEventMap.emplace(pchInvitee->GetPlayerID(), event_create(party_invite_event, info, PASSES_PER_SEC(10))); + + TPacketGCPartyInvite p; + p.header = HEADER_GC_PARTY_INVITE; + p.leader_vid = GetVID(); + pchInvitee->GetDesc()->Packet(p); +} + +void CHARACTER::PartyInviteAccept(LPCHARACTER pchInvitee) +{ + EventMap::iterator itFind = m_PartyInviteEventMap.find(pchInvitee->GetPlayerID()); + + if (itFind == m_PartyInviteEventMap.end()) + { + sys_log(1, "PartyInviteAccept from not invited character(%s)", pchInvitee->GetName()); + return; + } + + event_cancel(&itFind->second); + m_PartyInviteEventMap.erase(itFind); + + if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ʴ ִ ϴ.")); + return; + } + + PartyJoinErrCode errcode = IsPartyJoinableMutableCondition(this, pchInvitee); + + switch (errcode) + { + case PERR_NONE: + break; + + case PERR_SERVER: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return; + + case PERR_DUNGEON: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ뿡 ϴ.")); + return; + + case PERR_OBSERVER: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); + return; + + case PERR_LVBOUNDARY: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); + return; + + case PERR_LOWLEVEL: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); + return; + + case PERR_HILEVEL: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); + return; + + case PERR_ALREADYJOIN: + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ʴ뿡 ϴ.")); + return; + + case PERR_PARTYISFULL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ο ʰϿ Ƽ ϴ.")); + return; + + default: + sys_err("ignore party join error(%d)", errcode); + return; + } + + if (GetParty()) + pchInvitee->PartyJoin(this); + else + { + LPPARTY pParty = CPartyManager::instance().CreateParty(this); + + pParty->Join(pchInvitee->GetPlayerID()); + pParty->Link(pchInvitee); + pParty->SendPartyInfoAllToOne(this); + } +} + +void CHARACTER::PartyInviteDeny(DWORD dwPID) +{ + EventMap::iterator itFind = m_PartyInviteEventMap.find(dwPID); + + if (itFind == m_PartyInviteEventMap.end()) + { + sys_log(1, "PartyInviteDeny to not exist event(inviter PID: %d, invitee PID: %d)", GetPlayerID(), dwPID); + return; + } + + event_cancel(&itFind->second); + m_PartyInviteEventMap.erase(itFind); + + LPCHARACTER pchInvitee = CHARACTER_MANAGER::instance().FindByPID(dwPID); + if (pchInvitee) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> %s Ƽ ʴ븦 ϼ̽ϴ."), pchInvitee->GetName()); +} + +void CHARACTER::PartyJoin(LPCHARACTER pLeader) +{ + pLeader->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> %s Ƽ ϼ̽ϴ."), GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> %s Ƽ ϼ̽ϴ."), pLeader->GetName()); + + pLeader->GetParty()->Join(GetPlayerID()); + pLeader->GetParty()->Link(this); +} + +CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest) +{ + if (pchLeader->GetEmpire() != pchGuest->GetEmpire()) + return PERR_DIFFEMPIRE; + + return IsPartyJoinableMutableCondition(pchLeader, pchGuest); +} + +static bool __party_can_join_by_level(LPCHARACTER leader, LPCHARACTER quest) +{ + int level_limit = 30; + return (abs(leader->GetLevel() - quest->GetLevel()) <= level_limit); +} + +CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest) +{ + if (!CPartyManager::instance().IsEnablePCParty()) + return PERR_SERVER; + else if (pchLeader->GetDungeon()) + return PERR_DUNGEON; + else if (pchGuest->IsObserverMode()) + return PERR_OBSERVER; + else if (false == __party_can_join_by_level(pchLeader, pchGuest)) + return PERR_LVBOUNDARY; + else if (pchGuest->GetParty()) + return PERR_ALREADYJOIN; + else if (pchLeader->GetParty()) + { + if (pchLeader->GetParty()->GetMemberCount() == PARTY_MAX_MEMBER) + return PERR_PARTYISFULL; + else if (pchLeader->GetParty()->IsPartyInAnyDungeon()) // @fixme301 + return PERR_DUNGEON; + } + + return PERR_NONE; +} +// END_OF_PARTY_JOIN_BUG_FIX + +void CHARACTER::SetDungeon(LPDUNGEON pkDungeon) +{ + if (pkDungeon && m_pkDungeon) + sys_err("%s is trying to reassigning dungeon (current %p, new party %p)", GetName(), get_pointer(m_pkDungeon), get_pointer(pkDungeon)); + + if (m_pkDungeon == pkDungeon) { + return; + } + + if (m_pkDungeon) + { + if (IsPC()) + { + if (GetParty()) + m_pkDungeon->DecPartyMember(GetParty(), this); + else + m_pkDungeon->DecMember(this); + } + else if (IsMonster() || IsStone()) + { + m_pkDungeon->DecMonster(); + } + } + + m_pkDungeon = pkDungeon; + + if (pkDungeon) + { + sys_log(0, "%s DUNGEON set to %p, PARTY is %p", GetName(), get_pointer(pkDungeon), get_pointer(m_pkParty)); + + if (IsPC()) + { + if (GetParty()) + m_pkDungeon->IncPartyMember(GetParty(), this); + else + m_pkDungeon->IncMember(this); + } + else if (IsMonster() || IsStone()) + { + m_pkDungeon->IncMonster(); + } + } +} + +void CHARACTER::SetWarMap(CWarMap * pWarMap) +{ + if (m_pWarMap) + m_pWarMap->DecMember(this); + + m_pWarMap = pWarMap; + + if (m_pWarMap) + m_pWarMap->IncMember(this); +} + +void CHARACTER::SetWeddingMap(marriage::WeddingMap* pMap) +{ + if (m_pWeddingMap) + m_pWeddingMap->DecMember(this); + + m_pWeddingMap = pMap; + + if (m_pWeddingMap) + m_pWeddingMap->IncMember(this); +} + +void CHARACTER::SetRegen(LPREGEN pkRegen) +{ + m_pkRegen = pkRegen; + if (pkRegen != NULL) { + regen_id_ = pkRegen->id; + } + m_fRegenAngle = GetRotation(); + m_posRegen = GetXYZ(); +} + +bool CHARACTER::OnIdle() +{ + return false; +} + +void CHARACTER::OnMove(bool bIsAttack) +{ + m_dwLastMoveTime = get_dword_time(); + + if (bIsAttack) + { + m_dwLastAttackTime = m_dwLastMoveTime; + + if (IsAffectFlag(AFF_REVIVE_INVISIBLE)) + RemoveAffect(AFFECT_REVIVE_INVISIBLE); + + if (IsAffectFlag(AFF_EUNHYUNG)) + { + RemoveAffect(SKILL_EUNHYUNG); + SetAffectedEunhyung(); + } + else + { + ClearAffectedEunhyung(); + } + } + + // MINING + mining_cancel(); + // END_OF_MINING +} + +void CHARACTER::OnClick(LPCHARACTER pkChrCauser) +{ + if (!pkChrCauser) + { + sys_err("OnClick %s by NULL", GetName()); + return; + } + + DWORD vid = GetVID(); + sys_log(0, "OnClick %s[vnum %d ServerUniqueID %d, pid %d] by %s", GetName(), GetRaceNum(), vid, GetPlayerID(), pkChrCauser->GetName()); + + { + if (pkChrCauser->GetMyShop() && pkChrCauser != this) + { + sys_err("OnClick Fail (%s->%s) - pc has shop", pkChrCauser->GetName(), GetName()); + return; + } + } + + { + if (pkChrCauser->GetExchange()) + { + sys_err("OnClick Fail (%s->%s) - pc is exchanging", pkChrCauser->GetName(), GetName()); + return; + } + } + + if (IsPC()) + { + if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID())) + { + if (GetMyShop()) + { + if (pkChrCauser->IsDead() == true) return; + + //PREVENT_TRADE_WINDOW + if (pkChrCauser == this) + { + if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen()) + { + pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ŷ(â,ȯ,) λ ϴ.")); + return; + } + } + else + { + if ((pkChrCauser->GetExchange() || pkChrCauser->IsOpenSafebox() || pkChrCauser->GetMyShop() || pkChrCauser->GetShopOwner()) || pkChrCauser->IsCubeOpen() ) + { + pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ŷ(â,ȯ,) λ ϴ.")); + return; + } + + if ((GetExchange() || IsOpenSafebox() || IsCubeOpen())) + { + pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ٸ ŷ ϰ ִ Դϴ.")); + return; + } + } + //END_PREVENT_TRADE_WINDOW + + if (pkChrCauser->GetShop()) + { + pkChrCauser->GetShop()->RemoveGuest(pkChrCauser); + pkChrCauser->SetShop(NULL); + } + + GetMyShop()->AddGuest(pkChrCauser, GetVID(), false); + pkChrCauser->SetShopOwner(this); + return; + } + + if (test_server) + sys_err("%s.OnClickFailure(%s) - target is PC", pkChrCauser->GetName(), GetName()); + + return; + } + } + + if (g_bChinaIntoxicationCheck) + { + if (pkChrCauser->IsOverTime(OT_3HOUR)) + { + sys_log(0, "Play OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 3); + return; + } + else if (pkChrCauser->IsOverTime(OT_5HOUR)) + { + sys_log(0, "Play OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 5); + return; + } + } + + pkChrCauser->SetQuestNPCID(GetVID()); + + if (quest::CQuestManager::instance().Click(pkChrCauser->GetPlayerID(), this)) + { + return; + } + + if (!IsPC()) + { + if (!m_triggerOnClick.pFunc) + { + return; + } + + m_triggerOnClick.pFunc(this, pkChrCauser); + } +} + +BYTE CHARACTER::GetGMLevel() const +{ + if (test_server) + return GM_IMPLEMENTOR; + return m_pointsInstant.gm_level; +} + +void CHARACTER::SetGMLevel() +{ + if (GetDesc()) + { + m_pointsInstant.gm_level = gm_get_level(GetName(), GetDesc()->GetHostName(), GetDesc()->GetAccountTable().login); + } + else + { + m_pointsInstant.gm_level = GM_PLAYER; + } +} + +BOOL CHARACTER::IsGM() const +{ + if (m_pointsInstant.gm_level != GM_PLAYER) + return true; + if (test_server) + return true; + return false; +} + +void CHARACTER::SetStone(LPCHARACTER pkChrStone) +{ + m_pkChrStone = pkChrStone; + + if (m_pkChrStone) + { + if (pkChrStone->m_set_pkChrSpawnedBy.find(this) == pkChrStone->m_set_pkChrSpawnedBy.end()) + pkChrStone->m_set_pkChrSpawnedBy.emplace(this); + } +} + +struct FuncDeadSpawnedByStone +{ + void operator () (LPCHARACTER ch) + { + ch->Dead(NULL); + ch->SetStone(NULL); + } +}; + +void CHARACTER::ClearStone() +{ + if (!m_set_pkChrSpawnedBy.empty()) + { + FuncDeadSpawnedByStone f; + std::for_each(m_set_pkChrSpawnedBy.begin(), m_set_pkChrSpawnedBy.end(), f); + m_set_pkChrSpawnedBy.clear(); + } + + if (!m_pkChrStone) + return; + + m_pkChrStone->m_set_pkChrSpawnedBy.erase(this); + m_pkChrStone = NULL; +} + +void CHARACTER::ClearTarget() +{ + if (m_pkChrTarget) + { + m_pkChrTarget->m_set_pkChrTargetedBy.erase(this); + m_pkChrTarget = NULL; + } + + TPacketGCTarget p; + + p.header = HEADER_GC_TARGET; + p.dwVID = 0; + p.bHPPercent = 0; + + CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin(); + + while (it != m_set_pkChrTargetedBy.end()) + { + LPCHARACTER pkChr = *(it++); + pkChr->m_pkChrTarget = NULL; + + if (!pkChr->GetDesc()) + { + sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr)); + abort(); + } + + pkChr->GetDesc()->Packet(p); + } + + m_set_pkChrTargetedBy.clear(); +} + +void CHARACTER::SetTarget(LPCHARACTER pkChrTarget) +{ + if (m_pkChrTarget == pkChrTarget) + return; + + // CASTLE + if (IS_CASTLE_MAP(GetMapIndex()) && !IsGM()) + return; + // CASTLE + + if (m_pkChrTarget) + m_pkChrTarget->m_set_pkChrTargetedBy.erase(this); + + m_pkChrTarget = pkChrTarget; + + TPacketGCTarget p; + + p.header = HEADER_GC_TARGET; + + if (m_pkChrTarget) + { + m_pkChrTarget->m_set_pkChrTargetedBy.emplace(this); + + p.dwVID = m_pkChrTarget->GetVID(); + + if ((m_pkChrTarget->IsPC() && !m_pkChrTarget->IsPolymorphed()) || (m_pkChrTarget->GetMaxHP() <= 0)) + p.bHPPercent = 0; + else + { + if (m_pkChrTarget->GetRaceNum() == 20101 || + m_pkChrTarget->GetRaceNum() == 20102 || + m_pkChrTarget->GetRaceNum() == 20103 || + m_pkChrTarget->GetRaceNum() == 20104 || + m_pkChrTarget->GetRaceNum() == 20105 || + m_pkChrTarget->GetRaceNum() == 20106 || + m_pkChrTarget->GetRaceNum() == 20107 || + m_pkChrTarget->GetRaceNum() == 20108 || + m_pkChrTarget->GetRaceNum() == 20109) + { + LPCHARACTER owner = m_pkChrTarget->GetVictim(); + + if (owner) + { + int iHorseHealth = owner->GetHorseHealth(); + int iHorseMaxHealth = owner->GetHorseMaxHealth(); + + if (iHorseMaxHealth) + p.bHPPercent = MINMAX(0, iHorseHealth * 100 / iHorseMaxHealth, 100); + else + p.bHPPercent = 100; + } + else + p.bHPPercent = 100; + } + else + { + if (m_pkChrTarget->GetMaxHP() <= 0) // @fixme136 + p.bHPPercent = 0; + else + p.bHPPercent = MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100); + } + } + } + else + { + p.dwVID = 0; + p.bHPPercent = 0; + } + + GetDesc()->Packet(p); +} + +void CHARACTER::BroadcastTargetPacket() +{ + if (m_set_pkChrTargetedBy.empty()) + return; + + TPacketGCTarget p; + + p.header = HEADER_GC_TARGET; + p.dwVID = GetVID(); + + if (IsPC()) + p.bHPPercent = 0; + else if (GetMaxHP() <= 0) // @fixme136 + p.bHPPercent = 0; + else + p.bHPPercent = MINMAX(0, (GetHP() * 100) / GetMaxHP(), 100); + + CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin(); + + while (it != m_set_pkChrTargetedBy.end()) + { + LPCHARACTER pkChr = *it++; + + if (!pkChr->GetDesc()) + { + sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr)); + abort(); + } + + pkChr->GetDesc()->Packet(p); + } +} + +void CHARACTER::CheckTarget() +{ + if (!m_pkChrTarget) + return; + + if (DISTANCE_APPROX(GetX() - m_pkChrTarget->GetX(), GetY() - m_pkChrTarget->GetY()) >= 4800) + SetTarget(NULL); +} + +void CHARACTER::SetWarpLocation(long lMapIndex, long x, long y) +{ + m_posWarp.x = x * 100; + m_posWarp.y = y * 100; + m_lWarpMapIndex = lMapIndex; +} + +void CHARACTER::SaveExitLocation() +{ + m_posExit = GetXYZ(); + m_lExitMapIndex = GetMapIndex(); +} + +void CHARACTER::ExitToSavedLocation() +{ + sys_log (0, "ExitToSavedLocation"); + WarpSet(m_posWarp.x, m_posWarp.y, m_lWarpMapIndex); + + m_posExit.x = m_posExit.y = m_posExit.z = 0; + m_lExitMapIndex = 0; +} + +bool CHARACTER::WarpSet(long x, long y, long lPrivateMapIndex) +{ + if (!IsPC()) + return false; + + long lAddr; + long lMapIndex; + WORD wPort; + + if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort)) + { + sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex, x, y, GetName()); + return false; + } + + //Send Supplementary Data Block if new map requires security packages in loading this map + { + long lCurAddr; + long lCurMapIndex = 0; + WORD wCurPort; + + CMapLocation::instance().Get(GetX(), GetY(), lCurMapIndex, lCurAddr, wCurPort); + + //do not send SDB files if char is in the same map + if( lCurMapIndex != lMapIndex ) + { + const TMapRegion * rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex); + { + DESC_MANAGER::instance().SendClientPackageSDBToLoadMap( GetDesc(), rMapRgn->strMapName.c_str() ); + } + } + } + + if (lPrivateMapIndex >= 10000) + { + if (lPrivateMapIndex / 10000 != lMapIndex) + { + sys_err("Invalid map index %d, must be child of %d", lPrivateMapIndex, lMapIndex); + return false; + } + + lMapIndex = lPrivateMapIndex; + } + + Stop(); + Save(); + + if (GetSectree()) + { + GetSectree()->RemoveEntity(this); + ViewCleanup(); + + EncodeRemovePacket(this); + } + + m_lWarpMapIndex = lMapIndex; + m_posWarp.x = x; + m_posWarp.y = y; + + sys_log(0, "WarpSet %s %d %d current map %d target map %d", GetName(), x, y, GetMapIndex(), lMapIndex); + + TPacketGCWarp p; + + p.bHeader = HEADER_GC_WARP; + p.lX = x; + p.lY = y; + p.lAddr = lAddr; +#ifdef ENABLE_NEWSTUFF + if (!g_stProxyIP.empty()) + p.lAddr = inet_addr(g_stProxyIP.c_str()); +#endif + p.wPort = wPort; + + GetDesc()->Packet(p); + + char buf[256]; + snprintf(buf, sizeof(buf), "%s MapIdx %ld DestMapIdx%ld DestX%ld DestY%ld Empire%d", GetName(), GetMapIndex(), lPrivateMapIndex, x, y, GetEmpire()); + LogManager::instance().CharLog(this, 0, "WARP", buf); + + return true; +} + +#define ENABLE_GOHOME_IF_MAP_NOT_ALLOWED +void CHARACTER::WarpEnd() +{ + if (test_server) + sys_log(0, "WarpEnd %s", GetName()); + + if (m_posWarp.x == 0 && m_posWarp.y == 0) + return; + + int index = m_lWarpMapIndex; + + if (index > 10000) + index /= 10000; + + if (!map_allow_find(index)) + { + sys_err("location %d %d not allowed to login this server", m_posWarp.x, m_posWarp.y); +#ifdef ENABLE_GOHOME_IF_MAP_NOT_ALLOWED + GoHome(); +#else + GetDesc()->SetPhase(PHASE_CLOSE); +#endif + return; + } + + sys_log(0, "WarpEnd %s %d %u %u", GetName(), m_lWarpMapIndex, m_posWarp.x, m_posWarp.y); + + Show(m_lWarpMapIndex, m_posWarp.x, m_posWarp.y, 0); + Stop(); + + m_lWarpMapIndex = 0; + m_posWarp.x = m_posWarp.y = m_posWarp.z = 0; + + { + // P2P Login + TPacketGGLogin p; + + p.bHeader = HEADER_GG_LOGIN; + strlcpy(p.szName, GetName(), sizeof(p.szName)); + p.dwPID = GetPlayerID(); + p.bEmpire = GetEmpire(); + p.lMapIndex = SECTREE_MANAGER::instance().GetMapIndex(GetX(), GetY()); + p.bChannel = g_bChannel; + + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogin)); + } +} + +bool CHARACTER::Return() +{ + if (!IsNPC()) + return false; + + SetVictim(NULL); + + int x = m_pkMobInst->m_posLastAttacked.x; + int y = m_pkMobInst->m_posLastAttacked.y; + + SetRotationToXY(x, y); + + if (!Goto(x, y)) + return false; + + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + if (test_server) + sys_log(0, "%s %p ϰ ư! %d %d", GetName(), this, x, y); + + if (GetParty()) + GetParty()->SendMessage(this, PM_RETURN, x, y); + + return true; +} + +bool CHARACTER::Follow(LPCHARACTER pkChr, float fMinDistance) +{ + if (IsPC()) + { + sys_err("CHARACTER::Follow : PC cannot use this method", GetName()); + return false; + } + + // TRENT_MONSTER + if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + { + if (pkChr->IsPC()) + { + // If i'm in a party. I must obey party leader's AI. + if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this) + { + if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) + { + if (m_pkMobData->m_table.wAttackRange < DISTANCE_APPROX(pkChr->GetX() - GetX(), pkChr->GetY() - GetY())) + if (Return()) + return true; + } + } + } + return false; + } + // END_OF_TRENT_MONSTER + + long x = pkChr->GetX(); + long y = pkChr->GetY(); + + if (pkChr->IsPC()) + { + // If i'm in a party. I must obey party leader's AI. + if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this) + { + if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) + { + if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY())) + if (Return()) + return true; + } + } + } + + if (IsGuardNPC()) + { + if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY())) + if (Return()) + return true; + } + + if (pkChr->IsState(pkChr->m_stateMove) && + GetMobBattleType() != BATTLE_TYPE_RANGE && + GetMobBattleType() != BATTLE_TYPE_MAGIC && + false == IsPet()) + { + float rot = pkChr->GetRotation(); + float rot_delta = GetDegreeDelta(rot, GetDegreeFromPositionXY(GetX(), GetY(), pkChr->GetX(), pkChr->GetY())); + + float yourSpeed = pkChr->GetMoveSpeed(); + float mySpeed = GetMoveSpeed(); + + float fDist = DISTANCE_SQRT(x - GetX(), y - GetY()); + float fFollowSpeed = mySpeed - yourSpeed * cos(rot_delta * M_PI / 180); + + if (fFollowSpeed >= 0.1f) + { + float fMeetTime = fDist / fFollowSpeed; + float fYourMoveEstimateX, fYourMoveEstimateY; + + if( fMeetTime * yourSpeed <= 100000.0f ) + { + GetDeltaByDegree(pkChr->GetRotation(), fMeetTime * yourSpeed, &fYourMoveEstimateX, &fYourMoveEstimateY); + + x += (long) fYourMoveEstimateX; + y += (long) fYourMoveEstimateY; + + float fDistNew = sqrt(((double)x - GetX())*(x-GetX())+((double)y - GetY())*(y-GetY())); + if (fDist < fDistNew) + { + x = (long)(GetX() + (x - GetX()) * fDist / fDistNew); + y = (long)(GetY() + (y - GetY()) * fDist / fDistNew); + } + } + } + } + + SetRotationToXY(x, y); + + float fDist = DISTANCE_SQRT(x - GetX(), y - GetY()); + + if (fDist <= fMinDistance) + return false; + + float fx, fy; + + if (IsChangeAttackPosition(pkChr) && GetMobRank() < MOB_RANK_BOSS) + { + SetChangeAttackPositionTime(); + + int retry = 16; + int dx = 0, dy = 0; + int rot = (int) GetDegreeFromPositionXY(x, y, GetX(), GetY()); + + while (--retry) + { + if (fDist < 500.0f) + GetDeltaByDegree((rot + number(-90, 90) + number(-90, 90)) % 360, fMinDistance, &fx, &fy); + else + GetDeltaByDegree(number(0, 359), fMinDistance, &fx, &fy); + + dx = x + (int) fx; + dy = y + (int) fy; + + LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), dx, dy); + + if (NULL == tree) + break; + + if (0 == (tree->GetAttribute(dx, dy) & (ATTR_BLOCK | ATTR_OBJECT))) + break; + } + + if (!Goto(dx, dy)) + return false; + } + else + { + float fDistToGo = fDist - fMinDistance; + GetDeltaByDegree(GetRotation(), fDistToGo, &fx, &fy); + + if (!Goto(GetX() + (int) fx, GetY() + (int) fy)) + return false; + } + + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + return true; +} + +float CHARACTER::GetDistanceFromSafeboxOpen() const +{ + return DISTANCE_APPROX(GetX() - m_posSafeboxOpen.x, GetY() - m_posSafeboxOpen.y); +} + +void CHARACTER::SetSafeboxOpenPosition() +{ + m_posSafeboxOpen = GetXYZ(); +} + +CSafebox * CHARACTER::GetSafebox() const +{ + return m_pkSafebox; +} + +void CHARACTER::ReqSafeboxLoad(const char* pszPassword) +{ + if (!*pszPassword || strlen(pszPassword) > SAFEBOX_PASSWORD_MAX_LEN) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + else if (m_pkSafebox) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â ̹ ֽϴ.")); + return; + } + + int iPulse = thecore_pulse(); + + if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(10)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â 10 ȿ ϴ.")); + return; + } + else if (GetDistanceFromSafeboxOpen() > 1000) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> Ÿ ־ â ϴ.")); + return; + } + else if (m_bOpeningSafebox) + { + sys_log(0, "Overlapped safebox load request from %s", GetName()); + return; + } + + SetSafeboxLoadTime(); + m_bOpeningSafebox = true; + + TSafeboxLoadPacket p; + p.dwID = GetDesc()->GetAccountTable().id; + strlcpy(p.szLogin, GetDesc()->GetAccountTable().login, sizeof(p.szLogin)); + strlcpy(p.szPassword, pszPassword, sizeof(p.szPassword)); + + db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_LOAD, GetDesc()->GetHandle(), &p, sizeof(p)); +} + +void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems) +{ + bool bLoaded = false; + + //PREVENT_TRADE_WINDOW + SetOpenSafebox(true); + //END_PREVENT_TRADE_WINDOW + + if (m_pkSafebox) + bLoaded = true; + + if (!m_pkSafebox) + m_pkSafebox = M2_NEW CSafebox(this, iSize, dwGold); + else + m_pkSafebox->ChangeSize(iSize); + + m_iSafeboxSize = iSize; + + TPacketCGSafeboxSize p; + + p.bHeader = HEADER_GC_SAFEBOX_SIZE; + p.bSize = iSize; + + GetDesc()->Packet(p); + + if (!bLoaded) + { + for (int i = 0; i < iItemCount; ++i, ++pItems) + { + if (!m_pkSafebox->IsValidPosition(pItems->pos)) + continue; + + LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id); + + if (!item) + { + sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName()); + continue; + } + + item->SetSkipSave(true); + item->SetSockets(pItems->alSockets); + item->SetAttributes(pItems->aAttr); + + if (!m_pkSafebox->Add(pItems->pos, item)) + { + M2_DESTROY_ITEM(item); + } + else + { + item->OnAfterCreatedItem(); // @fixme306 + item->SetSkipSave(false); + } + } + } +} + +void CHARACTER::ChangeSafeboxSize(BYTE bSize) +{ + TPacketCGSafeboxSize p; + + p.bHeader = HEADER_GC_SAFEBOX_SIZE; + p.bSize = bSize; + + GetDesc()->Packet(p); + + if (m_pkSafebox) + m_pkSafebox->ChangeSize(bSize); + + m_iSafeboxSize = bSize; +} + +void CHARACTER::CloseSafebox() +{ + if (!m_pkSafebox) + return; + + //PREVENT_TRADE_WINDOW + SetOpenSafebox(false); + //END_PREVENT_TRADE_WINDOW + + m_pkSafebox->Save(); + + M2_DELETE(m_pkSafebox); + m_pkSafebox = NULL; + + ChatPacket(CHAT_TYPE_COMMAND, "CloseSafebox"); + + SetSafeboxLoadTime(); + m_bOpeningSafebox = false; + + Save(); +} + +CSafebox * CHARACTER::GetMall() const +{ + return m_pkMall; +} + +void CHARACTER::LoadMall(int iItemCount, TPlayerItem * pItems) +{ + bool bLoaded = false; + + if (m_pkMall) + bLoaded = true; + + if (!m_pkMall) + m_pkMall = M2_NEW CSafebox(this, 3 * SAFEBOX_PAGE_SIZE, 0); + else + m_pkMall->ChangeSize(3 * SAFEBOX_PAGE_SIZE); + + m_pkMall->SetWindowMode(MALL); + + TPacketCGSafeboxSize p; + + p.bHeader = HEADER_GC_MALL_OPEN; + p.bSize = 3 * SAFEBOX_PAGE_SIZE; + + GetDesc()->Packet(p); + + if (!bLoaded) + { + for (int i = 0; i < iItemCount; ++i, ++pItems) + { + if (!m_pkMall->IsValidPosition(pItems->pos)) + continue; + + LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id); + + if (!item) + { + sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName()); + continue; + } + + item->SetSkipSave(true); + item->SetSockets(pItems->alSockets); + item->SetAttributes(pItems->aAttr); + + if (!m_pkMall->Add(pItems->pos, item)) + M2_DESTROY_ITEM(item); + else + item->SetSkipSave(false); + } + } +} + +void CHARACTER::CloseMall() +{ + if (!m_pkMall) + return; + + m_pkMall->Save(); + + M2_DELETE(m_pkMall); + m_pkMall = NULL; + + ChatPacket(CHAT_TYPE_COMMAND, "CloseMall"); +} + +bool CHARACTER::BuildUpdatePartyPacket(TPacketGCPartyUpdate & out) +{ + if (!GetParty()) + return false; + + memset(&out, 0, sizeof(out)); + + out.header = HEADER_GC_PARTY_UPDATE; + out.pid = GetPlayerID(); + if (GetMaxHP() <= 0) // @fixme136 + out.percent_hp = 0; + else + out.percent_hp = MINMAX(0, GetHP() * 100 / GetMaxHP(), 100); + out.role = GetParty()->GetRole(GetPlayerID()); + + sys_log(1, "PARTY %s role is %d", GetName(), out.role); + + LPCHARACTER l = GetParty()->GetLeaderCharacter(); + + if (l && DISTANCE_APPROX(GetX() - l->GetX(), GetY() - l->GetY()) < PARTY_DEFAULT_RANGE) + { + out.affects[0] = GetParty()->GetPartyBonusExpPercent(); + out.affects[1] = GetPoint(POINT_PARTY_ATTACKER_BONUS); + out.affects[2] = GetPoint(POINT_PARTY_TANKER_BONUS); + out.affects[3] = GetPoint(POINT_PARTY_BUFFER_BONUS); + out.affects[4] = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS); + out.affects[5] = GetPoint(POINT_PARTY_HASTE_BONUS); + out.affects[6] = GetPoint(POINT_PARTY_DEFENDER_BONUS); + } + + return true; +} + +int CHARACTER::GetLeadershipSkillLevel() const +{ + return GetSkillLevel(SKILL_LEADERSHIP); +} + +void CHARACTER::QuerySafeboxSize() +{ + if (m_iSafeboxSize == -1) + { + DBManager::instance().ReturnQuery(QID_SAFEBOX_SIZE, + GetPlayerID(), + NULL, + "SELECT size FROM safebox%s WHERE account_id = %u", + get_table_postfix(), + GetDesc()->GetAccountTable().id); + } +} + +void CHARACTER::SetSafeboxSize(int iSize) +{ + sys_log(1, "SetSafeboxSize: %s %d", GetName(), iSize); + m_iSafeboxSize = iSize; + DBManager::instance().Query("UPDATE safebox%s SET size = %d WHERE account_id = %u", get_table_postfix(), iSize / SAFEBOX_PAGE_SIZE, GetDesc()->GetAccountTable().id); +} + +int CHARACTER::GetSafeboxSize() const +{ + return m_iSafeboxSize; +} + +void CHARACTER::SetNowWalking(bool bWalkFlag) +{ + if (m_bNowWalking != bWalkFlag) + { + if (bWalkFlag) + { + m_bNowWalking = true; + m_dwWalkStartTime = get_dword_time(); + } + else + { + m_bNowWalking = false; + } + + { + TPacketGCWalkMode p; + p.vid = GetVID(); + p.header = HEADER_GC_WALK_MODE; + p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN; + + PacketView(&p, sizeof(p)); + } + + if (IsNPC()) + { + if (m_bNowWalking) + MonsterLog("ȴ´"); + else + MonsterLog("ڴ"); + } + + //sys_log(0, "%s is now %s", GetName(), m_bNowWalking?"walking.":"running."); + } +} + +void CHARACTER::StartStaminaConsume() +{ + if (m_bStaminaConsume) + return; + PointChange(POINT_STAMINA, 0); + m_bStaminaConsume = true; + if (IsStaminaHalfConsume()) + ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec / 2, GetStamina()); + else + ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina()); +} + +void CHARACTER::StopStaminaConsume() +{ + if (!m_bStaminaConsume) + return; + PointChange(POINT_STAMINA, 0); + m_bStaminaConsume = false; + ChatPacket(CHAT_TYPE_COMMAND, "StopStaminaConsume %d", GetStamina()); +} + +bool CHARACTER::IsStaminaConsume() const +{ + return m_bStaminaConsume; +} + +bool CHARACTER::IsStaminaHalfConsume() const +{ + return IsEquipUniqueItem(UNIQUE_ITEM_HALF_STAMINA); +} + +void CHARACTER::ResetStopTime() +{ + m_dwStopTime = get_dword_time(); +} + +DWORD CHARACTER::GetStopTime() const +{ + return m_dwStopTime; +} + +void CHARACTER::ResetPoint(int iLv) +{ + BYTE bJob = GetJob(); + + PointChange(POINT_LEVEL, iLv - GetLevel()); + + SetRealPoint(POINT_ST, JobInitialPoints[bJob].st); + SetPoint(POINT_ST, GetRealPoint(POINT_ST)); + + SetRealPoint(POINT_HT, JobInitialPoints[bJob].ht); + SetPoint(POINT_HT, GetRealPoint(POINT_HT)); + + SetRealPoint(POINT_DX, JobInitialPoints[bJob].dx); + SetPoint(POINT_DX, GetRealPoint(POINT_DX)); + + SetRealPoint(POINT_IQ, JobInitialPoints[bJob].iq); + SetPoint(POINT_IQ, GetRealPoint(POINT_IQ)); + + SetRandomHP((iLv - 1) * number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end)); + SetRandomSP((iLv - 1) * number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end)); + + // @fixme104 + PointChange(POINT_STAT, (MINMAX(1, iLv, g_iStatusPointGetLevelLimit) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT)); + + ComputePoints(); + + PointChange(POINT_HP, GetMaxHP() - GetHP()); + PointChange(POINT_SP, GetMaxSP() - GetSP()); + + PointsPacket(); + + LogManager::instance().CharLog(this, 0, "RESET_POINT", ""); +} + +bool CHARACTER::IsChangeAttackPosition(LPCHARACTER target) const +{ + if (!IsNPC()) + return true; + + DWORD dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_NEAR; + + if (DISTANCE_APPROX(GetX() - target->GetX(), GetY() - target->GetY()) > + AI_CHANGE_ATTACK_POISITION_DISTANCE + GetMobAttackRange()) + dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_FAR; + + return get_dword_time() - m_dwLastChangeAttackPositionTime > dwChangeTime; +} + +void CHARACTER::GiveRandomSkillBook() +{ + LPITEM item = AutoGiveItem(50300); + + if (NULL != item) + { + extern const DWORD GetRandomSkillVnum(BYTE bJob = JOB_MAX_NUM); + DWORD dwSkillVnum = 0; + // 50% of getting random books or getting one of the same player's race + if (!number(0, 1)) + dwSkillVnum = GetRandomSkillVnum(GetJob()); + else + dwSkillVnum = GetRandomSkillVnum(); + item->SetSocket(0, dwSkillVnum); + } +} + +void CHARACTER::ReviveInvisible(int iDur) +{ + AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true); +} + +void CHARACTER::ToggleMonsterLog() +{ + m_bMonsterLog = !m_bMonsterLog; + + if (m_bMonsterLog) + { + CHARACTER_MANAGER::instance().RegisterForMonsterLog(this); + } + else + { + CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this); + } +} + +void CHARACTER::SetGuild(CGuild* pGuild) +{ + if (m_pGuild != pGuild) + { + m_pGuild = pGuild; + UpdatePacket(); + } +} + +void CHARACTER::BeginStateEmpty() +{ + MonsterLog("!"); +} + +void CHARACTER::EffectPacket(int enumEffectType) +{ + TPacketGCSpecialEffect p; + + p.header = HEADER_GC_SEPCIAL_EFFECT; + p.type = enumEffectType; + p.vid = GetVID(); + + PacketAround(p); +} + +void CHARACTER::SpecificEffectPacket(const char filename[MAX_EFFECT_FILE_NAME]) +{ + TPacketGCSpecificEffect p; + + p.header = HEADER_GC_SPECIFIC_EFFECT; + p.vid = GetVID(); + memcpy (p.effect_file, filename, MAX_EFFECT_FILE_NAME); + + PacketAround(p); +} + +void CHARACTER::MonsterChat(BYTE bMonsterChatType) +{ + if (IsPC()) + return; + + char sbuf[256+1]; + + if (IsMonster()) + { + if (number(0, 60)) + return; + + snprintf(sbuf, sizeof(sbuf), + "(locale.monster_chat[%i] and locale.monster_chat[%i][%d] or '')", + GetRaceNum(), GetRaceNum(), bMonsterChatType*3 + number(1, 3)); + } + else + { + if (bMonsterChatType != MONSTER_CHAT_WAIT) + return; + + if (IsGuardNPC()) + { + if (number(0, 6)) + return; + } + else + { + if (number(0, 30)) + return; + } + + snprintf(sbuf, sizeof(sbuf), "(locale.monster_chat[%i] and locale.monster_chat[%i][number(1, table.getn(locale.monster_chat[%i]))] or '')", GetRaceNum(), GetRaceNum(), GetRaceNum()); + } + + std::string text = quest::ScriptToString(sbuf); + + if (text.empty()) + return; + + struct packet_chat pack_chat; + + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(struct packet_chat) + text.size() + 1; + pack_chat.type = CHAT_TYPE_TALKING; + pack_chat.id = GetVID(); + pack_chat.bEmpire = 0; + + TEMP_BUFFER buf; + buf.write(pack_chat); + buf.write(text.c_str(), text.size() + 1); + + PacketAround(buf.read_peek(), buf.size()); +} + +void CHARACTER::SetQuestNPCID(DWORD vid) +{ + m_dwQuestNPCVID = vid; +} + +LPCHARACTER CHARACTER::GetQuestNPC() const +{ + return CHARACTER_MANAGER::instance().Find(m_dwQuestNPCVID); +} + +void CHARACTER::SetQuestItemPtr(LPITEM item) +{ + m_dwQuestItemVID = (item) ? item->GetVID() : 0; +} + +void CHARACTER::ClearQuestItemPtr() +{ + m_dwQuestItemVID = 0; +} + +LPITEM CHARACTER::GetQuestItemPtr() const +{ + if (!m_dwQuestItemVID) + return nullptr; + return ITEM_MANAGER::Instance().FindByVID(m_dwQuestItemVID); +} + +#ifdef ENABLE_QUEST_DND_EVENT +void CHARACTER::SetQuestDNDItemPtr(LPITEM item) +{ + m_dwQuestDNDItemVID = (item) ? item->GetVID() : 0; +} + +void CHARACTER::ClearQuestDNDItemPtr() +{ + m_dwQuestDNDItemVID = 0; +} + +LPITEM CHARACTER::GetQuestDNDItemPtr() const +{ + if (!m_dwQuestDNDItemVID) + return nullptr; + return ITEM_MANAGER::Instance().FindByVID(m_dwQuestDNDItemVID); +} +#endif + +LPDUNGEON CHARACTER::GetDungeonForce() const +{ + if (m_lWarpMapIndex > 10000) + return CDungeonManager::instance().FindByMapIndex(m_lWarpMapIndex); + + return m_pkDungeon; +} + +void CHARACTER::SetBlockMode(BYTE bFlag) +{ + m_pointsInstant.bBlockMode = bFlag; + + ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode); + + SetQuestFlag("game_option.block_exchange", bFlag & BLOCK_EXCHANGE ? 1 : 0); + SetQuestFlag("game_option.block_party_invite", bFlag & BLOCK_PARTY_INVITE ? 1 : 0); + SetQuestFlag("game_option.block_guild_invite", bFlag & BLOCK_GUILD_INVITE ? 1 : 0); + SetQuestFlag("game_option.block_whisper", bFlag & BLOCK_WHISPER ? 1 : 0); + SetQuestFlag("game_option.block_messenger_invite", bFlag & BLOCK_MESSENGER_INVITE ? 1 : 0); + SetQuestFlag("game_option.block_party_request", bFlag & BLOCK_PARTY_REQUEST ? 1 : 0); +} + +void CHARACTER::SetBlockModeForce(BYTE bFlag) +{ + m_pointsInstant.bBlockMode = bFlag; + ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode); +} + +bool CHARACTER::IsGuardNPC() const +{ + return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004); +} + +int CHARACTER::GetPolymorphPower() const +{ + if (test_server) + { + int value = quest::CQuestManager::instance().GetEventFlag("poly"); + if (value) + return value; + } + return aiPolymorphPowerByLevel[MINMAX(0, GetSkillLevel(SKILL_POLYMORPH), 40)]; +} + +void CHARACTER::SetPolymorph(DWORD dwRaceNum, bool bMaintainStat) +{ +#ifdef ENABLE_WOLFMAN_CHARACTER + if (dwRaceNum < MAIN_RACE_MAX_NUM) +#else + if (dwRaceNum < JOB_MAX_NUM) +#endif + { + dwRaceNum = 0; + bMaintainStat = false; + } + + if (m_dwPolymorphRace == dwRaceNum) + return; + + m_bPolyMaintainStat = bMaintainStat; + m_dwPolymorphRace = dwRaceNum; + + sys_log(0, "POLYMORPH: %s race %u ", GetName(), dwRaceNum); + + if (dwRaceNum != 0) + StopRiding(); + + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN); + m_afAffectFlag.Set(AFF_SPAWN); + + ViewReencode(); + + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN); + + if (!bMaintainStat) + { + PointChange(POINT_ST, 0); + PointChange(POINT_DX, 0); + PointChange(POINT_IQ, 0); + PointChange(POINT_HT, 0); + } + + SetValidComboInterval(0); + SetComboSequence(0); + + ComputeBattlePoints(); +} + +int CHARACTER::GetQuestFlag(const std::string& flag) const +{ + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + return pPC->GetFlag(flag); +} + +void CHARACTER::SetQuestFlag(const std::string& flag, int value) +{ + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + pPC->SetFlag(flag, value); +} + +void CHARACTER::DetermineDropMetinStone() +{ +#ifdef ENABLE_NEWSTUFF + if (g_NoDropMetinStone) + { + m_dwDropMetinStone = 0; + return; + } +#endif + + static const DWORD c_adwMetin[] = + { +#if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_WOLFMAN_STONES) + 28012, +#endif + 28030, + 28031, + 28032, + 28033, + 28034, + 28035, + 28036, + 28037, + 28038, + 28039, + 28040, + 28041, + 28042, + 28043, +#if defined(ENABLE_MAGIC_REDUCTION_SYSTEM) && defined(USE_MAGIC_REDUCTION_STONES) + 28044, + 28045, +#endif + }; + DWORD stone_num = GetRaceNum(); + int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop; + if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num) + { + m_dwDropMetinStone = 0; + } + else + { + const SStoneDropInfo & info = aStoneDrop[idx]; + m_bDropMetinStonePct = info.iDropPct; + { + m_dwDropMetinStone = c_adwMetin[number(0, sizeof(c_adwMetin)/sizeof(DWORD) - 1)]; + int iGradePct = number(1, 100); + for (int iStoneLevel = 0; iStoneLevel < STONE_LEVEL_MAX_NUM; iStoneLevel ++) + { + int iLevelGradePortion = info.iLevelPct[iStoneLevel]; + if (iGradePct <= iLevelGradePortion) + { + break; + } + else + { + iGradePct -= iLevelGradePortion; + m_dwDropMetinStone += 100; + } + } + } + } +} + +void CHARACTER::SendEquipment(LPCHARACTER ch) +{ + TPacketViewEquip p; + p.header = HEADER_GC_VIEW_EQUIP; + p.vid = GetVID(); + for (int i = 0; iGetVnum(); + p.equips[i].count = item->GetCount(); + + thecore_memcpy(p.equips[i].alSockets, item->GetSockets(), sizeof(p.equips[i].alSockets)); + thecore_memcpy(p.equips[i].aAttr, item->GetAttributes(), sizeof(p.equips[i].aAttr)); + } + else + { + p.equips[i].vnum = 0; + } + } + ch->GetDesc()->Packet(p); +} + +bool CHARACTER::CanSummon(int iLeaderShip) +{ + return ((iLeaderShip >= 20) || ((iLeaderShip >= 12) && ((m_dwLastDeadTime + 180) > get_dword_time()))); +} + +void CHARACTER::EnterMount() +{ + #ifdef ENABLE_MOUNT_COSTUME_EX_SYSTEM + const auto mountVnum = GetPoint(POINT_MOUNT); + if (mountVnum) + { + MountVnum(mountVnum); + return; + } + #endif + + if (GetHorseLevel() > 0) + EnterHorse(); +} + +// #define ENABLE_MOUNT_ENTITY_REFRESH +void CHARACTER::MountVnum(DWORD vnum) +{ + if (m_dwMountVnum == vnum) + return; + if ((m_dwMountVnum != 0)&&(vnum!=0)) //@fixme108 set recursively to 0 for eventuality + MountVnum(0); + + m_dwMountVnum = vnum; + m_dwMountTime = get_dword_time(); + + if (m_bIsObserver) + return; + + m_posDest.x = m_posStart.x = GetX(); + m_posDest.y = m_posStart.y = GetY(); +#ifdef ENABLE_MOUNT_ENTITY_REFRESH + // EncodeRemovePacket(this); // commented, otherwise it may warp you back +#endif + EncodeInsertPacket(this); + + ENTITY_MAP::iterator it = m_map_view.begin(); + + while (it != m_map_view.end()) + { + LPENTITY entity = (it++)->first; + +#ifdef ENABLE_MOUNT_ENTITY_REFRESH + if (entity->IsType(ENTITY_CHARACTER)) + { + EncodeRemovePacket(entity); + if (!m_bIsObserver) + EncodeInsertPacket(entity); + + if (!entity->IsObserverMode()) + entity->EncodeInsertPacket(this); + } + else + EncodeInsertPacket(entity); +#else + EncodeInsertPacket(entity); +#endif + } + + SetValidComboInterval(0); + SetComboSequence(0); + ComputePoints(); +} + +namespace { + class FuncCheckWarp + { + public: + FuncCheckWarp(LPCHARACTER pkWarp) + { + m_lTargetY = 0; + m_lTargetX = 0; + + m_lX = pkWarp->GetX(); + m_lY = pkWarp->GetY(); + + m_bInvalid = false; + m_bEmpire = pkWarp->GetEmpire(); + + char szTmp[64]; + + if (3 != sscanf(pkWarp->GetName(), " %s %ld %ld ", szTmp, &m_lTargetX, &m_lTargetY)) + { + if (number(1, 100) < 5) + sys_err("Warp NPC name wrong : vnum(%d) name(%s)", pkWarp->GetRaceNum(), pkWarp->GetName()); + + m_bInvalid = true; + + return; + } + + m_lTargetX *= 100; + m_lTargetY *= 100; + + m_bUseWarp = true; + + if (pkWarp->IsGoto()) + { + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(pkWarp->GetMapIndex()); + m_lTargetX += pkSectreeMap->m_setting.iBaseX; + m_lTargetY += pkSectreeMap->m_setting.iBaseY; + m_bUseWarp = false; + } + } + + bool Valid() + { + return !m_bInvalid; + } + + void operator () (LPENTITY ent) + { + if (!Valid()) + return; + + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!pkChr->IsPC()) + return; + + int iDist = DISTANCE_APPROX(pkChr->GetX() - m_lX, pkChr->GetY() - m_lY); + + if (iDist > 300) + return; + + if (m_bEmpire && pkChr->GetEmpire() && m_bEmpire != pkChr->GetEmpire()) + return; + + if (pkChr->IsHack()) + return; + + if (!pkChr->CanHandleItem(false, true)) + return; + + if (m_bUseWarp) + pkChr->WarpSet(m_lTargetX, m_lTargetY); + else + { + pkChr->Show(pkChr->GetMapIndex(), m_lTargetX, m_lTargetY); + pkChr->Stop(); + } + } + + bool m_bInvalid; + bool m_bUseWarp; + + long m_lX; + long m_lY; + long m_lTargetX; + long m_lTargetY; + + BYTE m_bEmpire; + }; +} + +EVENTFUNC(warp_npc_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "warp_npc_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->GetSectree()) + { + ch->m_pkWarpNPCEvent = NULL; + return 0; + } + + FuncCheckWarp f(ch); + if (f.Valid()) + ch->GetSectree()->ForEachAround(f); + + return passes_per_sec / 2; +} + +void CHARACTER::StartWarpNPCEvent() +{ + if (m_pkWarpNPCEvent) + return; + + if (!IsWarp() && !IsGoto()) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2); +} + +void CHARACTER::SyncPacket() +{ + TEMP_BUFFER buf; + + TPacketCGSyncPositionElement elem; + + elem.dwVID = GetVID(); + elem.lX = GetX(); + elem.lY = GetY(); + + TPacketGCSyncPosition pack; + + pack.bHeader = HEADER_GC_SYNC_POSITION; + pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem); + + buf.write(pack); + buf.write(elem); + + PacketAround(buf.read_peek(), buf.size()); +} + +LPCHARACTER CHARACTER::GetMarryPartner() const +{ + return m_pkChrMarried; +} + +void CHARACTER::SetMarryPartner(LPCHARACTER ch) +{ + m_pkChrMarried = ch; +} + +int CHARACTER::GetMarriageBonus(DWORD dwItemVnum, bool bSum) +{ + if (IsNPC()) + return 0; + + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID()); + + if (!pMarriage) + return 0; + + return pMarriage->GetBonus(dwItemVnum, bSum, this); +} + +void CHARACTER::ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID) +{ + if (!IsPC()) + return; + + TPacketGCQuestConfirm p; + + p.header = HEADER_GC_QUEST_CONFIRM; + p.requestPID = dwRequestPID; + p.timeout = iTimeout; + strlcpy(p.msg, szMsg, sizeof(p.msg)); + + GetDesc()->Packet(p); +} + +int CHARACTER::GetPremiumRemainSeconds(BYTE bType) const +{ + if (bType >= PREMIUM_MAX_NUM) + return 0; + + return m_aiPremiumTimes[bType] - get_global_time(); +} + +bool CHARACTER::WarpToPID(DWORD dwPID) +{ + LPCHARACTER victim; + if ((victim = (CHARACTER_MANAGER::instance().FindByPID(dwPID)))) + { + int mapIdx = victim->GetMapIndex(); + if (IS_SUMMONABLE_ZONE(mapIdx)) + { + if (CAN_ENTER_ZONE(this, mapIdx)) + { + WarpSet(victim->GetX(), victim->GetY()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + } + else + { + CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID); + + if (!pcci) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¶ ° ƴմϴ.")); + return false; + } + + if (pcci->bChannel != g_bChannel) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d äο ֽϴ. ( ä %d)"), pcci->bChannel, g_bChannel); + return false; + } + else if (false == IS_SUMMONABLE_ZONE(pcci->lMapIndex)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + else + { + if (!CAN_ENTER_ZONE(this, pcci->lMapIndex)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + + TPacketGGFindPosition p; + p.header = HEADER_GG_FIND_POSITION; + p.dwFromPID = GetPlayerID(); + p.dwTargetPID = dwPID; + pcci->pkDesc->Packet(p); + + if (test_server) + ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for teleport"); + } + } + return true; +} + +// ADD_REFINE_BUILDING +CGuild* CHARACTER::GetRefineGuild() const +{ + LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID); + + return (chRefineNPC ? chRefineNPC->GetGuild() : NULL); +} + +bool CHARACTER::IsRefineThroughGuild() const +{ + return GetRefineGuild() != NULL; +} + +int CHARACTER::ComputeRefineFee(int iCost, int iMultiply) const +{ + CGuild* pGuild = GetRefineGuild(); + if (pGuild) + { + if (pGuild == GetGuild()) + return iCost * iMultiply * 9 / 10; + + LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID); + if (chRefineNPC && chRefineNPC->GetEmpire() != GetEmpire()) + return iCost * iMultiply * 3; + + return iCost * iMultiply; + } + else + return iCost; +} + +void CHARACTER::PayRefineFee(int iTotalMoney) +{ + int iFee = iTotalMoney / 10; + CGuild* pGuild = GetRefineGuild(); + + int iRemain = iTotalMoney; + + if (pGuild) + { + if (pGuild != GetGuild()) + { + pGuild->RequestDepositMoney(this, iFee); + iRemain -= iFee; + } + } + + PointChange(POINT_GOLD, -iRemain); +} +// END_OF_ADD_REFINE_BUILDING + +bool CHARACTER::IsHack(bool bSendMsg, bool bCheckShopOwner, int limittime) +{ + const int iPulse = thecore_pulse(); + + if (test_server) + bSendMsg = true; + + if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(limittime)) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("â %d ̳ ٸ ̵Ҽ ϴ."), limittime); + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(limittime)); + return true; + } + + if (bCheckShopOwner) + { + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ٸ ̵, Ҽ ϴ")); + + return true; + } + } + else + { + if (GetExchange() || GetMyShop() || IsOpenSafebox() || IsCubeOpen()) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ٸ ̵, Ҽ ϴ")); + + return true; + } + } + + //PREVENT_PORTAL_AFTER_EXCHANGE + if (iPulse - GetExchangeTime() < PASSES_PER_SEC(limittime)) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷ %d ̳ ٸ ̵ ϴ."), limittime ); + return true; + } + //END_PREVENT_PORTAL_AFTER_EXCHANGE + + //PREVENT_ITEM_COPY + if (iPulse - GetMyShopTime() < PASSES_PER_SEC(limittime)) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷ %d ̳ ٸ ̵ ϴ."), limittime); + return true; + } + + if (iPulse - GetRefineTime() < PASSES_PER_SEC(limittime)) + { + if (bSendMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ̳ ȯ,ȯθ ϴ."), limittime); + return true; + } + //END_PREVENT_ITEM_COPY + + return false; +} + +BOOL CHARACTER::IsMonarch() const +{ + //MONARCH_LIMIT + if (CMonarch::instance().IsMonarch(GetPlayerID(), GetEmpire())) + return true; + + return false; + + //END_MONARCH_LIMIT +} +void CHARACTER::Say(const std::string & s) +{ + struct ::packet_script packet_script; + + packet_script.header = HEADER_GC_SCRIPT; + packet_script.skin = 1; + packet_script.src_size = s.size(); + packet_script.size = packet_script.src_size + sizeof(struct packet_script); + + TEMP_BUFFER buf; + + buf.write(packet_script); + buf.write(s.data(), s.size()); + + if (IsPC()) + { + GetDesc()->Packet(buf.read_peek(), buf.size()); + } +} + +// +// Monarch +// +void CHARACTER::InitMC() +{ + for (int n = 0; n < MI_MAX; ++n) + { + m_dwMonarchCooltime[n] = thecore_pulse(); + } + + m_dwMonarchCooltimelimit[MI_HEAL] = PASSES_PER_SEC(MC_HEAL); + m_dwMonarchCooltimelimit[MI_WARP] = PASSES_PER_SEC(MC_WARP); + m_dwMonarchCooltimelimit[MI_TRANSFER] = PASSES_PER_SEC(MC_TRANSFER); + m_dwMonarchCooltimelimit[MI_TAX] = PASSES_PER_SEC(MC_TAX); + m_dwMonarchCooltimelimit[MI_SUMMON] = PASSES_PER_SEC(MC_SUMMON); + + m_dwMonarchCooltime[MI_HEAL] -= PASSES_PER_SEC(GetMCL(MI_HEAL)); + m_dwMonarchCooltime[MI_WARP] -= PASSES_PER_SEC(GetMCL(MI_WARP)); + m_dwMonarchCooltime[MI_TRANSFER] -= PASSES_PER_SEC(GetMCL(MI_TRANSFER)); + m_dwMonarchCooltime[MI_TAX] -= PASSES_PER_SEC(GetMCL(MI_TAX)); + m_dwMonarchCooltime[MI_SUMMON] -= PASSES_PER_SEC(GetMCL(MI_SUMMON)); +} + +DWORD CHARACTER::GetMC(enum MONARCH_INDEX e) const +{ + return m_dwMonarchCooltime[e]; +} + +void CHARACTER::SetMC(enum MONARCH_INDEX e) +{ + m_dwMonarchCooltime[e] = thecore_pulse(); +} + +bool CHARACTER::IsMCOK(enum MONARCH_INDEX e) const +{ + int iPulse = thecore_pulse(); + + if ((iPulse - GetMC(e)) < GetMCL(e)) + { + if (test_server) + sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e)); + + return false; + } + + if (test_server) + sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e)); + + return true; +} + +DWORD CHARACTER::GetMCL(enum MONARCH_INDEX e) const +{ + return m_dwMonarchCooltimelimit[e]; +} + +DWORD CHARACTER::GetMCLTime(enum MONARCH_INDEX e) const +{ + int iPulse = thecore_pulse(); + + if (test_server) + sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e)); + + return (GetMCL(e)) / passes_per_sec - (iPulse - GetMC(e)) / passes_per_sec; +} + +bool CHARACTER::IsSiegeNPC() const +{ + return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004); +} + +//------------------------------------------------ +void CHARACTER::UpdateDepositPulse() +{ + m_deposit_pulse = thecore_pulse() + PASSES_PER_SEC(60*5); +} + +bool CHARACTER::CanDeposit() const +{ + return (m_deposit_pulse == 0 || (m_deposit_pulse < thecore_pulse())); +} +//------------------------------------------------ + +ESex GET_SEX(LPCHARACTER ch) +{ + switch (ch->GetRaceNum()) + { + case MAIN_RACE_WARRIOR_M: + case MAIN_RACE_SURA_M: + case MAIN_RACE_ASSASSIN_M: + case MAIN_RACE_SHAMAN_M: +#ifdef ENABLE_WOLFMAN_CHARACTER + case MAIN_RACE_WOLFMAN_M: +#endif + return SEX_MALE; + + case MAIN_RACE_ASSASSIN_W: + case MAIN_RACE_SHAMAN_W: + case MAIN_RACE_WARRIOR_W: + case MAIN_RACE_SURA_W: + return SEX_FEMALE; + } + + // default sex = male + return SEX_MALE; +} + +int CHARACTER::GetHPPct() const +{ + if (GetMaxHP() <= 0) // @fixme136 + return 0; + return (GetHP() * 100) / GetMaxHP(); +} + +bool CHARACTER::IsBerserk() const +{ + if (m_pkMobInst != NULL) + return m_pkMobInst->m_IsBerserk; + else + return false; +} + +void CHARACTER::SetBerserk(bool mode) +{ + if (m_pkMobInst != NULL) + m_pkMobInst->m_IsBerserk = mode; +} + +bool CHARACTER::IsGodSpeed() const +{ + if (m_pkMobInst != NULL) + { + return m_pkMobInst->m_IsGodSpeed; + } + else + { + return false; + } +} + +void CHARACTER::SetGodSpeed(bool mode) +{ + if (m_pkMobInst != NULL) + { + m_pkMobInst->m_IsGodSpeed = mode; + + if (mode == true) + { + SetPoint(POINT_ATT_SPEED, 250); + } + else + { + SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed); + } + } +} + +bool CHARACTER::IsDeathBlow() const +{ + if (number(1, 100) <= m_pkMobData->m_table.bDeathBlowPoint) + { + return true; + } + else + { + return false; + } +} + +struct FFindReviver +{ + FFindReviver() + { + pChar = NULL; + HasReviver = false; + } + + void operator() (LPCHARACTER ch) + { + if (ch->IsMonster() != true) + { + return; + } + + if (ch->IsReviver() == true && pChar != ch && ch->IsDead() != true) + { + if (number(1, 100) <= ch->GetMobTable().bRevivePoint) + { + HasReviver = true; + pChar = ch; + } + } + } + + LPCHARACTER pChar; + bool HasReviver; +}; + +bool CHARACTER::HasReviverInParty() const +{ + LPPARTY party = GetParty(); + + if (party != NULL) + { + if (party->GetMemberCount() == 1) return false; + + FFindReviver f; + party->ForEachMemberPtr(f); + return f.HasReviver; + } + + return false; +} + +bool CHARACTER::IsRevive() const +{ + if (m_pkMobInst != NULL) + { + return m_pkMobInst->m_IsRevive; + } + + return false; +} + +void CHARACTER::SetRevive(bool mode) +{ + if (m_pkMobInst != NULL) + { + m_pkMobInst->m_IsRevive = mode; + } +} + +#define IS_SPEED_HACK_PLAYER(ch) (ch->m_speed_hack_count > SPEEDHACK_LIMIT_COUNT) + +EVENTFUNC(check_speedhack_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "check_speedhack_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (NULL == ch || ch->IsNPC()) + return 0; + + if (IS_SPEED_HACK_PLAYER(ch)) + { + // write hack log + LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count); + + if (g_bEnableSpeedHackCrash) + { + // close connection + LPDESC desc = ch->GetDesc(); + + if (desc) + { + DESC_MANAGER::instance().DestroyDesc(desc); + return 0; + } + } + } + + ch->m_speed_hack_count = 0; + + ch->ResetComboHackCount(); + return PASSES_PER_SEC(60); +} + +void CHARACTER::StartCheckSpeedHackEvent() +{ + if (m_pkCheckSpeedHackEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkCheckSpeedHackEvent = event_create(check_speedhack_event, info, PASSES_PER_SEC(60)); +} + +void CHARACTER::GoHome() +{ + WarpSet(EMPIRE_START_X(GetEmpire()), EMPIRE_START_Y(GetEmpire())); +} + +void CHARACTER::SendGuildName(CGuild* pGuild) +{ + if (NULL == pGuild) return; + + DESC *desc = GetDesc(); + + if (NULL == desc) return; + if (m_known_guild.find(pGuild->GetID()) != m_known_guild.end()) return; + + m_known_guild.emplace(pGuild->GetID()); + + TPacketGCGuildName pack; + memset(&pack, 0x00, sizeof(pack)); + + pack.header = HEADER_GC_GUILD; + pack.subheader = GUILD_SUBHEADER_GC_GUILD_NAME; + pack.size = sizeof(TPacketGCGuildName); + pack.guildID = pGuild->GetID(); + memcpy(pack.guildName, pGuild->GetName(), GUILD_NAME_MAX_LEN); + + desc->Packet(pack); +} + +void CHARACTER::SendGuildName(DWORD dwGuildID) +{ + SendGuildName(CGuildManager::instance().FindGuild(dwGuildID)); +} + +EVENTFUNC(destroy_when_idle_event) +{ + char_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "destroy_when_idle_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + + if (ch->GetVictim()) + { + return PASSES_PER_SEC(300); + } + + sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName()); + + ch->m_pkDestroyWhenIdleEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +void CHARACTER::StartDestroyWhenIdleEvent() +{ + if (m_pkDestroyWhenIdleEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300)); +} + +void CHARACTER::SetComboSequence(BYTE seq) +{ + m_bComboSequence = seq; +} + +BYTE CHARACTER::GetComboSequence() const +{ + return m_bComboSequence; +} + +void CHARACTER::SetLastComboTime(DWORD time) +{ + m_dwLastComboTime = time; +} + +DWORD CHARACTER::GetLastComboTime() const +{ + return m_dwLastComboTime; +} + +void CHARACTER::SetValidComboInterval(int interval) +{ + m_iValidComboInterval = interval; +} + +int CHARACTER::GetValidComboInterval() const +{ + return m_iValidComboInterval; +} + +BYTE CHARACTER::GetComboIndex() const +{ + return m_bComboIndex; +} + +void CHARACTER::IncreaseComboHackCount(int k) +{ + m_iComboHackCount += k; + + if (m_iComboHackCount >= 10) + { + if (GetDesc()) + if (GetDesc()->DelayedDisconnect(number(2, 7))) + { + sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount); + LogManager::instance().HackLog("Combo", this); + } + } +} + +void CHARACTER::ResetComboHackCount() +{ + m_iComboHackCount = 0; +} + +void CHARACTER::SkipComboAttackByTime(int interval) +{ + m_dwSkipComboAttackByTime = get_dword_time() + interval; +} + +DWORD CHARACTER::GetSkipComboAttackByTime() const +{ + return m_dwSkipComboAttackByTime; +} + +void CHARACTER::ResetChatCounter() +{ + m_bChatCounter = 0; +} + +BYTE CHARACTER::IncreaseChatCounter() +{ + return ++m_bChatCounter; +} + +BYTE CHARACTER::GetChatCounter() const +{ + return m_bChatCounter; +} + +bool CHARACTER::IsRiding() const +{ + return IsHorseRiding() || GetMountVnum(); +} + +bool CHARACTER::CanWarp() const +{ + const int iPulse = thecore_pulse(); + const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime); + + if ((iPulse - GetSafeboxLoadTime()) < limit_time) + return false; + + if ((iPulse - GetExchangeTime()) < limit_time) + return false; + + if ((iPulse - GetMyShopTime()) < limit_time) + return false; + + if ((iPulse - GetRefineTime()) < limit_time) + return false; + + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + return false; + + return true; +} + +DWORD CHARACTER::GetNextExp() const +{ + if (PLAYER_MAX_LEVEL_CONST < GetLevel()) + return 2500000000u; + else + return exp_table[GetLevel()]; +} + +int CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const +{ + return CTableBySkill::instance().GetSkillPowerByLevelFromType(GetJob(), GetSkillGroup(), MINMAX(0, level, SKILL_MAX_LEVEL), bMob); +} + +#if defined(ENABLE_CHEQUE_SYSTEM) && defined(ENABLE_WON_EXCHANGE_WINDOW) +bool CHARACTER::WonExchange(const std::string & option, int value) +{ + if (!CanWarp()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot exchange the won right now. Close some windows.")); + return false; + } + + if (value <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid won exchange value parameter %d."), value); + return false; + } + + if (option == "sell") + { + const auto totalYang = static_cast(value) * YANG_PER_CHEQUE; + if (GetCheque() < value) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You don't have enough won for the exchange. Required %d, owned %d."), value, GetCheque()); + return false; + } + if (GetGold() + totalYang >= GOLD_MAX) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have too many yangs for the exchange. Reduce your yangs first.")); + return false; + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You successfully sold %d won for %s yangs."), value, FormatNumberWithDots(totalYang).c_str()); + PointChange(POINT_GOLD, totalYang, true); + PointChange(POINT_CHEQUE, -value, true); + } + else if (option == "buy") + { + const auto totalYang = static_cast(value) * YANG_PER_CHEQUE; + if (GetGold() < totalYang) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You don't have enough yangs for the exchange. Required %s, owned %d."), FormatNumberWithDots(totalYang).c_str(), GetGold()); + return false; + } + if (GetCheque() + value > CHEQUE_MAX) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have too many won for the exchange. Reduce your won first.")); + return false; + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You successfully bought %d won for %s yangs."), value, FormatNumberWithDots(totalYang).c_str()); + PointChange(POINT_GOLD, -totalYang, true); + PointChange(POINT_CHEQUE, value, true); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid parameter for the won exchange. Reported.")); + return false; + } + + SetExchangeTime(); + return true; +} +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM +std::vector CHARACTER::GetAcceMaterials() +{ + if (!m_PlayerSlots) + return std::vector{nullptr, nullptr}; + return std::vector{ITEM_MANAGER::instance().Find(m_PlayerSlots->pAcceMaterials[0].id), ITEM_MANAGER::instance().Find(m_PlayerSlots->pAcceMaterials[1].id)}; +} + +const TItemPosEx* CHARACTER::GetAcceMaterialsInfo() +{ + if (!m_PlayerSlots) + return nullptr; + return m_PlayerSlots->pAcceMaterials.data(); +} + +void CHARACTER::SetAcceMaterial(int pos, LPITEM ptr) +{ + if (!m_PlayerSlots) + return; + + if (pos < 0 || pos >= ACCE_WINDOW_MAX_MATERIALS) + return; + if (!ptr) + m_PlayerSlots->pAcceMaterials[pos] = {}; + else + { + m_PlayerSlots->pAcceMaterials[pos].id = ptr->GetID(); + m_PlayerSlots->pAcceMaterials[pos].pos.cell = ptr->GetCell(); + m_PlayerSlots->pAcceMaterials[pos].pos.window_type = ptr->GetWindow(); + } +} + +void CHARACTER::OpenAcce(bool bCombination) +{ + if (IsAcceOpened(bCombination)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The acce window it's already opened.")); + return; + } + + if (bCombination) + { + if (m_bAcceAbsorption) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Before you may close the acce absorption window.")); + return; + } + + m_bAcceCombination = true; + } + else + { + if (m_bAcceCombination) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Before you may close the acce combine window.")); + return; + } + + m_bAcceAbsorption = true; + } + + TItemPos tPos; + tPos.window_type = INVENTORY; + tPos.cell = 0; + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_GC_OPEN; + sPacket.bWindow = bCombination; + sPacket.dwPrice = 0; + sPacket.bPos = 0; + sPacket.tPos = tPos; + sPacket.dwItemVnum = 0; + sPacket.dwMinAbs = 0; + sPacket.dwMaxAbs = 0; + GetDesc()->Packet(sPacket); + + ClearAcceMaterials(); +} + +void CHARACTER::CloseAcce() +{ + if ((!m_bAcceCombination) && (!m_bAcceAbsorption)) + return; + + bool bWindow = m_bAcceCombination; + + TItemPos tPos; + tPos.window_type = INVENTORY; + tPos.cell = 0; + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_GC_CLOSE; + sPacket.bWindow = bWindow; + sPacket.dwPrice = 0; + sPacket.bPos = 0; + sPacket.tPos = tPos; + sPacket.dwItemVnum = 0; + sPacket.dwMinAbs = 0; + sPacket.dwMaxAbs = 0; + GetDesc()->Packet(sPacket); + + if (bWindow) + m_bAcceCombination = false; + else + m_bAcceAbsorption = false; + + ClearAcceMaterials(); +} + +void CHARACTER::ClearAcceMaterials() +{ + auto pkItemMaterial = GetAcceMaterials(); + for (int i = 0; i < ACCE_WINDOW_MAX_MATERIALS; ++i) + { + if (!pkItemMaterial[i]) + continue; + + pkItemMaterial[i]->Lock(false); + pkItemMaterial[i] = NULL; + SetAcceMaterial(i, nullptr); + } +} + +bool CHARACTER::AcceIsSameGrade(long lGrade) +{ + auto pkItemMaterial = GetAcceMaterials(); + if (!pkItemMaterial[0]) + return false; + return pkItemMaterial[0]->GetValue(ACCE_GRADE_VALUE_FIELD) == lGrade; +} + +DWORD CHARACTER::GetAcceCombinePrice(long lGrade) +{ + DWORD dwPrice = 0; + switch (lGrade) + { + case 2: + { + dwPrice = ACCE_GRADE_2_PRICE; + } + break; + case 3: + { + dwPrice = ACCE_GRADE_3_PRICE; + } + break; + case 4: + { + dwPrice = ACCE_GRADE_4_PRICE; + } + break; + default: + { + dwPrice = ACCE_GRADE_1_PRICE; + } + break; + } + + return dwPrice; +} + +BYTE CHARACTER::CheckEmptyMaterialSlot() +{ + auto pkItemMaterial = GetAcceMaterials(); + for (int i = 0; i < ACCE_WINDOW_MAX_MATERIALS; ++i) + { + if (!pkItemMaterial[i]) + return i; + } + + return 255; +} + +void CHARACTER::GetAcceCombineResult(DWORD & dwItemVnum, DWORD & dwMinAbs, DWORD & dwMaxAbs) +{ + auto pkItemMaterial = GetAcceMaterials(); + + if (m_bAcceCombination) + { + if ((pkItemMaterial[0]) && (pkItemMaterial[1])) + { + long lVal = pkItemMaterial[0]->GetValue(ACCE_GRADE_VALUE_FIELD); + if (lVal == 4) + { + dwItemVnum = pkItemMaterial[0]->GetOriginalVnum(); + dwMinAbs = pkItemMaterial[0]->GetSocket(ACCE_ABSORPTION_SOCKET); + DWORD dwMaxAbsCalc = (dwMinAbs + ACCE_GRADE_4_ABS_RANGE > ACCE_GRADE_4_ABS_MAX ? ACCE_GRADE_4_ABS_MAX : (dwMinAbs + ACCE_GRADE_4_ABS_RANGE)); + dwMaxAbs = dwMaxAbsCalc; + } + else + { + DWORD dwMaskVnum = pkItemMaterial[0]->GetOriginalVnum(); + TItemTable * pTable = ITEM_MANAGER::instance().GetTable(dwMaskVnum + 1); + if (pTable) + dwMaskVnum += 1; + + dwItemVnum = dwMaskVnum; + switch (lVal) + { + case 2: + { + dwMinAbs = ACCE_GRADE_3_ABS; + dwMaxAbs = ACCE_GRADE_3_ABS; + } + break; + case 3: + { + dwMinAbs = ACCE_GRADE_4_ABS_MIN; + dwMaxAbs = ACCE_GRADE_4_ABS_MAX_COMB; + } + break; + default: + { + dwMinAbs = ACCE_GRADE_2_ABS; + dwMaxAbs = ACCE_GRADE_2_ABS; + } + break; + } + } + } + else + { + dwItemVnum = 0; + dwMinAbs = 0; + dwMaxAbs = 0; + } + } + else + { + if ((pkItemMaterial[0]) && (pkItemMaterial[1])) + { + dwItemVnum = pkItemMaterial[0]->GetOriginalVnum(); + dwMinAbs = pkItemMaterial[0]->GetSocket(ACCE_ABSORPTION_SOCKET); + dwMaxAbs = dwMinAbs; + } + else + { + dwItemVnum = 0; + dwMinAbs = 0; + dwMaxAbs = 0; + } + } +} + +void CHARACTER::AddAcceMaterial(TItemPos tPos, BYTE bPos) +{ + if (bPos >= ACCE_WINDOW_MAX_MATERIALS) + { + if (bPos == 255) + { + bPos = CheckEmptyMaterialSlot(); + if (bPos >= ACCE_WINDOW_MAX_MATERIALS) + return; + } + else + return; + } + + LPITEM pkItem = GetItem(tPos); + if (!pkItem) + return; + else if ((pkItem->GetCell() >= INVENTORY_MAX_NUM) || (pkItem->IsEquipped()) || (tPos.IsBeltInventoryPosition()) || (pkItem->IsDragonSoul())) + return; + else if ((pkItem->GetType() != ITEM_COSTUME) && (m_bAcceCombination)) + return; + else if ((pkItem->GetType() != ITEM_COSTUME) && (bPos == 0) && (m_bAcceAbsorption)) + return; + else if (pkItem->isLocked()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add locked items.")); + return; + } +#ifdef __SOULBINDING_SYSTEM__ + else if ((pkItem->IsBind()) || (pkItem->IsUntilBind())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't add binded items.")); + return; + } +#endif + else if ((m_bAcceCombination) && (bPos == 1) && (!AcceIsSameGrade(pkItem->GetValue(ACCE_GRADE_VALUE_FIELD)))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can combine just accees of same grade.")); + return; + } + else if ((m_bAcceCombination) && (pkItem->GetSocket(ACCE_ABSORPTION_SOCKET) >= ACCE_GRADE_4_ABS_MAX)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This acce got already maximum absorption chance.")); + return; + } + else if ((bPos == 1) && (m_bAcceAbsorption)) + { + if ((pkItem->GetType() != ITEM_WEAPON) && (pkItem->GetType() != ITEM_ARMOR)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can absorb just the bonuses from armors and weapons.")); + return; + } + else if ((pkItem->GetType() == ITEM_ARMOR) && (pkItem->GetSubType() != ARMOR_BODY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can absorb just the bonuses from armors and weapons.")); + return; + } + } + else if ((pkItem->GetSubType() != COSTUME_ACCE) && (m_bAcceCombination)) + return; + else if ((pkItem->GetSubType() != COSTUME_ACCE) && (bPos == 0) && (m_bAcceAbsorption)) + return; + else if ((pkItem->GetSocket(ACCE_ABSORBED_SOCKET) > 0) && (bPos == 0) && (m_bAcceAbsorption)) + return; + + auto pkItemMaterial = GetAcceMaterials(); + if ((bPos == 1) && (!pkItemMaterial[0])) + return; + + if (pkItemMaterial[bPos]) + return; + + SetAcceMaterial(bPos, pkItem); + pkItemMaterial[bPos] = pkItem; + pkItemMaterial[bPos]->Lock(true); + + DWORD dwItemVnum, dwMinAbs, dwMaxAbs; + GetAcceCombineResult(dwItemVnum, dwMinAbs, dwMaxAbs); + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_GC_ADDED; + sPacket.bWindow = m_bAcceCombination; + sPacket.dwPrice = GetAcceCombinePrice(pkItem->GetValue(ACCE_GRADE_VALUE_FIELD)); + sPacket.bPos = bPos; + sPacket.tPos = tPos; + sPacket.dwItemVnum = dwItemVnum; + sPacket.dwMinAbs = dwMinAbs; + sPacket.dwMaxAbs = dwMaxAbs; + GetDesc()->Packet(sPacket); +} + +void CHARACTER::RemoveAcceMaterial(BYTE bPos) +{ + if (bPos >= ACCE_WINDOW_MAX_MATERIALS) + return; + + auto pkItemMaterial = GetAcceMaterials(); + + DWORD dwPrice = 0; + + if (bPos == 1) + { + if (pkItemMaterial[bPos]) + { + pkItemMaterial[bPos]->Lock(false); + pkItemMaterial[bPos] = NULL; + SetAcceMaterial(bPos, nullptr); + } + + if (pkItemMaterial[0]) + dwPrice = GetAcceCombinePrice(pkItemMaterial[0]->GetValue(ACCE_GRADE_VALUE_FIELD)); + } + else + ClearAcceMaterials(); + + TItemPos tPos; + tPos.window_type = INVENTORY; + tPos.cell = 0; + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_GC_REMOVED; + sPacket.bWindow = m_bAcceCombination; + sPacket.dwPrice = dwPrice; + sPacket.bPos = bPos; + sPacket.tPos = tPos; + sPacket.dwItemVnum = 0; + sPacket.dwMinAbs = 0; + sPacket.dwMaxAbs = 0; + GetDesc()->Packet(sPacket); +} + +BYTE CHARACTER::CanRefineAcceMaterials() +{ + BYTE bReturn = 0; + if (!GetDesc()) + return bReturn; + + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + return bReturn; + + auto materialInfo = GetAcceMaterialsInfo(); + auto pkItemMaterial = GetAcceMaterials(); + if (!pkItemMaterial[0] || !pkItemMaterial[1]) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial null"); + return bReturn; + } + else if (pkItemMaterial[0]->GetOwner()!=this || pkItemMaterial[1]->GetOwner() != this) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial different ownership"); + return bReturn; + } + else if (pkItemMaterial[0]->IsEquipped() || pkItemMaterial[1]->IsEquipped()) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial equipped"); + return bReturn; + } + else if (pkItemMaterial[0]->GetWindow() != INVENTORY || pkItemMaterial[1]->GetWindow() != INVENTORY) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial not in INVENTORY"); + return bReturn; + } + else if (!materialInfo[0].id || !materialInfo[1].id) + { + sys_err("CanRefineAcceMaterials: materialInfo id 0"); + return bReturn; + } + else if (materialInfo[0].pos.cell != pkItemMaterial[0]->GetCell() || materialInfo[1].pos.cell != pkItemMaterial[1]->GetCell()) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial wrong cell"); + return bReturn; + } + else if (materialInfo[0].pos.window_type != pkItemMaterial[0]->GetWindow() || materialInfo[1].pos.window_type != pkItemMaterial[1]->GetWindow()) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial wrong window_type"); + return bReturn; + } + + if (m_bAcceCombination) + { + if (!AcceIsSameGrade(pkItemMaterial[1]->GetValue(ACCE_GRADE_VALUE_FIELD))) + { + sys_err("CanRefineAcceMaterials: pkItemMaterial different acce grade"); + return bReturn; + } + + for (int i = 0; i < ACCE_WINDOW_MAX_MATERIALS; ++i) + { + if (pkItemMaterial[i]) + { + if ((pkItemMaterial[i]->GetType() == ITEM_COSTUME) && (pkItemMaterial[i]->GetSubType() == COSTUME_ACCE)) + bReturn = 1; + else + { + bReturn = 0; + break; + } + } + else + { + bReturn = 0; + break; + } + } + } + else if (m_bAcceAbsorption) + { + if ((pkItemMaterial[0]) && (pkItemMaterial[1])) + { + if ((pkItemMaterial[0]->GetType() == ITEM_COSTUME) && (pkItemMaterial[0]->GetSubType() == COSTUME_ACCE)) + bReturn = 2; + else + bReturn = 0; + + if ((pkItemMaterial[1]->GetType() == ITEM_WEAPON) || ((pkItemMaterial[1]->GetType() == ITEM_ARMOR) && (pkItemMaterial[1]->GetSubType() == ARMOR_BODY))) + bReturn = 2; + else + bReturn = 0; + + if (pkItemMaterial[0]->GetSocket(ACCE_ABSORBED_SOCKET) > 0) + bReturn = 0; + } + else + bReturn = 0; + } + + return bReturn; +} + +void CHARACTER::RefineAcceMaterials() +{ + BYTE bCan = CanRefineAcceMaterials(); + if (bCan == 0) + return; + + auto pkItemMaterial = GetAcceMaterials(); + + DWORD dwItemVnum, dwMinAbs, dwMaxAbs; + GetAcceCombineResult(dwItemVnum, dwMinAbs, dwMaxAbs); + DWORD dwPrice = GetAcceCombinePrice(pkItemMaterial[0]->GetValue(ACCE_GRADE_VALUE_FIELD)); + + if (bCan == 1) + { + int iSuccessChance = 0; + auto lVal = pkItemMaterial[0]->GetValue(ACCE_GRADE_VALUE_FIELD); + switch (lVal) + { + break; case 2: + iSuccessChance = ACCE_COMBINE_GRADE_2; + break; case 3: + iSuccessChance = ACCE_COMBINE_GRADE_3; + break; case 4: + iSuccessChance = ACCE_COMBINE_GRADE_4; + break; default: + iSuccessChance = ACCE_COMBINE_GRADE_1; + } + + if (GetGold() < dwPrice) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You don't have enough Yang.")); + return; + } + + int iChance = number(1, 100); + bool bSucces = (iChance <= iSuccessChance); + if (bSucces) + { + LPITEM pkItem = ITEM_MANAGER::instance().CreateItem(dwItemVnum, 1, 0, false); + if (!pkItem) + { + sys_err("%d can't be created.", dwItemVnum); + return; + } + + ITEM_MANAGER::CopyAllAttrTo(pkItemMaterial[0], pkItem); + LogManager::instance().ItemLog(this, pkItem, "COMBINE SUCCESS", pkItem->GetName()); + DWORD dwAbs = (dwMinAbs == dwMaxAbs ? dwMinAbs : number(dwMinAbs + 1, dwMaxAbs)); + pkItem->SetSocket(ACCE_ABSORPTION_SOCKET, dwAbs); + pkItem->SetSocket(ACCE_ABSORBED_SOCKET, pkItemMaterial[0]->GetSocket(ACCE_ABSORBED_SOCKET)); + + PointChange(POINT_GOLD, -dwPrice); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, pkItemMaterial[0]->GetVnum(), -dwPrice); + + WORD wCell = pkItemMaterial[0]->GetCell(); + ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[0], "COMBINE (REFINE SUCCESS)"); + ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "COMBINE (REFINE SUCCESS)"); + + pkItem->AddToCharacter(this, TItemPos(INVENTORY, wCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkItem); + pkItem->AttrLog(); + + if (lVal == 4) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("New absorption rate: %d%"), dwAbs); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Success.")); + + EffectPacket(SE_EFFECT_ACCE_SUCESS_ABSORB); + LogManager::instance().AcceLog(GetPlayerID(), GetX(), GetY(), dwItemVnum, pkItem->GetID(), 1, dwAbs, 1); + + ClearAcceMaterials(); + } + else + { + PointChange(POINT_GOLD, -dwPrice); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, pkItemMaterial[0]->GetVnum(), -dwPrice); + + ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "COMBINE (REFINE FAIL)"); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Failed.")); + + LogManager::instance().AcceLog(GetPlayerID(), GetX(), GetY(), dwItemVnum, 0, 0, 0, 0); + + pkItemMaterial[1] = NULL; + } + + TItemPos tPos; + tPos.window_type = INVENTORY; + tPos.cell = 0; + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_CG_REFINED; + sPacket.bWindow = m_bAcceCombination; + sPacket.dwPrice = dwPrice; + sPacket.bPos = 0; + sPacket.tPos = tPos; + sPacket.dwItemVnum = 0; + sPacket.dwMinAbs = 0; + if (bSucces) + sPacket.dwMaxAbs = 100; + else + sPacket.dwMaxAbs = 0; + + GetDesc()->Packet(sPacket); + } + else + { + pkItemMaterial[1]->CopyAttributeTo(pkItemMaterial[0]); + LogManager::instance().ItemLog(this, pkItemMaterial[0], "ABSORB (REFINE SUCCESS)", pkItemMaterial[0]->GetName()); + pkItemMaterial[0]->SetSocket(ACCE_ABSORBED_SOCKET, pkItemMaterial[1]->GetOriginalVnum()); + #ifdef USE_ACCE_ABSORB_WITH_NO_NEGATIVE_BONUS + for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + { + if (pkItemMaterial[0]->GetAttributeValue(i) < 0) + pkItemMaterial[0]->SetForceAttribute(i, pkItemMaterial[0]->GetAttributeType(i), 0); + } + #endif + ITEM_MANAGER::instance().RemoveItem(pkItemMaterial[1], "ABSORBED (REFINE SUCCESS)"); + + ITEM_MANAGER::instance().FlushDelayedSave(pkItemMaterial[0]); + pkItemMaterial[0]->AttrLog(); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Success.")); + EffectPacket(SE_EFFECT_ACCE_SUCESS_ABSORB); + + ClearAcceMaterials(); + + TItemPos tPos; + tPos.window_type = INVENTORY; + tPos.cell = 0; + + TPacketAcce sPacket; + sPacket.header = HEADER_GC_ACCE; + sPacket.subheader = ACCE_SUBHEADER_CG_REFINED; + sPacket.bWindow = m_bAcceCombination; + sPacket.dwPrice = dwPrice; + sPacket.bPos = 255; + sPacket.tPos = tPos; + sPacket.dwItemVnum = 0; + sPacket.dwMinAbs = 0; + sPacket.dwMaxAbs = 1; + GetDesc()->Packet(sPacket); + } +} + +bool CHARACTER::CleanAcceAttr(LPITEM pkItem, LPITEM pkTarget) +{ + if (!CanHandleItem()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Close the other windows.")); + return false; + } + else if ((!pkItem) || (!pkTarget)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("pkItem or pkTarget nullptr.")); + return false; + } + else if ((pkTarget->GetType() != ITEM_COSTUME) && (pkTarget->GetSubType() != COSTUME_ACCE)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't use it on non-sash items.")); + return false; + } + + if (pkTarget->GetSocket(ACCE_ABSORBED_SOCKET) <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The sash item has no absorbed socket.")); + return false; + } + + pkTarget->SetSocket(ACCE_ABSORBED_SOCKET, 0); + for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i) + pkTarget->SetForceAttribute(i, 0, 0); + + LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT (CLEAN ATTR)", pkTarget->GetName()); + return true; +} +#endif + +#ifdef ENABLE_MOVE_CHANNEL +bool CHARACTER::ChangeChannel(BYTE newChannel) +{ + if (!CanWarp() || IsDead()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have to wait few seconds before changing channel.")); + return false; + } + + if (newChannel == 99 || g_bChannel == 99) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot change channel from this map.")); + return false; + } + + if (!IsPC()) + return false; + + if (newChannel == g_bChannel) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You are already in this channel. %d"), newChannel); + return false; + } + + long newMapIndex; + long newAddr; + WORD newPort; + if (!CMapLocation::instance().Get(GetX(), GetY(), newMapIndex, newAddr, newPort, newChannel)) + { + sys_err("cannot find map location index %d x %d y %d name %s", newMapIndex, GetX(), GetY(), GetName()); + return false; + } + + long curMapIndex; + long curAddr; + WORD curPort; + CMapLocation::instance().Get(GetX(), GetY(), curMapIndex, curAddr, curPort, g_bChannel); + + if (curMapIndex != newMapIndex) + { + const TMapRegion *rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(newMapIndex); + DESC_MANAGER::instance().SendClientPackageSDBToLoadMap(GetDesc(), rMapRgn->strMapName.c_str()); + } + + Stop(); + Save(); + + if (GetSectree()) + { + GetSectree()->RemoveEntity(this); + ViewCleanup(); + EncodeRemovePacket(this); + } + + m_lWarpMapIndex = GetMapIndex(); + m_posWarp.x = GetX(); + m_posWarp.y = GetY(); + + sys_log(0, "ChangeChannel %s %d %d current map %d target map %d (%d %d)", GetName(), GetX(), GetY(), GetMapIndex(), GetMapIndex(), newAddr, newPort); + + TPacketGCWarp p{}; + p.bHeader = HEADER_GC_WARP; + p.lX = GetX(); + p.lY = GetY(); + p.lAddr = newAddr; +#ifdef ENABLE_NEWSTUFF + if (!g_stProxyIP.empty()) + p.lAddr = inet_addr(g_stProxyIP.c_str()); +#endif + p.wPort = newPort; + GetDesc()->Packet(p); + + Stop(); + return true; +} +#endif + +#ifdef USE_PET_SEAL_ON_LOGIN +bool CHARACTER::SummonPetFromItem(LPITEM item) +{ + if (!item) + { + ChatPacket(CHAT_TYPE_INFO, "Item Pet nullptr"); + return false; + } + + auto mobVnum = item->GetValue(0); + if (!mobVnum) + { + ChatPacket(CHAT_TYPE_INFO, "Item Pet %d with no mob in value0", item->GetVnum()); + return false; + } + auto * petSystem = GetPetSystem(); + if (!petSystem) + return false; + + if (petSystem->CountSummoned()) + { + petSystem->UnsummonAll(); + return false; + } + else + { + // summon + constexpr bool bFromFar = false; + const auto * pkMob = CMobManager::instance().Get(mobVnum); + const auto * petName = (pkMob) ? pkMob->GetLocaleName() : ""; + petSystem->Summon(mobVnum, item, petName, bFromFar); + // spawn effect + constexpr const char * spawn_effect_file_name = "d:\\ymir work\\effect\\etc\\appear_die\\npc2_appear.mse"; + auto * petActor = petSystem->GetByVnum(mobVnum); + if (petActor && petActor->GetCharacter() && item->GetSubType() == PET_PAY) + petActor->GetCharacter()->SpecificEffectPacket(spawn_effect_file_name); + } + return true; +} + +void CHARACTER::EnterPet() +{ + for (auto & itemID : m_loadedPetItems) + { + auto item = ITEM_MANAGER::instance().Find(itemID); + if (!item) + continue; + if (item->GetOwner() != this) + continue; + SummonPetFromItem(item); + } +} +#endif + +#ifdef ENABLE_GUILD_TOKEN_AUTH +bool CHARACTER::IsGuildMaster() const +{ + const auto guild = GetGuild(); + return (guild && GetPlayerID() == guild->GetMasterPID()); +} + +uint64_t CHARACTER::GetGuildToken() const +{ + const auto guild = GetGuild(); + if (guild && IsGuildMaster()) + return guild->GetToken(); + return 0; +} + +void CHARACTER::SendGuildToken() +{ + CGuildManager::instance().SendGuildToken(this, GetGuildToken()); +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char.h b/source-server/Srcs/Server/game/src/char.h new file mode 100644 index 000000000..3e847ee24 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char.h @@ -0,0 +1,2125 @@ +#ifndef __INC_METIN_II_CHAR_H__ +#define __INC_METIN_II_CHAR_H__ + +#include +#include "../../common/stl.h" +#include "entity.h" +#include "FSM.h" +#include "horse_rider.h" +#include "vid.h" +#include "constants.h" +#include "affect.h" +#include "affect_flag.h" +#include "cube.h" +#include "mining.h" +#include "../../common/CommonDefines.h" +#include +#include +#ifdef ENABLE_ACCE_COSTUME_SYSTEM +#include +#endif +#ifdef USE_PET_SEAL_ON_LOGIN +#include +#endif + +#define ENABLE_ANTI_CMD_FLOOD +#define ENABLE_OPEN_SHOP_WITH_ARMOR +#define ENABLE_SKILL_COOLDOWN_CHECK + +enum eMountType {MOUNT_TYPE_NONE=0, MOUNT_TYPE_NORMAL=1, MOUNT_TYPE_COMBAT=2, MOUNT_TYPE_MILITARY=3}; +eMountType GetMountLevelByVnum(DWORD dwMountVnum, bool IsNew); +const DWORD GetRandomSkillVnum(BYTE bJob = JOB_MAX_NUM); + +class CBuffOnAttributes; +class CPetSystem; + +#define INSTANT_FLAG_DEATH_PENALTY (1 << 0) +#define INSTANT_FLAG_SHOP (1 << 1) +#define INSTANT_FLAG_EXCHANGE (1 << 2) +#define INSTANT_FLAG_STUN (1 << 3) +#define INSTANT_FLAG_NO_REWARD (1 << 4) + +#define AI_FLAG_NPC (1 << 0) +#define AI_FLAG_AGGRESSIVE (1 << 1) +#define AI_FLAG_HELPER (1 << 2) +#define AI_FLAG_STAYZONE (1 << 3) + +#define SET_OVER_TIME(ch, time) (ch)->SetOverTime(time) + +extern int g_nPortalLimitTime; + +enum +{ + MAIN_RACE_WARRIOR_M, + MAIN_RACE_ASSASSIN_W, + MAIN_RACE_SURA_M, + MAIN_RACE_SHAMAN_W, + MAIN_RACE_WARRIOR_W, + MAIN_RACE_ASSASSIN_M, + MAIN_RACE_SURA_W, + MAIN_RACE_SHAMAN_M, +#ifdef ENABLE_WOLFMAN_CHARACTER + MAIN_RACE_WOLFMAN_M, +#endif + MAIN_RACE_MAX_NUM, +}; + +enum +{ + POISON_LENGTH = 30, +#ifdef ENABLE_WOLFMAN_CHARACTER + BLEEDING_LENGTH = 30, +#endif + STAMINA_PER_STEP = 1, + SAFEBOX_PAGE_SIZE = 9, + AI_CHANGE_ATTACK_POISITION_TIME_NEAR = 10000, + AI_CHANGE_ATTACK_POISITION_TIME_FAR = 1000, + AI_CHANGE_ATTACK_POISITION_DISTANCE = 100, + SUMMON_MONSTER_COUNT = 3, +}; + +enum +{ + FLY_NONE, + FLY_EXP, + FLY_HP_MEDIUM, + FLY_HP_BIG, + FLY_SP_SMALL, + FLY_SP_MEDIUM, + FLY_SP_BIG, + FLY_FIREWORK1, + FLY_FIREWORK2, + FLY_FIREWORK3, + FLY_FIREWORK4, + FLY_FIREWORK5, + FLY_FIREWORK6, + FLY_FIREWORK_CHRISTMAS, + FLY_CHAIN_LIGHTNING, + FLY_HP_SMALL, + FLY_SKILL_MUYEONG, +#ifdef ENABLE_QUIVER_SYSTEM + FLY_QUIVER_ATTACK_NORMAL, +#endif +}; + +enum EDamageType +{ + DAMAGE_TYPE_NONE, + DAMAGE_TYPE_NORMAL, + DAMAGE_TYPE_NORMAL_RANGE, + DAMAGE_TYPE_MELEE, + DAMAGE_TYPE_RANGE, + DAMAGE_TYPE_FIRE, + DAMAGE_TYPE_ICE, + DAMAGE_TYPE_ELEC, + DAMAGE_TYPE_MAGIC, + DAMAGE_TYPE_POISON, + DAMAGE_TYPE_SPECIAL, +#ifdef ENABLE_WOLFMAN_CHARACTER + DAMAGE_TYPE_BLEEDING, +#endif +}; + +enum DamageFlag +{ + DAMAGE_NORMAL = (1 << 0), + DAMAGE_POISON = (1 << 1), + DAMAGE_DODGE = (1 << 2), + DAMAGE_BLOCK = (1 << 3), + DAMAGE_PENETRATE= (1 << 4), + DAMAGE_CRITICAL = (1 << 5), +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + DAMAGE_BLEEDING = (1 << 6), +#endif +}; + +enum EPointTypes +{ + POINT_NONE, // 0 + POINT_LEVEL, // 1 + POINT_VOICE, // 2 + POINT_EXP, // 3 + POINT_NEXT_EXP, // 4 + POINT_HP, // 5 + POINT_MAX_HP, // 6 + POINT_SP, // 7 + POINT_MAX_SP, // 8 + POINT_STAMINA, // 9 + POINT_MAX_STAMINA, // 10 + + POINT_GOLD, // 11 + POINT_ST, // 12 + POINT_HT, // 13 + POINT_DX, // 14 + POINT_IQ, // 15 + POINT_DEF_GRADE, // 16 ... + POINT_ATT_SPEED, // 17 + POINT_ATT_GRADE, // 18 + POINT_MOV_SPEED, // 19 + POINT_CLIENT_DEF_GRADE, // 20 + POINT_CASTING_SPEED, // 21 + POINT_MAGIC_ATT_GRADE, // 22 + POINT_MAGIC_DEF_GRADE, // 23 + POINT_EMPIRE_POINT, // 24 + POINT_LEVEL_STEP, // 25 + POINT_STAT, // 26 + POINT_SUB_SKILL, // 27 + POINT_SKILL, // 28 + POINT_WEAPON_MIN, // 29 + POINT_WEAPON_MAX, // 30 + POINT_PLAYTIME, // 31 + POINT_HP_REGEN, // 32 + POINT_SP_REGEN, // 33 + + POINT_BOW_DISTANCE, // 34 + + POINT_HP_RECOVERY, // 35 + POINT_SP_RECOVERY, // 36 + + POINT_POISON_PCT, // 37 + POINT_STUN_PCT, // 38 + POINT_SLOW_PCT, // 39 + POINT_CRITICAL_PCT, // 40 + POINT_PENETRATE_PCT, // 41 + POINT_CURSE_PCT, // 42 + + POINT_ATTBONUS_HUMAN, // 43 + POINT_ATTBONUS_ANIMAL, // 44 + POINT_ATTBONUS_ORC, // 45 + POINT_ATTBONUS_MILGYO, // 46 + POINT_ATTBONUS_UNDEAD, // 47 + POINT_ATTBONUS_DEVIL, // 48 + POINT_ATTBONUS_INSECT, // 49 + POINT_ATTBONUS_FIRE, // 50 + POINT_ATTBONUS_ICE, // 51 + POINT_ATTBONUS_DESERT, // 52 + POINT_ATTBONUS_MONSTER, // 53 + POINT_ATTBONUS_WARRIOR, // 54 + POINT_ATTBONUS_ASSASSIN, // 55 + POINT_ATTBONUS_SURA, // 56 + POINT_ATTBONUS_SHAMAN, // 57 + POINT_ATTBONUS_TREE, // 58 + + POINT_RESIST_WARRIOR, // 59 + POINT_RESIST_ASSASSIN, // 60 + POINT_RESIST_SURA, // 61 + POINT_RESIST_SHAMAN, // 62 + + POINT_STEAL_HP, // 63 + POINT_STEAL_SP, // 64 + + POINT_MANA_BURN_PCT, // 65 + POINT_DAMAGE_SP_RECOVER, // 66 + + POINT_BLOCK, // 67 + POINT_DODGE, // 68 + + POINT_RESIST_SWORD, // 69 + POINT_RESIST_TWOHAND, // 70 + POINT_RESIST_DAGGER, // 71 + POINT_RESIST_BELL, // 72 + POINT_RESIST_FAN, // 73 + POINT_RESIST_BOW, // 74 + POINT_RESIST_FIRE, // 75 + POINT_RESIST_ELEC, // 76 + POINT_RESIST_MAGIC, // 77 + POINT_RESIST_WIND, // 78 + + POINT_REFLECT_MELEE, // 79 + + POINT_REFLECT_CURSE, // 80 + POINT_POISON_REDUCE, // 81 + + POINT_KILL_SP_RECOVER, // 82 + POINT_EXP_DOUBLE_BONUS, // 83 + POINT_GOLD_DOUBLE_BONUS, // 84 + POINT_ITEM_DROP_BONUS, // 85 + + POINT_POTION_BONUS, // 86 + POINT_KILL_HP_RECOVERY, // 87 + + POINT_IMMUNE_STUN, // 88 + POINT_IMMUNE_SLOW, // 89 + POINT_IMMUNE_FALL, // 90 + ////////////////// + + POINT_PARTY_ATTACKER_BONUS, // 91 + POINT_PARTY_TANKER_BONUS, // 92 + + POINT_ATT_BONUS, // 93 + POINT_DEF_BONUS, // 94 + + POINT_ATT_GRADE_BONUS, // 95 + POINT_DEF_GRADE_BONUS, // 96 + POINT_MAGIC_ATT_GRADE_BONUS, // 97 + POINT_MAGIC_DEF_GRADE_BONUS, // 98 + + POINT_RESIST_NORMAL_DAMAGE, // 99 + + POINT_HIT_HP_RECOVERY, // 100 + POINT_HIT_SP_RECOVERY, // 101 + POINT_MANASHIELD, // 102 + + POINT_PARTY_BUFFER_BONUS, // 103 + POINT_PARTY_SKILL_MASTER_BONUS, // 104 + + POINT_HP_RECOVER_CONTINUE, // 105 + POINT_SP_RECOVER_CONTINUE, // 106 + + POINT_STEAL_GOLD, // 107 + POINT_POLYMORPH, // 108 + POINT_MOUNT, // 109 + + POINT_PARTY_HASTE_BONUS, // 110 + POINT_PARTY_DEFENDER_BONUS, // 111 + POINT_STAT_RESET_COUNT, // 112 + + POINT_HORSE_SKILL, // 113 + + POINT_MALL_ATTBONUS, // 114 + POINT_MALL_DEFBONUS, // 115 + POINT_MALL_EXPBONUS, // 116 + POINT_MALL_ITEMBONUS, // 117 + POINT_MALL_GOLDBONUS, // 118 + + POINT_MAX_HP_PCT, // 119 + POINT_MAX_SP_PCT, // 120 + + POINT_SKILL_DAMAGE_BONUS, // 121 + POINT_NORMAL_HIT_DAMAGE_BONUS, // 122 + + // DEFEND_BONUS_ATTRIBUTES + POINT_SKILL_DEFEND_BONUS, // 123 + POINT_NORMAL_HIT_DEFEND_BONUS, // 124 + // END_OF_DEFEND_BONUS_ATTRIBUTES + + POINT_PC_BANG_EXP_BONUS, // 125 + POINT_PC_BANG_DROP_BONUS, // 126 + + POINT_RAMADAN_CANDY_BONUS_EXP, // 127 + + POINT_ENERGY = 128, + + POINT_ENERGY_END_TIME = 129, + + POINT_COSTUME_ATTR_BONUS = 130, + POINT_MAGIC_ATT_BONUS_PER = 131, + POINT_MELEE_MAGIC_ATT_BONUS_PER = 132, + + POINT_RESIST_ICE = 133, + POINT_RESIST_EARTH = 134, + POINT_RESIST_DARK = 135, + + POINT_RESIST_CRITICAL = 136, + POINT_RESIST_PENETRATE = 137, + +#ifdef ENABLE_WOLFMAN_CHARACTER + POINT_BLEEDING_REDUCE = 138, + POINT_BLEEDING_PCT = 139, + + POINT_ATTBONUS_WOLFMAN = 140, + POINT_RESIST_WOLFMAN = 141, + POINT_RESIST_CLAW = 142, +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + POINT_ACCEDRAIN_RATE = 143, +#endif +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + POINT_RESIST_MAGIC_REDUCTION = 144, +#endif + +#ifdef ENABLE_CHEQUE_SYSTEM + POINT_CHEQUE = 145, +#endif + + POINT_RESIST_MOUNT_FALL = 146, //unk + POINT_RESIST_HUMAN = 147, + + POINT_ENCHANT_ELECT = 148, + POINT_ENCHANT_FIRE = 149, + POINT_ENCHANT_ICE = 150, + POINT_ENCHANT_WIND = 151, + POINT_ENCHANT_EARTH = 152, + POINT_ENCHANT_DARK = 153, + + POINT_ATTBONUS_CZ = 154, + POINT_ATTBONUS_SWORD = 155, //unk + POINT_ATTBONUS_TWOHAND = 156, //unk + POINT_ATTBONUS_DAGGER = 157, //unk + POINT_ATTBONUS_BELL = 158, //unk + POINT_ATTBONUS_FAN = 159, //unk + POINT_ATTBONUS_BOW = 160, //unk +#ifdef ENABLE_WOLFMAN_CHARACTER + POINT_ATTBONUS_CLAW = 161, //unk +#endif + + POINT_MAX, +}; + +enum EPKModes +{ + PK_MODE_PEACE, + PK_MODE_REVENGE, + PK_MODE_FREE, + PK_MODE_PROTECT, + PK_MODE_GUILD, + PK_MODE_MAX_NUM +}; + +enum EPositions +{ + POS_DEAD, + POS_SLEEPING, + POS_RESTING, + POS_SITTING, + POS_FISHING, + POS_FIGHTING, + POS_MOUNTING, + POS_STANDING +}; + +enum EBlockAction +{ + BLOCK_EXCHANGE = (1 << 0), + BLOCK_PARTY_INVITE = (1 << 1), + BLOCK_GUILD_INVITE = (1 << 2), + BLOCK_WHISPER = (1 << 3), + BLOCK_MESSENGER_INVITE = (1 << 4), + BLOCK_PARTY_REQUEST = (1 << 5), +}; + +// Dynamically evaluated CHARACTER* equivalent. +// Referring to SCharDeadEventInfo. +struct DynamicCharacterPtr { + DynamicCharacterPtr() : is_pc(false), id(0) {} + DynamicCharacterPtr(const DynamicCharacterPtr& o) + : is_pc(o.is_pc), id(o.id) {} + + // Returns the LPCHARACTER found in CHARACTER_MANAGER. + LPCHARACTER Get() const; + // Clears the current settings. + void Reset() { + is_pc = false; + id = 0; + } + + // Basic assignment operator. + DynamicCharacterPtr& operator=(const DynamicCharacterPtr& rhs) { + is_pc = rhs.is_pc; + id = rhs.id; + return *this; + } + // Supports assignment with LPCHARACTER type. + DynamicCharacterPtr& operator=(LPCHARACTER character); + // Supports type casting to LPCHARACTER. + operator LPCHARACTER() const { + return Get(); + } + + bool is_pc; + uint32_t id; +}; + +typedef struct character_point +{ + long points[POINT_MAX_NUM]; + + BYTE job; + BYTE voice; + + BYTE level; + DWORD exp; + long gold; +#ifdef ENABLE_CHEQUE_SYSTEM + int cheque; +#endif + int hp; + int sp; + + int iRandomHP; + int iRandomSP; + + int stamina; + + BYTE skill_group; +} CHARACTER_POINT; + +typedef struct character_point_instant +{ + long points[POINT_MAX_NUM]; + + float fRot; + + int iMaxHP; + int iMaxSP; + + long position; + + long instant_flag; + DWORD dwAIFlag; + DWORD dwImmuneFlag; + DWORD dwLastShoutPulse; + + DWORD parts[PART_MAX_NUM]; // @fixme502 + + LPCHARACTER pCubeNpc; + LPCHARACTER battle_victim; + + BYTE gm_level; + + BYTE bBasePart; + + int iMaxStamina; + + BYTE bBlockMode; + + int iDragonSoulActiveDeck; + LPENTITY m_pDragonSoulRefineWindowOpener; +} CHARACTER_POINT_INSTANT; + +// @fixme199 BEGIN +struct PlayerSlotT { + std::array pItems; + std::array bItemGrid; + std::array pDSItems; + std::array wDSItemGrid; + std::array pCubeItems; +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + std::array pAcceMaterials; +#endif + std::array pQuickslot; +}; +// @fixme199 END + +#define TRIGGERPARAM LPCHARACTER ch, LPCHARACTER causer + +typedef struct trigger +{ + BYTE type; + int (*func) (TRIGGERPARAM); + long value; +} TRIGGER; + +class CTrigger +{ + public: + CTrigger() : bType(0), pFunc(NULL) + { + } + + BYTE bType; + int (*pFunc) (TRIGGERPARAM); +}; + +EVENTINFO(char_event_info) +{ + DynamicCharacterPtr ch; +}; + +typedef std::map target_map; +struct TSkillUseInfo +{ + int iHitCount; + int iMaxHitCount; + int iSplashCount; + DWORD dwNextSkillUsableTime; + int iRange; + bool bUsed; + DWORD dwVID; + bool isGrandMaster; + #ifdef ENABLE_SKILL_COOLDOWN_CHECK + bool cooldown{false}; + int skillCount{0}; + #endif + + target_map TargetVIDMap; + + TSkillUseInfo() + : iHitCount(0), iMaxHitCount(0), iSplashCount(0), dwNextSkillUsableTime(0), iRange(0), bUsed(false), + dwVID(0), isGrandMaster(false) + {} + + bool HitOnce(DWORD dwVnum = 0); + + bool UseSkill(bool isGrandMaster, DWORD vid, DWORD dwCooltime, int splashcount = 1, int hitcount = -1, int range = -1); + DWORD GetMainTargetVID() const { return dwVID; } + void SetMainTargetVID(DWORD vid) { dwVID=vid; } + void ResetHitCount() { if (iSplashCount) { iHitCount = iMaxHitCount; iSplashCount--; } } + #ifdef ENABLE_SKILL_COOLDOWN_CHECK + bool IsOnCooldown(int vnum); + #endif +}; + +typedef struct packet_party_update TPacketGCPartyUpdate; +class CExchange; +class CSkillProto; +class CParty; +class CDungeon; +class CWarMap; +class CAffect; +class CGuild; +class CSafebox; +class CArena; + +class CShop; +typedef class CShop * LPSHOP; + +class CMob; +class CMobInstance; +typedef struct SMobSkillInfo TMobSkillInfo; + +//SKILL_POWER_BY_LEVEL +extern int GetSkillPowerByLevelFromType(int job, int skillgroup, int skilllevel); +//END_SKILL_POWER_BY_LEVEL + +namespace marriage +{ + class WeddingMap; +} +enum e_overtime +{ + OT_NONE, + OT_3HOUR, + OT_5HOUR, +}; + +#define NEW_ICEDAMAGE_SYSTEM +class CHARACTER : public CEntity, public CFSM, public CHorseRider +{ + protected: + ////////////////////////////////////////////////////////////////////////////////// + virtual void EncodeInsertPacket(LPENTITY entity); + virtual void EncodeRemovePacket(LPENTITY entity); + ////////////////////////////////////////////////////////////////////////////////// + + public: + LPCHARACTER FindCharacterInView(const char * name, bool bFindPCOnly); + void UpdatePacket(); + + ////////////////////////////////////////////////////////////////////////////////// + protected: + CStateTemplate m_stateMove; + CStateTemplate m_stateBattle; + CStateTemplate m_stateIdle; + + public: + virtual void StateMove(); + virtual void StateBattle(); + virtual void StateIdle(); + virtual void StateFlag(); + virtual void StateFlagBase(); + void StateHorse(); + + protected: + // STATE_IDLE_REFACTORING + void __StateIdle_Monster(); + void __StateIdle_Stone(); + void __StateIdle_NPC(); + // END_OF_STATE_IDLE_REFACTORING + + public: + DWORD GetAIFlag() const { return m_pointsInstant.dwAIFlag; } + + void SetAggressive(); + bool IsAggressive() const; + + void SetCoward(); + bool IsCoward() const; + void CowardEscape(); + + void SetNoAttackShinsu(); + bool IsNoAttackShinsu() const; + + void SetNoAttackChunjo(); + bool IsNoAttackChunjo() const; + + void SetNoAttackJinno(); + bool IsNoAttackJinno() const; + + void SetAttackMob(); + bool IsAttackMob() const; + + virtual void BeginStateEmpty(); + virtual void EndStateEmpty() {} + + void RestartAtSamePos(); + + protected: + DWORD m_dwStateDuration; + ////////////////////////////////////////////////////////////////////////////////// + + public: + CHARACTER(); + virtual ~CHARACTER(); + + void Create(const char * c_pszName, DWORD vid, bool isPC); + void Destroy(); + + void Disconnect(const char * c_pszReason); + + protected: + void Initialize(); + + ////////////////////////////////////////////////////////////////////////////////// + // Basic Points + public: + DWORD GetPlayerID() const { return m_dwPlayerID; } + + void SetPlayerProto(const TPlayerTable * table); + void CreatePlayerProto(TPlayerTable & tab); + + void SetProto(const CMob * c_pkMob); + DWORD GetRaceNum() const; // @fixme501 + + void Save(); // DelayedSave + void SaveReal(); + void FlushDelayedSaveItem(); + + const char * GetName() const; + const VID & GetVID() const { return m_vid; } + + void SetName(const std::string& name) { m_stName = name; } + + void SetRace(BYTE race); + bool ChangeSex(); + + DWORD GetAID() const; + int GetChangeEmpireCount() const; + void SetChangeEmpireCount(); + int ChangeEmpire(BYTE empire); + + BYTE GetJob() const; + BYTE GetCharType() const; + + bool IsPC() const { return GetDesc() ? true : false; } + bool IsNPC() const { return m_bCharType != CHAR_TYPE_PC; } + bool IsMonster() const { return m_bCharType == CHAR_TYPE_MONSTER; } + bool IsStone() const { return m_bCharType == CHAR_TYPE_STONE; } + bool IsDoor() const { return m_bCharType == CHAR_TYPE_DOOR; } + bool IsBuilding() const { return m_bCharType == CHAR_TYPE_BUILDING; } + bool IsWarp() const { return m_bCharType == CHAR_TYPE_WARP; } + bool IsGoto() const { return m_bCharType == CHAR_TYPE_GOTO; } +// bool IsPet() const { return m_bCharType == CHAR_TYPE_PET; } + + DWORD GetLastShoutPulse() const { return m_pointsInstant.dwLastShoutPulse; } + void SetLastShoutPulse(DWORD pulse) { m_pointsInstant.dwLastShoutPulse = pulse; } + int GetLevel() const { return m_points.level; } + void SetLevel(BYTE level); + + BYTE GetGMLevel() const; + BOOL IsGM() const; + void SetGMLevel(); + + DWORD GetExp() const { return m_points.exp; } + void SetExp(DWORD exp) { m_points.exp = exp; } + DWORD GetNextExp() const; + LPCHARACTER DistributeExp(); + void DistributeHP(LPCHARACTER pkKiller); + void DistributeSP(LPCHARACTER pkKiller, int iMethod=0); + +#ifdef ENABLE_KILL_EVENT_FIX + LPCHARACTER GetMostAttacked(); +#endif + + void SetPosition(int pos); + bool IsPosition(int pos) const { return m_pointsInstant.position == pos ? true : false; } + int GetPosition() const { return m_pointsInstant.position; } + + void SetPart(BYTE bPartPos, DWORD wVal); // @fixme502 + DWORD GetPart(BYTE bPartPos) const; // @fixme502 + DWORD GetOriginalPart(BYTE bPartPos) const; // @fixme502 + + void SetHP(int hp) { m_points.hp = hp; } + int GetHP() const { return m_points.hp; } + + void SetSP(int sp) { m_points.sp = sp; } + int GetSP() const { return m_points.sp; } + + void SetStamina(int stamina) { m_points.stamina = stamina; } + int GetStamina() const { return m_points.stamina; } + + void SetMaxHP(int iVal) { m_pointsInstant.iMaxHP = iVal; } + int GetMaxHP() const { return m_pointsInstant.iMaxHP; } + + void SetMaxSP(int iVal) { m_pointsInstant.iMaxSP = iVal; } + int GetMaxSP() const { return m_pointsInstant.iMaxSP; } + + void SetMaxStamina(int iVal) { m_pointsInstant.iMaxStamina = iVal; } + int GetMaxStamina() const { return m_pointsInstant.iMaxStamina; } + + void SetRandomHP(int v) { m_points.iRandomHP = v; } + void SetRandomSP(int v) { m_points.iRandomSP = v; } + + int GetRandomHP() const { return m_points.iRandomHP; } + int GetRandomSP() const { return m_points.iRandomSP; } + + int GetHPPct() const; + + void SetRealPoint(BYTE idx, int val); + int GetRealPoint(BYTE idx) const; + + void SetPoint(BYTE idx, int val); + int GetPoint(BYTE idx) const; + int GetLimitPoint(BYTE idx) const; + int GetPolymorphPoint(BYTE idx) const; + + const TMobTable & GetMobTable() const; + BYTE GetMobRank() const; + BYTE GetMobBattleType() const; + BYTE GetMobSize() const; + DWORD GetMobDamageMin() const; + DWORD GetMobDamageMax() const; + WORD GetMobAttackRange() const; + DWORD GetMobDropItemVnum() const; + float GetMobDamageMultiply() const; + + // NEWAI + bool IsBerserker() const; + bool IsBerserk() const; + void SetBerserk(bool mode); + + bool IsStoneSkinner() const; + + bool IsGodSpeeder() const; + bool IsGodSpeed() const; + void SetGodSpeed(bool mode); + + bool IsDeathBlower() const; + bool IsDeathBlow() const; + + bool IsReviver() const; + bool HasReviverInParty() const; + bool IsRevive() const; + void SetRevive(bool mode); + // NEWAI END + + bool IsRaceFlag(DWORD dwBit) const; + bool IsSummonMonster() const; + DWORD GetSummonVnum() const; + + DWORD GetPolymorphItemVnum() const; + DWORD GetMonsterDrainSPPoint() const; + + void MainCharacterPacket(); + + void ComputePoints(); + void ComputeBattlePoints(); + void PointChange(BYTE type, int amount, bool bAmount = false, bool bBroadcast = false); + void PointsPacket(); + void ApplyPoint(BYTE bApplyType, int iVal); + void CheckMaximumPoints(); + + bool Show(long lMapIndex, long x, long y, long z = LONG_MAX, bool bShowSpawnMotion = false); + + void Sitdown(int is_ground); + void Standup(); + + void SetRotation(float fRot); + void SetRotationToXY(long x, long y); + float GetRotation() const { return m_pointsInstant.fRot; } + + void MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet); + void Motion(BYTE motion, LPCHARACTER victim = NULL); + + void ChatPacket(BYTE type, const char *format, ...); + void MonsterChat(BYTE bMonsterChatType); + + void ResetPoint(int iLv); + + void SetBlockMode(BYTE bFlag); + void SetBlockModeForce(BYTE bFlag); + bool IsBlockMode(BYTE bFlag) const { return (m_pointsInstant.bBlockMode & bFlag)?true:false; } + + bool IsPolymorphed() const { return m_dwPolymorphRace>0; } + bool IsPolyMaintainStat() const { return m_bPolyMaintainStat; } + void SetPolymorph(DWORD dwRaceNum, bool bMaintainStat = false); + DWORD GetPolymorphVnum() const { return m_dwPolymorphRace; } + int GetPolymorphPower() const; + + // FISING + void fishing(); + void fishing_take(); + // END_OF_FISHING + + // MINING + void mining(LPCHARACTER chLoad); + void mining_cancel(); + void mining_take(); + // END_OF_MINING + + void ResetPlayTime(DWORD dwTimeRemain = 0); + + void CreateFly(BYTE bType, LPCHARACTER pkVictim); + + void ResetChatCounter(); + BYTE IncreaseChatCounter(); + BYTE GetChatCounter() const; + + protected: + DWORD m_dwPolymorphRace; + bool m_bPolyMaintainStat; + DWORD m_dwLoginPlayTime; + DWORD m_dwPlayerID; + VID m_vid; + std::string m_stName; + BYTE m_bCharType; + + CHARACTER_POINT m_points; + CHARACTER_POINT_INSTANT m_pointsInstant; + std::unique_ptr m_PlayerSlots; // @fixme199 + + int m_iMoveCount; + DWORD m_dwPlayStartTime; + BYTE m_bAddChrState; + bool m_bSkipSave; + BYTE m_bChatCounter; + + // End of Basic Points + + ////////////////////////////////////////////////////////////////////////////////// + // Move & Synchronize Positions + ////////////////////////////////////////////////////////////////////////////////// + public: + bool IsStateMove() const { return IsState((CState&)m_stateMove); } + bool IsStateIdle() const { return IsState((CState&)m_stateIdle); } + bool IsWalking() const { return m_bNowWalking || GetStamina()<=0; } + void SetWalking(bool bWalkFlag) { m_bWalking=bWalkFlag; } + void SetNowWalking(bool bWalkFlag); + void ResetWalking() { SetNowWalking(m_bWalking); } + + bool Goto(long x, long y); + void Stop(); + + bool CanMove() const; + + void SyncPacket(); + bool Sync(long x, long y); + bool Move(long x, long y); + void OnMove(bool bIsAttack = false); + DWORD GetMotionMode() const; + float GetMoveMotionSpeed() const; + float GetMoveSpeed() const; + void CalculateMoveDuration(); + void SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime=0, int iRot=-1); + DWORD GetCurrentMoveDuration() const { return m_dwMoveDuration; } + DWORD GetWalkStartTime() const { return m_dwWalkStartTime; } + DWORD GetLastMoveTime() const { return m_dwLastMoveTime; } + DWORD GetLastAttackTime() const { return m_dwLastAttackTime; } + + void SetLastAttacked(DWORD time); + + bool SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList = true); + bool IsSyncOwner(LPCHARACTER ch) const; + + bool WarpSet(long x, long y, long lRealMapIndex = 0); + void SetWarpLocation(long lMapIndex, long x, long y); + void WarpEnd(); + const PIXEL_POSITION & GetWarpPosition() const { return m_posWarp; } + bool WarpToPID(DWORD dwPID); + + void SaveExitLocation(); + void ExitToSavedLocation(); + + void StartStaminaConsume(); + void StopStaminaConsume(); + bool IsStaminaConsume() const; + bool IsStaminaHalfConsume() const; + + void ResetStopTime(); + DWORD GetStopTime() const; + + protected: + void ClearSync(); + + float m_fSyncTime; + LPCHARACTER m_pkChrSyncOwner; + CHARACTER_LIST m_kLst_pkChrSyncOwned; + + PIXEL_POSITION m_posDest; + PIXEL_POSITION m_posStart; + PIXEL_POSITION m_posWarp; + long m_lWarpMapIndex; + + PIXEL_POSITION m_posExit; + long m_lExitMapIndex; + + DWORD m_dwMoveStartTime; + DWORD m_dwMoveDuration; + + DWORD m_dwLastMoveTime; + DWORD m_dwLastAttackTime; + DWORD m_dwWalkStartTime; + DWORD m_dwStopTime; + + bool m_bWalking; + bool m_bNowWalking; + bool m_bStaminaConsume; + // End + + public: + void SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos); + bool GetQuickslot(BYTE pos, TQuickslot ** ppSlot); + bool SetQuickslot(BYTE pos, TQuickslot & rSlot); + bool DelQuickslot(BYTE pos); + bool SwapQuickslot(BYTE a, BYTE b); + void ChainQuickslotItem(LPITEM pItem, BYTE bType, BYTE bOldPos); + + //////////////////////////////////////////////////////////////////////////////////////// + // Affect + public: + void StartAffectEvent(); + void ClearAffect(bool bSave=false); + void ComputeAffect(CAffect * pkAff, bool bAdd); + bool AddAffect(DWORD dwType, BYTE bApplyOn, long lApplyValue, DWORD dwFlag, long lDuration, long lSPCost, bool bOverride, bool IsCube = false); + void RefreshAffect(); + bool RemoveAffect(DWORD dwType); + bool IsAffectFlag(DWORD dwAff) const; + + bool UpdateAffect(); // called from EVENT + int ProcessAffect(); + + void LoadAffect(DWORD dwCount, TPacketAffectElement * pElements); + void SaveAffect(); + + bool IsLoadedAffect() const { return m_bIsLoadedAffect; } + + bool IsGoodAffect(BYTE bAffectType) const; + + void RemoveSkillAffect(); + void RemoveGoodAffect(); + void RemoveBadAffect(); + + CAffect * FindAffect(DWORD dwType, BYTE bApply=APPLY_NONE) const; + const std::list & GetAffectContainer() const { return m_list_pkAffect; } + bool RemoveAffect(CAffect * pkAff); + + protected: + bool m_bIsLoadedAffect; + TAffectFlag m_afAffectFlag; + std::list m_list_pkAffect; + + public: + // PARTY_JOIN_BUG_FIX + void SetParty(LPPARTY pkParty); + LPPARTY GetParty() const { return m_pkParty; } + + bool RequestToParty(LPCHARACTER leader); + void DenyToParty(LPCHARACTER member); + void AcceptToParty(LPCHARACTER member); + + void PartyInvite(LPCHARACTER pchInvitee); + + void PartyInviteAccept(LPCHARACTER pchInvitee); + + void PartyInviteDeny(DWORD dwPID); + + bool BuildUpdatePartyPacket(TPacketGCPartyUpdate & out); + int GetLeadershipSkillLevel() const; + + bool CanSummon(int iLeaderShip); + + void SetPartyRequestEvent(LPEVENT pkEvent) { m_pkPartyRequestEvent = pkEvent; } + + protected: + + void PartyJoin(LPCHARACTER pkLeader); + + enum PartyJoinErrCode { + PERR_NONE = 0, + PERR_SERVER, + PERR_DUNGEON, + PERR_OBSERVER, + PERR_LVBOUNDARY, + PERR_LOWLEVEL, + PERR_HILEVEL, + PERR_ALREADYJOIN, + PERR_PARTYISFULL, + PERR_SEPARATOR, ///< Error type separator. + PERR_DIFFEMPIRE, + PERR_MAX + }; + + static PartyJoinErrCode IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest); + + static PartyJoinErrCode IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest); + + LPPARTY m_pkParty; + DWORD m_dwLastDeadTime; + LPEVENT m_pkPartyRequestEvent; + + typedef std::map< DWORD, LPEVENT > EventMap; + EventMap m_PartyInviteEventMap; + + // END_OF_PARTY_JOIN_BUG_FIX + + //////////////////////////////////////////////////////////////////////////////////////// + // Dungeon + public: + void SetDungeon(LPDUNGEON pkDungeon); + LPDUNGEON GetDungeon() const { return m_pkDungeon; } + LPDUNGEON GetDungeonForce() const; + protected: + LPDUNGEON m_pkDungeon; + int m_iEventAttr; + + //////////////////////////////////////////////////////////////////////////////////////// + // Guild + public: + void SetGuild(CGuild * pGuild); + CGuild* GetGuild() const { return m_pGuild; } + + void SetWarMap(CWarMap* pWarMap); + CWarMap* GetWarMap() const { return m_pWarMap; } + + protected: + CGuild * m_pGuild; + DWORD m_dwUnderGuildWarInfoMessageTime; + CWarMap * m_pWarMap; + + //////////////////////////////////////////////////////////////////////////////////////// + // Item related + public: + bool CanHandleItem(bool bSkipRefineCheck = false, bool bSkipObserver = false); + + bool IsItemLoaded() const { return m_bItemLoaded; } + void SetItemLoaded() { m_bItemLoaded = true; } + + void ClearItem(); +#ifdef ENABLE_HIGHLIGHT_NEW_ITEM + void SetItem(TItemPos Cell, LPITEM item, bool bWereMine = false); +#else + void SetItem(TItemPos Cell, LPITEM item); +#endif + LPITEM GetItem(TItemPos Cell) const; + LPITEM GetInventoryItem(WORD wCell) const; + bool IsEmptyItemGrid(TItemPos Cell, BYTE size, int iExceptionCell = -1) const; + + void SetWear(BYTE bCell, LPITEM item); + LPITEM GetWear(BYTE bCell) const; + + // MYSHOP_PRICE_LIST + void UseSilkBotary(void); + + void UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p); + // END_OF_MYSHOP_PRICE_LIST + + bool UseItemEx(LPITEM item, TItemPos DestCell); + bool UseItem(TItemPos Cell, TItemPos DestCell = NPOS); + + // ADD_REFINE_BUILDING + bool IsRefineThroughGuild() const; + CGuild * GetRefineGuild() const; + int ComputeRefineFee(int iCost, int iMultiply = 5) const; + void PayRefineFee(int iTotalMoney); + void SetRefineNPC(LPCHARACTER ch); + // END_OF_ADD_REFINE_BUILDING + + bool RefineItem(LPITEM pkItem, LPITEM pkTarget); + bool DropItem(TItemPos Cell, BYTE bCount=0); + bool GiveRecallItem(LPITEM item); + void ProcessRecallItem(LPITEM item); + + // void PotionPacket(int iPotionType); + void EffectPacket(int enumEffectType); + void SpecificEffectPacket(const char filename[128]); + + // ADD_MONSTER_REFINE + bool DoRefine(LPITEM item, bool bMoneyOnly = false); + // END_OF_ADD_MONSTER_REFINE + + bool DoRefineWithScroll(LPITEM item); + bool RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell = -1); + + void SetRefineMode(int iAdditionalCell = -1); + void ClearRefineMode(); + + bool GiveItem(LPCHARACTER victim, TItemPos Cell); + bool CanReceiveItem(LPCHARACTER from, LPITEM item) const; + void ReceiveItem(LPCHARACTER from, LPITEM item); + bool GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector &dwItemVnums, + std::vector &dwItemCounts, std::vector &item_gets, int &count); + + bool MoveItem(TItemPos pos, TItemPos change_pos, BYTE num); + bool PickupItem(DWORD vid); + bool EquipItem(LPITEM item, int iCandidateCell = -1); + bool UnequipItem(LPITEM item); + + bool CanEquipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS); + + bool CanUnequipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS); + + bool SwapItem(BYTE bCell, BYTE bDestCell); + LPITEM AutoGiveItem(DWORD dwItemVnum, BYTE bCount=1, int iRarePct = -1, bool bMsg = true); + void AutoGiveItem(LPITEM item, bool longOwnerShip = false); + + std::pair AutoStackItemProto(DWORD dwItemVnum, BYTE & bCount); + LPITEM AutoStackItem(LPITEM item); + + int GetEmptyInventoryEx(LPITEM item); + + int GetEmptyInventory(BYTE size) const; + int GetEmptyDragonSoulInventory(LPITEM pItem) const; + void CopyDragonSoulItemGrid(std::vector& vDragonSoulItemGrid) const; + + int CountEmptyInventory() const; + + int CountSpecifyItem(DWORD vnum) const; + void RemoveSpecifyItem(DWORD vnum, DWORD count = 1); + LPITEM FindSpecifyItem(DWORD vnum) const; + LPITEM FindItemByID(DWORD id) const; + + int CountSpecifyTypeItem(BYTE type) const; + void RemoveSpecifyTypeItem(BYTE type, DWORD count = 1); + + bool IsEquipUniqueItem(DWORD dwItemVnum) const; + + // CHECK_UNIQUE_GROUP + bool IsEquipUniqueGroup(DWORD dwGroupVnum) const; + // END_OF_CHECK_UNIQUE_GROUP + + void SendEquipment(LPCHARACTER ch); + // End of Item + + protected: + + void SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice); + + bool m_bNoOpenedShop; + + bool m_bItemLoaded; + int m_iRefineAdditionalCell; + bool m_bUnderRefine; + DWORD m_dwRefineNPCVID; + + public: + //////////////////////////////////////////////////////////////////////////////////////// + // Money related + INT GetGold() const { return m_points.gold; } + void SetGold(INT gold) { m_points.gold = gold; } + bool DropGold(INT gold); + INT GetAllowedGold() const; + void GiveGold(INT iAmount); + // End of Money +#ifdef ENABLE_CHEQUE_SYSTEM + INT GetCheque() const { return m_points.cheque; } + void SetCheque(int cheque) { m_points.cheque = cheque; } + void GiveCheque(INT iAmount); + bool DropCheque(INT gold); +#endif + + //////////////////////////////////////////////////////////////////////////////////////// + // Shop related + public: + void SetShop(LPSHOP pkShop); + LPSHOP GetShop() const { return m_pkShop; } + void ShopPacket(BYTE bSubHeader); + + void SetShopOwner(LPCHARACTER ch) { m_pkChrShopOwner = ch; } + LPCHARACTER GetShopOwner() const { return m_pkChrShopOwner;} + + void OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount); + LPSHOP GetMyShop() const { return m_pkMyShop; } + void CloseMyShop(); + + protected: + + LPSHOP m_pkShop; + LPSHOP m_pkMyShop; + std::string m_stShopSign; + LPCHARACTER m_pkChrShopOwner; + // End of shop + + //////////////////////////////////////////////////////////////////////////////////////// + // Exchange related + public: + bool ExchangeStart(LPCHARACTER victim); + void SetExchange(CExchange * pkExchange); + CExchange * GetExchange() const { return m_pkExchange; } + + protected: + CExchange * m_pkExchange; + // End of Exchange + + //////////////////////////////////////////////////////////////////////////////////////// + // Battle + public: + struct TBattleInfo + { + int iTotalDamage; + int iAggro; + + TBattleInfo(int iTot, int iAggr) + : iTotalDamage(iTot), iAggro(iAggr) + {} + }; + typedef std::map TDamageMap; + + typedef struct SAttackLog + { + DWORD dwVID; + DWORD dwTime; + } AttackLog; + + bool Damage(LPCHARACTER pAttacker, int dam, EDamageType type = DAMAGE_TYPE_NORMAL); + bool __Profile__Damage(LPCHARACTER pAttacker, int dam, EDamageType type = DAMAGE_TYPE_NORMAL); + void DeathPenalty(BYTE bExpLossPercent); + void ReviveInvisible(int iDur); + + bool Attack(LPCHARACTER pkVictim, BYTE bType = 0); + bool IsAlive() const { return m_pointsInstant.position == POS_DEAD ? false : true; } + bool CanFight() const; + + bool CanBeginFight() const; + void BeginFight(LPCHARACTER pkVictim); + + bool CounterAttack(LPCHARACTER pkChr); + + bool IsStun() const; + void Stun(); + bool IsDead() const; + void Dead(LPCHARACTER pkKiller = NULL, bool bImmediateDead=true); // @fixme188 instant death false to true + + void Reward(bool bItemDrop); + void RewardGold(LPCHARACTER pkAttacker); + + bool Shoot(BYTE bType); + void FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader); + + void ForgetMyAttacker(); + void AggregateMonster(); + void AttractRanger(); + void PullMonster(); + + int GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount = 1); + void UseArrow(LPITEM pkArrow, DWORD dwArrowCount); + + void AttackedByPoison(LPCHARACTER pkAttacker); + void RemovePoison(); +#ifdef ENABLE_WOLFMAN_CHARACTER + void AttackedByBleeding(LPCHARACTER pkAttacker); + void RemoveBleeding(); +#endif + void AttackedByFire(LPCHARACTER pkAttacker, int amount, int count); + void RemoveFire(); + + void UpdateAlignment(int iAmount); + int GetAlignment() const; + + int GetRealAlignment() const; + void ShowAlignment(bool bShow); + + void SetKillerMode(bool bOn); + bool IsKillerMode() const; + void UpdateKillerMode(); + + BYTE GetPKMode() const; + void SetPKMode(BYTE bPKMode); + + void ItemDropPenalty(LPCHARACTER pkKiller); + + void UpdateAggrPoint(LPCHARACTER ch, EDamageType type, int dam); + + // HACK + + public: + void SetComboSequence(BYTE seq); + BYTE GetComboSequence() const; + + void SetLastComboTime(DWORD time); + DWORD GetLastComboTime() const; + + int GetValidComboInterval() const; + void SetValidComboInterval(int interval); + + BYTE GetComboIndex() const; + + void IncreaseComboHackCount(int k = 1); + void ResetComboHackCount(); + void SkipComboAttackByTime(int interval); + DWORD GetSkipComboAttackByTime() const; + + protected: + BYTE m_bComboSequence; + DWORD m_dwLastComboTime; + int m_iValidComboInterval; + BYTE m_bComboIndex; + int m_iComboHackCount; + DWORD m_dwSkipComboAttackByTime; + + protected: + void UpdateAggrPointEx(LPCHARACTER ch, EDamageType type, int dam, TBattleInfo & info); + void ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim); + + DWORD m_dwFlyTargetID; + std::vector m_vec_dwFlyTargets; + TDamageMap m_map_kDamage; + DWORD m_dwKillerPID; + + int m_iAlignment; // Lawful/Chaotic value -200000 ~ 200000 + int m_iRealAlignment; + int m_iKillerModePulse; + BYTE m_bPKMode; + + // Aggro + DWORD m_dwLastVictimSetTime; + int m_iMaxAggro; + // End of Battle + + // Stone + public: + void SetStone(LPCHARACTER pkChrStone); + void ClearStone(); + void DetermineDropMetinStone(); + DWORD GetDropMetinStoneVnum() const { return m_dwDropMetinStone; } + BYTE GetDropMetinStonePct() const { return m_bDropMetinStonePct; } + + protected: + LPCHARACTER m_pkChrStone; + CHARACTER_SET m_set_pkChrSpawnedBy; + DWORD m_dwDropMetinStone; + BYTE m_bDropMetinStonePct; + // End of Stone + + public: + enum + { + SKILL_UP_BY_POINT, + SKILL_UP_BY_BOOK, + SKILL_UP_BY_TRAIN, + + // ADD_GRANDMASTER_SKILL + SKILL_UP_BY_QUEST, + // END_OF_ADD_GRANDMASTER_SKILL + }; + + void SkillLevelPacket(); + void SkillLevelUp(DWORD dwVnum, BYTE bMethod = SKILL_UP_BY_POINT); + bool SkillLevelDown(DWORD dwVnum); + // ADD_GRANDMASTER_SKILL + bool UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster = true); + void ResetSkill(); + void SetSkillLevel(DWORD dwVnum, BYTE bLev); + int GetUsedSkillMasterType(DWORD dwVnum); + + bool IsLearnableSkill(DWORD dwSkillVnum) const; + // END_OF_ADD_GRANDMASTER_SKILL + + bool CheckSkillHitCount(const BYTE SkillID, const VID dwTargetVID); + bool CanUseSkill(DWORD dwSkillVnum) const; + bool IsUsableSkillMotion(DWORD dwMotionIndex) const; + int GetSkillLevel(DWORD dwVnum) const; + int GetSkillMasterType(DWORD dwVnum) const; + int GetSkillPower(DWORD dwVnum, BYTE bLevel = 0) const; + + time_t GetSkillNextReadTime(DWORD dwVnum) const; + void SetSkillNextReadTime(DWORD dwVnum, time_t time); + void SkillLearnWaitMoreTimeMessage(DWORD dwVnum); + + void ComputePassiveSkill(DWORD dwVnum); + int ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel = 0); +#ifdef ENABLE_SKILL_FLAG_PARTY + int ComputeSkillParty(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel = 0); +#endif + int ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTarget, BYTE bSkillLevel = 0); + void ComputeSkillPoints(); + + void SetSkillGroup(BYTE bSkillGroup); + BYTE GetSkillGroup() const { return m_points.skill_group; } + + int ComputeCooltime(int time); + + void GiveRandomSkillBook(); + + void DisableCooltime(); + bool LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb = 0); + bool LearnGrandMasterSkill(DWORD dwSkillVnum); + + #ifdef ENABLE_SKILL_COOLDOWN_CHECK + bool SkillIsOnCooldown(int vnum) { return m_SkillUseInfo[vnum].IsOnCooldown(vnum); } + #endif + private: + bool m_bDisableCooltime; + DWORD m_dwLastSkillTime; + // End of Skill + + // MOB_SKILL + public: + bool HasMobSkill() const; + size_t CountMobSkill() const; + const TMobSkillInfo * GetMobSkill(unsigned int idx) const; + bool CanUseMobSkill(unsigned int idx) const; + bool UseMobSkill(unsigned int idx); + void ResetMobSkillCooltime(); + protected: + DWORD m_adwMobSkillCooltime[MOB_SKILL_MAX_NUM]; + // END_OF_MOB_SKILL + + // for SKILL_MUYEONG + public: + void StartMuyeongEvent(); + void StopMuyeongEvent(); + + private: + LPEVENT m_pkMuyeongEvent; + + // for SKILL_CHAIN lighting + public: + int GetChainLightningIndex() const { return m_iChainLightingIndex; } + void IncChainLightningIndex() { ++m_iChainLightingIndex; } + void AddChainLightningExcept(LPCHARACTER ch) { m_setExceptChainLighting.emplace(ch); } + void ResetChainLightningIndex() { m_iChainLightingIndex = 0; m_setExceptChainLighting.clear(); } + int GetChainLightningMaxCount() const; + const CHARACTER_SET& GetChainLightingExcept() const { return m_setExceptChainLighting; } + + private: + int m_iChainLightingIndex; + CHARACTER_SET m_setExceptChainLighting; + + // for SKILL_EUNHYUNG + public: + void SetAffectedEunhyung(); + void ClearAffectedEunhyung() { m_dwAffectedEunhyungLevel = 0; } + bool GetAffectedEunhyung() const { return m_dwAffectedEunhyungLevel; } + + private: + DWORD m_dwAffectedEunhyungLevel; + + // Skill levels + + protected: + TPlayerSkill* m_pSkillLevels; + boost::unordered_map m_SkillDamageBonus; + std::map m_SkillUseInfo; + + //////////////////////////////////////////////////////////////////////////////////////// + // AI related + public: + void AssignTriggers(const TMobTable * table); + LPCHARACTER GetVictim() const; + void SetVictim(LPCHARACTER pkVictim); + LPCHARACTER GetNearestVictim(LPCHARACTER pkChr); + LPCHARACTER GetProtege() const; + + bool Follow(LPCHARACTER pkChr, float fMinimumDistance = 150.0f); + bool Return(); + bool IsGuardNPC() const; + bool IsChangeAttackPosition(LPCHARACTER target) const; + void ResetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time() - AI_CHANGE_ATTACK_POISITION_TIME_NEAR;} + void SetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time();} + + bool OnIdle(); + + void OnAttack(LPCHARACTER pkChrAttacker); + void OnClick(LPCHARACTER pkChrCauser); + + VID m_kVIDVictim; + + protected: + DWORD m_dwLastChangeAttackPositionTime; + CTrigger m_triggerOnClick; + // End of AI + + //////////////////////////////////////////////////////////////////////////////////////// + // Target + protected: + LPCHARACTER m_pkChrTarget; + CHARACTER_SET m_set_pkChrTargetedBy; + + public: + void SetTarget(LPCHARACTER pkChrTarget); + void BroadcastTargetPacket(); + void ClearTarget(); + void CheckTarget(); + LPCHARACTER GetTarget() const { return m_pkChrTarget; } + + //////////////////////////////////////////////////////////////////////////////////////// + // Safebox + public: + int GetSafeboxSize() const; + void QuerySafeboxSize(); + void SetSafeboxSize(int size); + + CSafebox * GetSafebox() const; + void LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems); + void ChangeSafeboxSize(BYTE bSize); + void CloseSafebox(); + + void ReqSafeboxLoad(const char* pszPassword); + + void CancelSafeboxLoad( void ) { m_bOpeningSafebox = false; } + + void SetMallLoadTime(int t) { m_iMallLoadTime = t; } + int GetMallLoadTime() const { return m_iMallLoadTime; } + + CSafebox * GetMall() const; + void LoadMall(int iItemCount, TPlayerItem * pItems); + void CloseMall(); + + void SetSafeboxOpenPosition(); + float GetDistanceFromSafeboxOpen() const; + + protected: + CSafebox * m_pkSafebox; + int m_iSafeboxSize; + int m_iSafeboxLoadTime; + bool m_bOpeningSafebox; + + CSafebox * m_pkMall; + int m_iMallLoadTime; + + PIXEL_POSITION m_posSafeboxOpen; + + //////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////// + // Mounting + public: + void EnterMount(); + void MountVnum(DWORD vnum); + DWORD GetMountVnum() const { return m_dwMountVnum; } + DWORD GetLastMountTime() const { return m_dwMountTime; } + + bool CanUseHorseSkill(); + + // Horse + virtual void SetHorseLevel(int iLevel); + + virtual bool StartRiding(); + virtual bool StopRiding(); + + virtual DWORD GetMyHorseVnum() const; + + virtual void HorseDie(); + virtual bool ReviveHorse(); + + virtual void SendHorseInfo(); + virtual void ClearHorseInfo(); + + void HorseSummon(bool bSummon, bool bFromFar = false, DWORD dwVnum = 0, const char* name = 0); + + LPCHARACTER GetHorse() const { return m_chHorse; } + LPCHARACTER GetRider() const; // rider on horse + void SetRider(LPCHARACTER ch); + + bool IsRiding() const; + +#ifdef __PET_SYSTEM__ + public: + CPetSystem* GetPetSystem() { return m_petSystem; } + + protected: + CPetSystem* m_petSystem; +#endif +#ifdef USE_PET_SEAL_ON_LOGIN + public: + void EnterPet(); + bool SummonPetFromItem(LPITEM item); + void AddLoadedPetItems(DWORD itemID) { m_loadedPetItems.emplace(itemID); } + std::unordered_set& GetLoadedPetItems() { return m_loadedPetItems; } + protected: + std::unordered_set m_loadedPetItems; +#endif + + protected: + LPCHARACTER m_chHorse; + LPCHARACTER m_chRider; + + DWORD m_dwMountVnum; + DWORD m_dwMountTime; + + BYTE m_bSendHorseLevel; + BYTE m_bSendHorseHealthGrade; + BYTE m_bSendHorseStaminaGrade; + + //////////////////////////////////////////////////////////////////////////////////////// + // Detailed Log + public: + void DetailLog() { m_bDetailLog = !m_bDetailLog; } + void ToggleMonsterLog(); + void MonsterLog(const char* format, ...); + private: + bool m_bDetailLog; + bool m_bMonsterLog; + + //////////////////////////////////////////////////////////////////////////////////////// + // Empire + + public: + void SetEmpire(BYTE bEmpire); + BYTE GetEmpire() const { return m_bEmpire; } + + protected: + BYTE m_bEmpire; + + //////////////////////////////////////////////////////////////////////////////////////// + // Regen + public: + void SetRegen(LPREGEN pkRegen); + + protected: + PIXEL_POSITION m_posRegen; + float m_fRegenAngle; + LPREGEN m_pkRegen; + size_t regen_id_; // to help dungeon regen identification + // End of Regen + + //////////////////////////////////////////////////////////////////////////////////////// + // Resists & Proofs + public: + bool CannotMoveByAffect() const; + bool IsImmune(DWORD dwImmuneFlag); + void SetImmuneFlag(DWORD dw) { m_pointsInstant.dwImmuneFlag = dw; } + + protected: + void ApplyMobAttribute(const TMobTable* table); + // End of Resists & Proofs + + //////////////////////////////////////////////////////////////////////////////////////// + // QUEST + + public: + void SetQuestNPCID(DWORD vid); + DWORD GetQuestNPCID() const { return m_dwQuestNPCVID; } + LPCHARACTER GetQuestNPC() const; + + void SetQuestItemPtr(LPITEM item); + void ClearQuestItemPtr(); + LPITEM GetQuestItemPtr() const; + +#ifdef ENABLE_QUEST_DND_EVENT + void SetQuestDNDItemPtr(LPITEM item); + void ClearQuestDNDItemPtr(); + LPITEM GetQuestDNDItemPtr() const; +#endif + + void SetQuestBy(DWORD dwQuestVnum) { m_dwQuestByVnum = dwQuestVnum; } + DWORD GetQuestBy() const { return m_dwQuestByVnum; } + + int GetQuestFlag(const std::string& flag) const; + void SetQuestFlag(const std::string& flag, int value); + + void ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID); + + private: + DWORD m_dwQuestNPCVID; + DWORD m_dwQuestByVnum; + DWORD m_dwQuestItemVID{}; // @fixme304 (LPITEM -> DWORD) +#ifdef ENABLE_QUEST_DND_EVENT + DWORD m_dwQuestDNDItemVID{}; // @fixme304 (LPITEM -> DWORD) +#endif + + // Events + public: + bool StartStateMachine(int iPulse = 1); + void StopStateMachine(); + void UpdateStateMachine(DWORD dwPulse); + void SetNextStatePulse(int iPulseNext); + + void UpdateCharacter(DWORD dwPulse); + + protected: + DWORD m_dwNextStatePulse; + + // Marriage + public: + LPCHARACTER GetMarryPartner() const; + void SetMarryPartner(LPCHARACTER ch); + int GetMarriageBonus(DWORD dwItemVnum, bool bSum = true); + + void SetWeddingMap(marriage::WeddingMap* pMap); + marriage::WeddingMap* GetWeddingMap() const { return m_pWeddingMap; } + + private: + marriage::WeddingMap* m_pWeddingMap; + LPCHARACTER m_pkChrMarried; + + // Warp Character + public: + void StartWarpNPCEvent(); + + public: + void StartSaveEvent(); + void StartRecoveryEvent(); + void StartCheckSpeedHackEvent(); + void StartDestroyWhenIdleEvent(); + + LPEVENT m_pkDeadEvent; + LPEVENT m_pkStunEvent; + LPEVENT m_pkSaveEvent; + LPEVENT m_pkRecoveryEvent; + LPEVENT m_pkTimedEvent; + LPEVENT m_pkFishingEvent; + LPEVENT m_pkAffectEvent; + LPEVENT m_pkPoisonEvent; +#ifdef ENABLE_WOLFMAN_CHARACTER + LPEVENT m_pkBleedingEvent; +#endif + LPEVENT m_pkFireEvent; + LPEVENT m_pkWarpNPCEvent; + //DELAYED_WARP + //END_DELAYED_WARP + + // MINING + LPEVENT m_pkMiningEvent; + // END_OF_MINING + LPEVENT m_pkWarpEvent; + LPEVENT m_pkCheckSpeedHackEvent; + LPEVENT m_pkDestroyWhenIdleEvent; + LPEVENT m_pkPetSystemUpdateEvent; + + bool IsWarping() const { return m_pkWarpEvent ? true : false; } + + bool m_bHasPoisoned; +#ifdef ENABLE_WOLFMAN_CHARACTER + bool m_bHasBled; +#endif + + const CMob * m_pkMobData; + CMobInstance * m_pkMobInst; + + std::map m_mapMobSkillEvent; + + friend struct FuncSplashDamage; + friend struct FuncSplashAffect; + friend class CFuncShoot; + + public: + int GetPremiumRemainSeconds(BYTE bType) const; + + private: + int m_aiPremiumTimes[PREMIUM_MAX_NUM]; + + // CHANGE_ITEM_ATTRIBUTES + static const char msc_szLastChangeItemAttrFlag[]; + // END_OF_CHANGE_ITEM_ATTRIBUTES + + // NEW_HAIR_STYLE_ADD + public : + bool ItemProcess_Hair(LPITEM item, int iDestCell); + // END_NEW_HAIR_STYLE_ADD + + public : + void ClearSkill(); + void ClearSubSkill(); + + // RESET_ONE_SKILL + bool ResetOneSkill(DWORD dwVnum); + // END_RESET_ONE_SKILL + + private : + void SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag); + + // ARENA + private : + CArena *m_pArena; + bool m_ArenaObserver; + int m_nPotionLimit; + + public : + void SetArena(CArena* pArena) { m_pArena = pArena; } + void SetArenaObserverMode(bool flag) { m_ArenaObserver = flag; } + + CArena* GetArena() const { return m_pArena; } + bool GetArenaObserverMode() const { return m_ArenaObserver; } + + void SetPotionLimit(int count) { m_nPotionLimit = count; } + int GetPotionLimit() const { return m_nPotionLimit; } + // END_ARENA + + //PREVENT_TRADE_WINDOW + public: + bool IsOpenSafebox() const { return m_isOpenSafebox ? true : false; } + void SetOpenSafebox(bool b) { m_isOpenSafebox = b; } + + int GetSafeboxLoadTime() const { return m_iSafeboxLoadTime; } + void SetSafeboxLoadTime() { m_iSafeboxLoadTime = thecore_pulse(); } + //END_PREVENT_TRADE_WINDOW + private: + bool m_isOpenSafebox; + + public: + int GetSkillPowerByLevel(int level, bool bMob = false) const; + + //PREVENT_REFINE_HACK + int GetRefineTime() const { return m_iRefineTime; } + void SetRefineTime() { m_iRefineTime = thecore_pulse(); } + int m_iRefineTime; + //END_PREVENT_REFINE_HACK + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + int GetUseSeedOrMoonBottleTime() const { return m_iSeedTime; } + void SetUseSeedOrMoonBottleTime() { m_iSeedTime = thecore_pulse(); } + int m_iSeedTime; + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + + //PREVENT_PORTAL_AFTER_EXCHANGE + int GetExchangeTime() const { return m_iExchangeTime; } + void SetExchangeTime() { m_iExchangeTime = thecore_pulse(); } + int m_iExchangeTime; + //END_PREVENT_PORTAL_AFTER_EXCHANGE + + int m_iMyShopTime; + int GetMyShopTime() const { return m_iMyShopTime; } + void SetMyShopTime() { m_iMyShopTime = thecore_pulse(); } + + bool IsHack(bool bSendMsg = true, bool bCheckShopOwner = true, int limittime = g_nPortalLimitTime); + + // MONARCH + BOOL IsMonarch() const; + // END_MONARCH + void Say(const std::string & s); + + enum MONARCH_COOLTIME + { + MC_HEAL = 10, + MC_WARP = 60, + MC_TRANSFER = 60, + MC_TAX = (60 * 60 * 24 * 7), + MC_SUMMON = (60 * 60), + }; + + enum MONARCH_INDEX + { + MI_HEAL = 0, + MI_WARP, + MI_TRANSFER, + MI_TAX, + MI_SUMMON, + MI_MAX + }; + + DWORD m_dwMonarchCooltime[MI_MAX]; //todo move inside PlayerSlotT + DWORD m_dwMonarchCooltimelimit[MI_MAX]; //todo move inside PlayerSlotT + + void InitMC(); + DWORD GetMC(enum MONARCH_INDEX e) const; + void SetMC(enum MONARCH_INDEX e); + bool IsMCOK(enum MONARCH_INDEX e) const; + DWORD GetMCL(enum MONARCH_INDEX e) const; + DWORD GetMCLTime(enum MONARCH_INDEX e) const; + + public: + bool ItemProcess_Polymorph(LPITEM item); + + LPITEM* GetCubeItem() { return (m_PlayerSlots) ? m_PlayerSlots->pCubeItems.data() : nullptr; } + bool IsCubeOpen () const { return (m_pointsInstant.pCubeNpc); } + void SetCubeNpc(LPCHARACTER npc) { m_pointsInstant.pCubeNpc = npc; } + bool CanDoCube() const; + + public: + bool IsSiegeNPC() const; + + private: + + e_overtime m_eOverTime; + + public: + bool IsOverTime(e_overtime e) const { return (e == m_eOverTime); } + void SetOverTime(e_overtime e) { m_eOverTime = e; } + + private: + int m_deposit_pulse; + + public: + void UpdateDepositPulse(); + bool CanDeposit() const; + + private: + void __OpenPrivateShop(); + + public: + struct AttackedLog + { + DWORD dwPID; + DWORD dwAttackedTime; + + AttackedLog() : dwPID(0), dwAttackedTime(0) + { + } + }; + + AttackLog m_kAttackLog; + AttackedLog m_AttackedLog; + int m_speed_hack_count; + + private : + std::string m_strNewName; + + public : + const std::string GetNewName() const { return this->m_strNewName; } + void SetNewName(const std::string name) { this->m_strNewName = name; } + + public : + void GoHome(); + + private : + std::set m_known_guild; + + public : + void SendGuildName(CGuild* pGuild); + void SendGuildName(DWORD dwGuildID); + + private : + DWORD m_dwLogOffInterval; + + public : + DWORD GetLogOffInterval() const { return m_dwLogOffInterval; } + + public: + bool UnEquipSpecialRideUniqueItem (); + + bool CanWarp () const; + + public: + void AutoRecoveryItemProcess (const EAffectTypes); + + public: + void BuffOnAttr_AddBuffsFromItem(LPITEM pItem); + void BuffOnAttr_RemoveBuffsFromItem(LPITEM pItem); + + private: + void BuffOnAttr_ValueChange(BYTE bType, BYTE bOldValue, BYTE bNewValue); + void BuffOnAttr_ClearAll(); + + typedef std::map TMapBuffOnAttrs; + TMapBuffOnAttrs m_map_buff_on_attrs; + + public: + void SetArmada() { cannot_dead = true; } + void ResetArmada() { cannot_dead = false; } + private: + bool cannot_dead; + +#ifdef __PET_SYSTEM__ + private: + bool m_bIsPet; + public: + void SetPet() { m_bIsPet = true; } + bool IsPet() { return m_bIsPet; } +#endif +#ifdef NEW_ICEDAMAGE_SYSTEM + private: + DWORD m_dwNDRFlag; + std::set m_setNDAFlag; + public: + const DWORD GetNoDamageRaceFlag(); + void SetNoDamageRaceFlag(DWORD dwRaceFlag); + void UnsetNoDamageRaceFlag(DWORD dwRaceFlag); + void ResetNoDamageRaceFlag(); + const std::set & GetNoDamageAffectFlag(); + void SetNoDamageAffectFlag(DWORD dwAffectFlag); + void UnsetNoDamageAffectFlag(DWORD dwAffectFlag); + void ResetNoDamageAffectFlag(); +#endif + + private: + float m_fAttMul; + float m_fDamMul; + public: + float GetAttMul() { return this->m_fAttMul; } + void SetAttMul(float newAttMul) {this->m_fAttMul = newAttMul; } + float GetDamMul() { return this->m_fDamMul; } + void SetDamMul(float newDamMul) {this->m_fDamMul = newDamMul; } + + private: + bool IsValidItemPosition(TItemPos Pos) const; + + public: + + void DragonSoul_Initialize(); + + bool DragonSoul_IsQualified() const; + void DragonSoul_GiveQualification(); + + int DragonSoul_GetActiveDeck() const; + bool DragonSoul_IsDeckActivated() const; + bool DragonSoul_ActivateDeck(int deck_idx); + + void DragonSoul_DeactivateAll(); + + void DragonSoul_CleanUp(); + + public: + bool DragonSoul_RefineWindow_Open(LPENTITY pEntity); + bool DragonSoul_RefineWindow_Close(); + LPENTITY DragonSoul_RefineWindow_GetOpener() { return m_pointsInstant.m_pDragonSoulRefineWindowOpener; } + bool DragonSoul_RefineWindow_CanRefine(); + + private: + unsigned int itemAward_vnum; + char itemAward_cmd[20]; + public: + unsigned int GetItemAward_vnum() { return itemAward_vnum; } + char* GetItemAward_cmd() { return itemAward_cmd; } + void SetItemAward_vnum(unsigned int vnum) { itemAward_vnum = vnum; } + void SetItemAward_cmd(char* cmd) { strcpy(itemAward_cmd,cmd); } + + private: + timeval m_tvLastSyncTime; + int m_iSyncHackCount; + public: + void SetLastSyncTime(const timeval &tv) { memcpy(&m_tvLastSyncTime, &tv, sizeof(timeval)); } + const timeval& GetLastSyncTime() { return m_tvLastSyncTime; } + void SetSyncHackCount(int iCount) { m_iSyncHackCount = iCount;} + int GetSyncHackCount() { return m_iSyncHackCount; } + + // @fixme188 BEGIN + public: + void ResetRewardInfo() { + m_map_kDamage.clear(); + if (!IsPC()) + SetExp(0); + } + void DeadNoReward() { + if (!IsDead()) + ResetRewardInfo(); + Dead(); + } + // @fixme188 END + +#if defined(ENABLE_CHEQUE_SYSTEM) && defined(ENABLE_WON_EXCHANGE_WINDOW) + public: + bool WonExchange(const std::string & option, int value); +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + protected: + bool m_bAcceCombination, m_bAcceAbsorption; + + public: + bool IsAcceOpened(bool bCombination) {return bCombination ? m_bAcceCombination : m_bAcceAbsorption;} + bool IsAcceOpened() {return (m_bAcceCombination || m_bAcceAbsorption);} + void OpenAcce(bool bCombination); + void CloseAcce(); + void ClearAcceMaterials(); + bool CleanAcceAttr(LPITEM pkItem, LPITEM pkTarget); + std::vector GetAcceMaterials(); + const TItemPosEx* GetAcceMaterialsInfo(); + void SetAcceMaterial(int pos, LPITEM ptr); + bool AcceIsSameGrade(long lGrade); + DWORD GetAcceCombinePrice(long lGrade); + void GetAcceCombineResult(DWORD & dwItemVnum, DWORD & dwMinAbs, DWORD & dwMaxAbs); + BYTE CheckEmptyMaterialSlot(); + void AddAcceMaterial(TItemPos tPos, BYTE bPos); + void RemoveAcceMaterial(BYTE bPos); + BYTE CanRefineAcceMaterials(); + void RefineAcceMaterials(); +#endif + +#ifdef ENABLE_MOVE_CHANNEL + public: + bool ChangeChannel(BYTE newChannel); +#endif +#ifdef ENABLE_GUILD_TOKEN_AUTH + public: + bool IsGuildMaster() const; + uint64_t GetGuildToken() const; + void SendGuildToken(); +#endif +}; + +ESex GET_SEX(LPCHARACTER ch); + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_affect.cpp b/source-server/Srcs/Server/game/src/char_affect.cpp new file mode 100644 index 000000000..d8599c61b --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_affect.cpp @@ -0,0 +1,809 @@ +#include "stdafx.h" + +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "affect.h" +#include "packet.h" +#include "buffer_manager.h" +#include "desc_client.h" +#include "battle.h" +#include "guild.h" +#include "utils.h" +#include "locale_service.h" +#include "lua_incl.h" +#include "arena.h" +#include "horsename_manager.h" +#include "item.h" +#include "DragonSoul.h" +#include "../../common/CommonDefines.h" + +#define IS_NO_SAVE_AFFECT(type) ((type) == AFFECT_WAR_FLAG || (type) == AFFECT_REVIVE_INVISIBLE || ((type) >= AFFECT_PREMIUM_START && (type) <= AFFECT_PREMIUM_END) || (type) == AFFECT_MOUNT_BONUS) // @fixme156 added MOUNT_BONUS (if the game core crashes, the bonus would double if present in player.affect) +#define IS_NO_CLEAR_ON_DEATH_AFFECT(type) ((type) == AFFECT_BLOCK_CHAT || ((type) >= 500 && (type) < 600)) + +void SendAffectRemovePacket(LPDESC d, DWORD pid, DWORD type, BYTE point) +{ + TPacketGCAffectRemove ptoc; + ptoc.bHeader = HEADER_GC_AFFECT_REMOVE; + ptoc.dwType = type; + ptoc.bApplyOn = point; + d->Packet(&ptoc, sizeof(TPacketGCAffectRemove)); + + TPacketGDRemoveAffect ptod; + ptod.dwPID = pid; + ptod.dwType = type; + ptod.bApplyOn = point; + db_clientdesc->DBPacket(HEADER_GD_REMOVE_AFFECT, 0, &ptod, sizeof(ptod)); +} + +void SendAffectAddPacket(LPDESC d, CAffect * pkAff) +{ + TPacketGCAffectAdd ptoc; + ptoc.bHeader = HEADER_GC_AFFECT_ADD; + ptoc.elem.dwType = pkAff->dwType; + ptoc.elem.bApplyOn = pkAff->bApplyOn; + ptoc.elem.lApplyValue = pkAff->lApplyValue; + ptoc.elem.dwFlag = pkAff->dwFlag; + ptoc.elem.lDuration = pkAff->lDuration; + ptoc.elem.lSPCost = pkAff->lSPCost; + d->Packet(&ptoc, sizeof(TPacketGCAffectAdd)); +} +//////////////////////////////////////////////////////////////////// +// Affect +CAffect * CHARACTER::FindAffect(DWORD dwType, BYTE bApply) const +{ + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAffect = *it++; + + if (pkAffect->dwType == dwType && (bApply == APPLY_NONE || bApply == pkAffect->bApplyOn)) + return pkAffect; + } + + return NULL; +} + +EVENTFUNC(affect_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "affect_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->UpdateAffect()) + return 0; + else + return passes_per_sec; +} + +bool CHARACTER::UpdateAffect() +{ + if (GetPoint(POINT_HP_RECOVERY) > 0) + { + if (GetMaxHP() <= GetHP()) + { + PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY)); + } + else + { + int iVal = MIN(GetPoint(POINT_HP_RECOVERY), GetMaxHP() * 7 / 100); + + PointChange(POINT_HP, iVal); + PointChange(POINT_HP_RECOVERY, -iVal); + } + } + + if (GetPoint(POINT_SP_RECOVERY) > 0) + { + if (GetMaxSP() <= GetSP()) + PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY)); + else + { + int iVal = MIN(GetPoint(POINT_SP_RECOVERY), GetMaxSP() * 7 / 100); + + PointChange(POINT_SP, iVal); + PointChange(POINT_SP_RECOVERY, -iVal); + } + } + + if (GetPoint(POINT_HP_RECOVER_CONTINUE) > 0) + { + PointChange(POINT_HP, GetPoint(POINT_HP_RECOVER_CONTINUE)); + } + + if (GetPoint(POINT_SP_RECOVER_CONTINUE) > 0) + { + PointChange(POINT_SP, GetPoint(POINT_SP_RECOVER_CONTINUE)); + } + + AutoRecoveryItemProcess( AFFECT_AUTO_HP_RECOVERY ); + AutoRecoveryItemProcess( AFFECT_AUTO_SP_RECOVERY ); + + if (GetMaxStamina() > GetStamina()) + { + int iSec = (get_dword_time() - GetStopTime()) / 3000; + if (iSec) + PointChange(POINT_STAMINA, GetMaxStamina()/1); + } + + if (ProcessAffect()) + if (GetPoint(POINT_HP_RECOVERY) == 0 && GetPoint(POINT_SP_RECOVERY) == 0 && GetStamina() == GetMaxStamina()) + { + m_pkAffectEvent = NULL; + return false; + } + + return true; +} + +void CHARACTER::StartAffectEvent() +{ + if (m_pkAffectEvent) + return; + + char_event_info* info = AllocEventInfo(); + info->ch = this; + m_pkAffectEvent = event_create(affect_event, info, passes_per_sec); + sys_log(1, "StartAffectEvent %s %p %p", GetName(), this, get_pointer(m_pkAffectEvent)); +} + +void CHARACTER::ClearAffect(bool bSave) +{ + TAffectFlag afOld = m_afAffectFlag; + WORD wMovSpd = GetPoint(POINT_MOV_SPEED); + WORD wAttSpd = GetPoint(POINT_ATT_SPEED); + + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it; + + if (bSave) + { + if ( IS_NO_CLEAR_ON_DEATH_AFFECT(pkAff->dwType) || IS_NO_SAVE_AFFECT(pkAff->dwType) ) + { + ++it; + continue; + } + + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + } + + ComputeAffect(pkAff, false); + + it = m_list_pkAffect.erase(it); + CAffect::Release(pkAff); + } + + if (afOld != m_afAffectFlag || + wMovSpd != GetPoint(POINT_MOV_SPEED) || + wAttSpd != GetPoint(POINT_ATT_SPEED)) + UpdatePacket(); + + CheckMaximumPoints(); + + if (m_list_pkAffect.empty()) + event_cancel(&m_pkAffectEvent); +} + +int CHARACTER::ProcessAffect() +{ + bool bDiff = false; + CAffect *pkAff = NULL; + + for (int i = 0; i <= PREMIUM_MAX_NUM; ++i) + { + int aff_idx = i + AFFECT_PREMIUM_START; + + pkAff = FindAffect(aff_idx); + + if (!pkAff) + continue; + + int remain = GetPremiumRemainSeconds(i); + + if (remain < 0) + { + RemoveAffect(aff_idx); + bDiff = true; + } + else + pkAff->lDuration = remain + 1; + } + + ////////// HAIR_AFFECT + pkAff = FindAffect(AFFECT_HAIR); + if (pkAff) + { + // IF HAIR_LIMIT_TIME() < CURRENT_TIME() + if ( this->GetQuestFlag("hair.limit_time") < get_global_time()) + { + // SET HAIR NORMAL + this->SetPart(PART_HAIR, 0); + // REMOVE HAIR AFFECT + RemoveAffect(AFFECT_HAIR); + } + else + { + // INCREASE AFFECT DURATION + ++(pkAff->lDuration); + } + } + ////////// HAIR_AFFECT + + CHorseNameManager::instance().Validate(this); + + TAffectFlag afOld = m_afAffectFlag; + long lMovSpd = GetPoint(POINT_MOV_SPEED); + long lAttSpd = GetPoint(POINT_ATT_SPEED); + + itertype(m_list_pkAffect) it; + + it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + pkAff = *it; + + bool bEnd = false; + + if (pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END) + { + if (!GetGuild() || !GetGuild()->UnderAnyWar()) + bEnd = true; + } + + if (pkAff->lSPCost > 0) + { + if (GetSP() < pkAff->lSPCost) + bEnd = true; + else + PointChange(POINT_SP, -pkAff->lSPCost); + } + + // AFFECT_DURATION_BUG_FIX + + if ( --pkAff->lDuration <= 0 ) + { + bEnd = true; + } + // END_AFFECT_DURATION_BUG_FIX + + if (bEnd) + { + it = m_list_pkAffect.erase(it); + ComputeAffect(pkAff, false); + bDiff = true; + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + + CAffect::Release(pkAff); + + continue; + } + + ++it; + } + + if (bDiff) + { + if (afOld != m_afAffectFlag || + lMovSpd != GetPoint(POINT_MOV_SPEED) || + lAttSpd != GetPoint(POINT_ATT_SPEED)) + { + UpdatePacket(); + } + + CheckMaximumPoints(); + } + + if (m_list_pkAffect.empty()) + return true; + + return false; +} + +void CHARACTER::SaveAffect() +{ + TPacketGDAddAffect p; + + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it++; + + if (IS_NO_SAVE_AFFECT(pkAff->dwType)) + continue; + + sys_log(1, "AFFECT_SAVE: %u %u %d %d", pkAff->dwType, pkAff->bApplyOn, pkAff->lApplyValue, pkAff->lDuration); + + p.dwPID = GetPlayerID(); + p.elem.dwType = pkAff->dwType; + p.elem.bApplyOn = pkAff->bApplyOn; + p.elem.lApplyValue = pkAff->lApplyValue; + p.elem.dwFlag = pkAff->dwFlag; + p.elem.lDuration = pkAff->lDuration; + p.elem.lSPCost = pkAff->lSPCost; + db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p)); + } +} + +EVENTINFO(load_affect_login_event_info) +{ + DWORD pid; + DWORD count; + char* data; + + load_affect_login_event_info() + : pid( 0 ) + , count( 0 ) + , data( 0 ) + { + } +}; + +EVENTFUNC(load_affect_login_event) +{ + load_affect_login_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "load_affect_login_event_info> Null pointer" ); + return 0; + } + + DWORD dwPID = info->pid; + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(dwPID); + + if (!ch) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + + LPDESC d = ch->GetDesc(); + + if (!d) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + + if (d->IsPhase(PHASE_HANDSHAKE) || + d->IsPhase(PHASE_LOGIN) || + d->IsPhase(PHASE_SELECT) || + d->IsPhase(PHASE_DEAD) || + d->IsPhase(PHASE_LOADING)) + { + return PASSES_PER_SEC(1); + } + else if (d->IsPhase(PHASE_CLOSE)) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + else if (d->IsPhase(PHASE_GAME)) + { + sys_log(1, "Affect Load by Event"); + ch->LoadAffect(info->count, (TPacketAffectElement*)info->data); + M2_DELETE_ARRAY(info->data); + return 0; + } + else + { + sys_err("input_db.cpp:quest_login_event INVALID PHASE pid %d", ch->GetPlayerID()); + M2_DELETE_ARRAY(info->data); + return 0; + } +} + +void CHARACTER::LoadAffect(DWORD dwCount, TPacketAffectElement * pElements) +{ + m_bIsLoadedAffect = false; + + if (!GetDesc()->IsPhase(PHASE_GAME)) + { + if (test_server) + sys_log(0, "LOAD_AFFECT: Creating Event", GetName(), dwCount); + + load_affect_login_event_info* info = AllocEventInfo(); + + info->pid = GetPlayerID(); + info->count = dwCount; + info->data = M2_NEW char[sizeof(TPacketAffectElement) * dwCount]; + thecore_memcpy(info->data, pElements, sizeof(TPacketAffectElement) * dwCount); + + event_create(load_affect_login_event, info, PASSES_PER_SEC(1)); + + return; + } + + ClearAffect(true); + + if (test_server) + sys_log(0, "LOAD_AFFECT: %s count %d", GetName(), dwCount); + + TAffectFlag afOld = m_afAffectFlag; + + long lMovSpd = GetPoint(POINT_MOV_SPEED); + long lAttSpd = GetPoint(POINT_ATT_SPEED); + + for (DWORD i = 0; i < dwCount; ++i, ++pElements) + { + if (pElements->dwType == SKILL_MUYEONG) + continue; + + if (AFFECT_AUTO_HP_RECOVERY == pElements->dwType || AFFECT_AUTO_SP_RECOVERY == pElements->dwType) + { + LPITEM item = FindItemByID( pElements->dwFlag ); + + if (NULL == item) + continue; + + item->Lock(true); + } + + if (pElements->bApplyOn >= POINT_MAX_NUM) + { + sys_err("invalid affect data %s ApplyOn %u ApplyValue %d", + GetName(), pElements->bApplyOn, pElements->lApplyValue); + continue; + } + + if (test_server) + { + sys_log(0, "Load Affect : Affect %s %d %d", GetName(), pElements->dwType, pElements->bApplyOn ); + } + + CAffect* pkAff = CAffect::Acquire(); + m_list_pkAffect.emplace_back(pkAff); + + pkAff->dwType = pElements->dwType; + pkAff->bApplyOn = pElements->bApplyOn; + pkAff->lApplyValue = pElements->lApplyValue; + pkAff->dwFlag = pElements->dwFlag; + pkAff->lDuration = pElements->lDuration; + pkAff->lSPCost = pElements->lSPCost; + + SendAffectAddPacket(GetDesc(), pkAff); + + ComputeAffect(pkAff, true); + + } + + if ( CArenaManager::instance().IsArenaMap(GetMapIndex()) == true ) + { + RemoveGoodAffect(); + } + + if (afOld != m_afAffectFlag || lMovSpd != GetPoint(POINT_MOV_SPEED) || lAttSpd != GetPoint(POINT_ATT_SPEED)) + { + UpdatePacket(); + } + + StartAffectEvent(); + + m_bIsLoadedAffect = true; + + ComputePoints(); // @fixme156 + DragonSoul_Initialize(); + + // @fixme118 BEGIN (regain affect hp/mp) + if (!IsDead()) + { + PointChange(POINT_HP, GetMaxHP() - GetHP()); + PointChange(POINT_SP, GetMaxSP() - GetSP()); + } + // @fixme118 END +} + +bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, long lApplyValue, DWORD dwFlag, long lDuration, long lSPCost, bool bOverride, bool IsCube ) +{ + // CHAT_BLOCK + if (dwType == AFFECT_BLOCK_CHAT && lDuration > 1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ä Ǿϴ.")); + } + // END_OF_CHAT_BLOCK + + if (lDuration == 0) + { + sys_err("Character::AddAffect lDuration == 0 type %d", lDuration, dwType); + lDuration = 1; + } + + CAffect * pkAff = NULL; + + if (IsCube) + pkAff = FindAffect(dwType,bApplyOn); + else + pkAff = FindAffect(dwType); + + if (dwFlag == AFF_STUN) + { + if (m_posDest.x != GetX() || m_posDest.y != GetY()) + { + m_posDest.x = m_posStart.x = GetX(); + m_posDest.y = m_posStart.y = GetY(); + battle_end(this); + + SyncPacket(); + } + } + + if (pkAff && bOverride) + { + ComputeAffect(pkAff, false); + + if (GetDesc()) + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + else + { + pkAff = CAffect::Acquire(); + m_list_pkAffect.emplace_back(pkAff); + + } + + sys_log(1, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration); + sys_log(0, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration); + + pkAff->dwType = dwType; + pkAff->bApplyOn = bApplyOn; + pkAff->lApplyValue = lApplyValue; + pkAff->dwFlag = dwFlag; + pkAff->lDuration = lDuration; + pkAff->lSPCost = lSPCost; + + WORD wMovSpd = GetPoint(POINT_MOV_SPEED); + WORD wAttSpd = GetPoint(POINT_ATT_SPEED); + + ComputeAffect(pkAff, true); + + if (pkAff->dwFlag || wMovSpd != GetPoint(POINT_MOV_SPEED) || wAttSpd != GetPoint(POINT_ATT_SPEED)) + UpdatePacket(); + + StartAffectEvent(); + + if (IsPC()) + { + SendAffectAddPacket(GetDesc(), pkAff); + + if (IS_NO_SAVE_AFFECT(pkAff->dwType)) + return true; + + TPacketGDAddAffect p; + p.dwPID = GetPlayerID(); + p.elem.dwType = pkAff->dwType; + p.elem.bApplyOn = pkAff->bApplyOn; + p.elem.lApplyValue = pkAff->lApplyValue; + p.elem.dwFlag = pkAff->dwFlag; + p.elem.lDuration = pkAff->lDuration; + p.elem.lSPCost = pkAff->lSPCost; + db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p)); + } + + return true; +} + +void CHARACTER::RefreshAffect() +{ + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it++; + ComputeAffect(pkAff, true); + } +} + +void CHARACTER::ComputeAffect(CAffect * pkAff, bool bAdd) +{ + if (bAdd && pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END) + { + if (!GetGuild()) + return; + + if (!GetGuild()->UnderAnyWar()) + return; + } + + if (pkAff->dwFlag) + { + if (!bAdd) + m_afAffectFlag.Reset(pkAff->dwFlag); + else + m_afAffectFlag.Set(pkAff->dwFlag); + } + + if (bAdd) + PointChange(pkAff->bApplyOn, pkAff->lApplyValue); + else + PointChange(pkAff->bApplyOn, -pkAff->lApplyValue); + + if (pkAff->dwType == SKILL_MUYEONG) + { + if (bAdd) + StartMuyeongEvent(); + else + StopMuyeongEvent(); + } +} + +bool CHARACTER::RemoveAffect(CAffect * pkAff) +{ + if (!pkAff) + return false; + + // AFFECT_BUF_FIX + m_list_pkAffect.remove(pkAff); + // END_OF_AFFECT_BUF_FIX + + ComputeAffect(pkAff, false); + + if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType) + ComputePoints(); + else // @fixme110 + UpdatePacket(); + + CheckMaximumPoints(); + + if (test_server) + sys_log(0, "AFFECT_REMOVE: %s (flag %u apply: %u)", GetName(), pkAff->dwFlag, pkAff->bApplyOn); + + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + + CAffect::Release(pkAff); + return true; +} + +bool CHARACTER::RemoveAffect(DWORD dwType) +{ + // CHAT_BLOCK + if (dwType == AFFECT_BLOCK_CHAT) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ä ǮȽϴ.")); + } + // END_OF_CHAT_BLOCK + + bool flag = false; + + CAffect * pkAff; + + while ((pkAff = FindAffect(dwType))) + { + RemoveAffect(pkAff); + flag = true; + } + + return flag; +} + +bool CHARACTER::IsAffectFlag(DWORD dwAff) const +{ + return m_afAffectFlag.IsSet(dwAff); +} + +void CHARACTER::RemoveSkillAffect() +{ + RemoveAffect(SKILL_JEONGWI); + RemoveAffect(SKILL_GEOMKYUNG); + RemoveAffect(SKILL_CHUNKEON); + RemoveAffect(SKILL_EUNHYUNG); + RemoveAffect(SKILL_GYEONGGONG); + RemoveAffect(SKILL_GWIGEOM); + RemoveAffect(SKILL_TERROR); + RemoveAffect(SKILL_JUMAGAP); + RemoveAffect(SKILL_MANASHILED); + RemoveAffect(SKILL_HOSIN); + RemoveAffect(SKILL_REFLECT); + RemoveAffect(SKILL_KWAESOK); + RemoveAffect(SKILL_JEUNGRYEOK); + RemoveAffect(SKILL_GICHEON); +#ifdef ENABLE_WOLFMAN_CHARACTER + RemoveAffect(SKILL_JEOKRANG); + RemoveAffect(SKILL_CHEONGRANG); +#endif +} + +void CHARACTER::RemoveGoodAffect() +{ + RemoveAffect(AFFECT_MOV_SPEED); + RemoveAffect(AFFECT_ATT_SPEED); + RemoveAffect(AFFECT_STR); + RemoveAffect(AFFECT_DEX); + RemoveAffect(AFFECT_INT); + RemoveAffect(AFFECT_CON); + RemoveAffect(AFFECT_CHINA_FIREWORK); + RemoveSkillAffect(); +} + +bool CHARACTER::IsGoodAffect(BYTE bAffectType) const +{ + switch (bAffectType) + { + case (AFFECT_MOV_SPEED): + case (AFFECT_ATT_SPEED): + case (AFFECT_STR): + case (AFFECT_DEX): + case (AFFECT_INT): + case (AFFECT_CON): + case (AFFECT_CHINA_FIREWORK): + + case (SKILL_JEONGWI): + case (SKILL_GEOMKYUNG): + case (SKILL_CHUNKEON): + case (SKILL_EUNHYUNG): + case (SKILL_GYEONGGONG): + case (SKILL_GWIGEOM): + case (SKILL_TERROR): + case (SKILL_JUMAGAP): + case (SKILL_MANASHILED): + case (SKILL_HOSIN): + case (SKILL_REFLECT): + case (SKILL_KWAESOK): + case (SKILL_JEUNGRYEOK): + case (SKILL_GICHEON): +#ifdef ENABLE_WOLFMAN_CHARACTER + + case (SKILL_JEOKRANG): + case (SKILL_CHEONGRANG): +#endif + return true; + } + return false; +} + +void CHARACTER::RemoveBadAffect() +{ + sys_log(0, "RemoveBadAffect %s", GetName()); + RemovePoison(); +#ifdef ENABLE_WOLFMAN_CHARACTER + RemoveBleeding(); +#endif + RemoveFire(); + + RemoveAffect(AFFECT_STUN); + + RemoveAffect(AFFECT_SLOW); + + RemoveAffect(SKILL_TUSOK); + + //RemoveAffect(SKILL_CURSE); + + //RemoveAffect(SKILL_PABUP); + + //RemoveAffect(AFFECT_FAINT); + + //RemoveAffect(AFFECT_WEB); + + //RemoveAffect(AFFECT_SLEEP); + + //RemoveAffect(AFFECT_CURSE); + + //RemoveAffect(AFFECT_PARALYZE); + + //RemoveAffect(SKILL_BUDONG); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_battle.cpp b/source-server/Srcs/Server/game/src/char_battle.cpp new file mode 100644 index 000000000..16a07f425 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_battle.cpp @@ -0,0 +1,3566 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc.h" +#include "desc_manager.h" +#include "char_manager.h" +#include "item.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "battle.h" +#include "pvp.h" +#include "skill.h" +#include "start_position.h" +#include "profiler.h" +#include "cmd.h" +#include "dungeon.h" +#include "log.h" +#include "unique_item.h" +#include "priv_manager.h" +#include "db.h" +#include "vector.h" +#include "marriage.h" +#include "arena.h" +#include "regen.h" +#include "monarch.h" +#include "exchange.h" +#include "shop_manager.h" +#include "castle.h" +#include "ani.h" +#include "packet.h" +#include "party.h" +#include "affect.h" +#include "guild.h" +#include "guild_manager.h" +#include "questmanager.h" +#include "questlua.h" +#include "threeway_war.h" +#include "BlueDragon.h" +#include "DragonLair.h" + +#define ENABLE_EFFECT_PENETRATE +#define ENABLE_NEWEXP_CALCULATION +// #define ENABLE_NO_DAMAGE_QUEST_RUNNING + +static DWORD __GetPartyExpNP(const DWORD level) +{ + if (!level || level > PLAYER_EXP_TABLE_MAX) + return 14000; + return party_exp_distribute_table[level]; +} + +static int __GetExpLossPerc(const DWORD level) +{ + if (!level || level > PLAYER_EXP_TABLE_MAX) + return 1; + return aiExpLossPercents[level]; +} + +DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp) +{ + if (PLAYER_MAX_LEVEL_CONST < ch->GetLevel()) + { + double ret = 0.95; + double factor = 0.1; + + for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i) + { + if ( (i%10) == 0) + factor /= 2.0; + + ret *= 1.0 - factor; + } + + ret = ret * static_cast(exp); + + if (ret < 1.0) + return 1; + + return static_cast(ret); + } + + return exp; +} + +bool CHARACTER::CanBeginFight() const +{ + if (!CanMove()) + return false; + + return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun(); +} + +void CHARACTER::BeginFight(LPCHARACTER pkVictim) +{ + SetVictim(pkVictim); + SetPosition(POS_FIGHTING); + SetNextStatePulse(1); +} + +bool CHARACTER::CanFight() const +{ + return m_pointsInstant.position >= POS_FIGHTING ? true : false; +} + +void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim) +{ + TPacketGCCreateFly packFly; + + packFly.bHeader = HEADER_GC_CREATE_FLY; + packFly.bType = bType; + packFly.dwStartVID = GetVID(); + packFly.dwEndVID = pkVictim->GetVID(); + + PacketAround(&packFly, sizeof(TPacketGCCreateFly)); +} + +void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod) +{ + if (pkKiller->GetSP() >= pkKiller->GetMaxSP()) + return; + + bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000; + bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000; + + if (iMethod == 1) + { + int num = number(0, 3); + + if (!num) + { + int iLvDelta = GetLevel() - pkKiller->GetLevel(); + int iAmount = 0; + + if (iLvDelta >= 5) + iAmount = 10; + else if (iLvDelta >= 0) + iAmount = 6; + else if (iLvDelta >= -3) + iAmount = 2; + + if (iAmount != 0) + { + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + + if (iAmount >= 11) + CreateFly(FLY_SP_BIG, pkKiller); + else if (iAmount >= 7) + CreateFly(FLY_SP_MEDIUM, pkKiller); + else + CreateFly(FLY_SP_SMALL, pkKiller); + + pkKiller->PointChange(POINT_SP, iAmount); + } + } + } + else + { + if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2)) + { + int iAmount; + + if (bAttacking) + iAmount = 2 + GetMaxSP() / 100; + else if (bMoving) + iAmount = 3 + GetMaxSP() * 2 / 100; + else + iAmount = 10 + GetMaxSP() * 3 / 100; + + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + pkKiller->PointChange(POINT_SP, iAmount); + } + else + { + int iAmount; + + if (bAttacking) + iAmount = 2 + pkKiller->GetMaxSP() / 200; + else if (bMoving) + iAmount = 2 + pkKiller->GetMaxSP() / 100; + else + { + if (pkKiller->GetHP() < pkKiller->GetMaxHP()) + iAmount = 2 + (pkKiller->GetMaxSP() / 100); + else + iAmount = 9 + (pkKiller->GetMaxSP() / 100); + } + + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + pkKiller->PointChange(POINT_SP, iAmount); + } + } +} + +bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType) +{ + if (test_server) + sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange()); + + if (!CanMove()) + return false; + + // CASTLE + if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim)) + return false; + // CASTLE + + // @fixme131 + if (!battle_is_attackable(this, pkVictim)) + return false; + + DWORD dwCurrentTime = get_dword_time(); + + if (IsPC()) + { + if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime)) + return false; + + if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime()) + return false; + } + else + { + MonsterChat(MONSTER_CHAT_ATTACK); + } + + pkVictim->SetSyncOwner(this); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(this); + + int iRet; + + if (bType == 0) + { + switch (GetMobBattleType()) + { + case BATTLE_TYPE_MELEE: + case BATTLE_TYPE_POWER: + case BATTLE_TYPE_TANKER: + case BATTLE_TYPE_SUPER_POWER: + case BATTLE_TYPE_SUPER_TANKER: + iRet = battle_melee_attack(this, pkVictim); + break; + + case BATTLE_TYPE_RANGE: + FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); + iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE; + break; + + case BATTLE_TYPE_MAGIC: + FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); + iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE; + break; + + default: + sys_err("Unhandled battle type %d", GetMobBattleType()); + iRet = BATTLE_NONE; + break; + } + } + else + { + if (IsPC() == true) + { + if (dwCurrentTime - m_dwLastSkillTime > 1500) + { + sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)", + GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime)); + return false; + } + } + + sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():""); + iRet = ComputeSkill(bType, pkVictim); + } + + if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD) + { + OnMove(true); + pkVictim->OnMove(); + + // only pc sets victim null. For npc, state machine will reset this. + if (BATTLE_DEAD == iRet && IsPC()) + SetVictim(NULL); + + return true; + } + + return false; +} + +void CHARACTER::DeathPenalty(BYTE bTown) +{ + sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown); + + Cube_close(this); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + CloseAcce(); +#endif + + if (GetLevel() < 10) + { + sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + return; + } + + if (number(0, 2)) + { + sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + return; + } + + if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY)) + { + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + + // NO_DEATH_PENALTY_BUG_FIX + if (!bTown) + { + if (FindAffect(AFFECT_NO_DEATH_PENALTY)) + { + sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + RemoveAffect(AFFECT_NO_DEATH_PENALTY); + return; + } + } + // END_OF_NO_DEATH_PENALTY_BUG_FIX + + int iLoss = ((GetNextExp() * __GetExpLossPerc(GetLevel())) / 100); + + iLoss = MIN(800000, iLoss); + + if (bTown) + iLoss = 0; + + if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS)) + iLoss /= 2; + + sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, __GetExpLossPerc(GetLevel())); + + PointChange(POINT_EXP, -iLoss, true); + } +} + +bool CHARACTER::IsStun() const +{ + if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN)) + return true; + + return false; +} + +EVENTFUNC(StunEvent) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "StunEvent> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + ch->m_pkStunEvent = NULL; + ch->Dead(); + return 0; +} + +void CHARACTER::Stun() +{ + if (IsStun()) + return; + + if (IsDead()) + return; + + if (!IsPC() && m_pkParty) + { + m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0); + } + + sys_log(1, "%s: Stun %p", GetName(), this); + + PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY)); + PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY)); + + CloseMyShop(); + + event_cancel(&m_pkRecoveryEvent); + + TPacketGCStun pack; + pack.header = HEADER_GC_STUN; + pack.vid = m_vid; + PacketAround(&pack, sizeof(pack)); + + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); + + if (m_pkStunEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3)); +} + +EVENTINFO(SCharDeadEventInfo) +{ + bool isPC; + uint32_t dwID; + + SCharDeadEventInfo() + : isPC(0) + , dwID(0) + { + } +}; + +EVENTFUNC(dead_event) +{ + const SCharDeadEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "dead_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = NULL; + + if (true == info->isPC) + { + ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID ); + } + else + { + ch = CHARACTER_MANAGER::instance().Find( info->dwID ); + } + + if (NULL == ch) + { + sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID ); + return 0; + } + + ch->m_pkDeadEvent = NULL; + + if (ch->GetDesc()) + { + ch->GetDesc()->SetPhase(PHASE_GAME); + + ch->SetPosition(POS_STANDING); + + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + ch->WarpSet(pos.x, pos.y); + else + { + sys_err("cannot find spawn position (name %s)", ch->GetName()); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + + ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true); + + ch->DeathPenalty(0); + + ch->StartRecoveryEvent(); + + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + } + else + { + if (ch->IsMonster() == true) + { + if (ch->IsRevive() == false && ch->HasReviverInParty() == true) + { + ch->SetPosition(POS_STANDING); + ch->SetHP(ch->GetMaxHP()); + + ch->ViewReencode(); + + ch->SetAggressive(); + ch->SetRevive(true); + + return 0; + } + } + + M2_DESTROY_CHARACTER(ch); + } + + return 0; +} + +bool CHARACTER::IsDead() const +{ + if (m_pointsInstant.position == POS_DEAD) + return true; + + return false; +} + +#define GetGoldMultipler() (distribution_test_server ? 3 : 1) + +void CHARACTER::RewardGold(LPCHARACTER pkAttacker) +{ + // ADD_PREMIUM + bool isAutoLoot = + (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 || + pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT)) + ? true : false; + // END_OF_ADD_PREMIUM + + PIXEL_POSITION pos = GetXYZ(); // @fixme194 GetMovablePosition is useless here + + int iTotalGold = 0; + int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent; + + if (pkAttacker->IsPC()) + iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100; + + if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS)) + iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100); + + iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100; + + // ADD_PREMIUM + if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 || + pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD)) + iGoldPercent += iGoldPercent; + // END_OF_ADD_PREMIUM + + if (iGoldPercent > 100) + iGoldPercent = 100; + + int iPercent; + + if (GetMobRank() >= MOB_RANK_BOSS) + iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100); + else + iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100); + + if (number(1, 100) > iPercent) + return; + + int iGoldMultipler = GetGoldMultipler(); + + if (1 == number(1, 50000)) + iGoldMultipler *= 10; + else if (1 == number(1, 10000)) + iGoldMultipler *= 5; + + if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) + if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) + iGoldMultipler *= 2; + + if (test_server) + pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker)); + + LPITEM item; + + int iGold10DropPct = 100; + iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP)); + + if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0) + { + if (1 == number(1, iGold10DropPct)) + iGoldMultipler *= 10; + + int iSplitCount = number(25, 35); + + for (int i = 0; i < iSplitCount; ++i) + { + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount; + if (test_server) + sys_log(0, "iGold %d", iGold); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + if (iGold == 0) + continue; + + if (test_server) + { + sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler); + sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax); + } + + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) + { + pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23); + pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + iTotalGold += iGold; // Total gold + } + } + } + + else if (1 == number(1, iGold10DropPct)) + { + for (int i = 0; i < 10; ++i) + { + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + if (iGold == 0) + continue; + + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) + { + pos.x = GetX() + (number(-7, 7) * 20); + pos.y = GetY() + (number(-7, 7) * 20); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + iTotalGold += iGold; // Total gold + } + } + } + else + { + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + int iSplitCount; + + if (iGold >= 3) + iSplitCount = number(1, 3); + else if (GetMobRank() >= MOB_RANK_BOSS) + { + iSplitCount = number(3, 10); + + if ((iGold / iSplitCount) == 0) + iSplitCount = 1; + } + else + iSplitCount = 1; + + if (iGold != 0) + { + iTotalGold += iGold; // Total gold + + for (int i = 0; i < iSplitCount; ++i) + { + if (isAutoLoot) + { + pkAttacker->GiveGold(iGold / iSplitCount); + } + else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) + { + pos.x = GetX() + (number(-7, 7) * 20); + pos.y = GetY() + (number(-7, 7) * 20); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + } + } + + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold); +} + +void CHARACTER::Reward(bool bItemDrop) +{ + if (GetRaceNum() == 5001) + { + // @fixme194 BEGIN GetMovablePosition should not return + PIXEL_POSITION pos = GetXYZ(); + SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos); + // @fixme194 END + + LPITEM item; + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100; + iGold *= GetGoldMultipler(); + int iSplitCount = number(25, 35); + + sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount); + + for (int i = 1; i <= iSplitCount; ++i) + { + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) + { + if (i != 0) + { + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + + pos.x += GetX(); + pos.y += GetY(); + } + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + return; + } + + LPCHARACTER pkAttacker = DistributeExp(); + +#ifdef ENABLE_KILL_EVENT_FIX + if (!pkAttacker && !(pkAttacker = GetMostAttacked())) + return; +#else + if (!pkAttacker) + return; +#endif + + if (pkAttacker->IsPC()) + { + if ((GetLevel() - pkAttacker->GetLevel()) >= -10) + { + if (pkAttacker->GetRealAlignment() < 0) + { + if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL)) + pkAttacker->UpdateAlignment(14); + else + pkAttacker->UpdateAlignment(7); + } + else + pkAttacker->UpdateAlignment(2); + } + + pkAttacker->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum()); + CHARACTER_MANAGER::instance().KillLog(GetRaceNum()); + + if (!number(0, 9)) + { + if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY)) + { + int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100; + pkAttacker->PointChange(POINT_HP, iHP); + CreateFly(FLY_HP_SMALL, pkAttacker); + } + + if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER)) + { + int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100; + pkAttacker->PointChange(POINT_SP, iSP); + CreateFly(FLY_SP_SMALL, pkAttacker); + } + } + } + + if (!bItemDrop) + return; + + // @fixme194 BEGIN GetMovablePosition should not return + PIXEL_POSITION pos = GetXYZ(); + SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos); + // @fixme194 END + + if (test_server) + sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName()); + RewardGold(pkAttacker); + + LPITEM item; + + static std::vector s_vec_item; + s_vec_item.clear(); + + if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item)) + { + if (s_vec_item.size() == 0); + else if (s_vec_item.size() == 1) + { + item = s_vec_item[0]; + item->AddToGround(GetMapIndex(), pos); + +#ifdef ENABLE_DICE_SYSTEM + if (pkAttacker->GetParty()) + { + FPartyDropDiceRoll f(item, pkAttacker); + f.Process(this); + } + else + item->SetOwnership(pkAttacker); +#else + item->SetOwnership(pkAttacker); +#endif + + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); + } + else + { + int iItemIdx = s_vec_item.size() - 1; + + std::priority_queue > pq; + + int total_dam = 0; + + for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) + { + int iDamage = it->second.iTotalDamage; + if (iDamage > 0) + { + LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); + + if (ch) + { + pq.push(std::make_pair(iDamage, ch)); + total_dam += iDamage; + } + } + } + + std::vector v; + + while (!pq.empty() && pq.top().first * 10 >= total_dam) + { + v.emplace_back(pq.top().second); + pq.pop(); + } + + if (v.empty()) + { + while (iItemIdx >= 0) + { + item = s_vec_item[iItemIdx--]; + + if (!item) + { + sys_err("item null in vector idx %d", iItemIdx + 1); + continue; + } + + item->AddToGround(GetMapIndex(), pos); + + //item->SetOwnership(pkAttacker); + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); + } + } + else + { + std::vector::iterator it = v.begin(); + + while (iItemIdx >= 0) + { + item = s_vec_item[iItemIdx--]; + + if (!item) + { + sys_err("item null in vector idx %d", iItemIdx + 1); + continue; + } + + item->AddToGround(GetMapIndex(), pos); + + LPCHARACTER ch = *it; + + if (ch->GetParty()) + ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY()); + + ++it; + + if (it == v.end()) + it = v.begin(); + +#ifdef ENABLE_DICE_SYSTEM + if (ch->GetParty()) + { + FPartyDropDiceRoll f(item, ch); + f.Process(this); + } + else + item->SetOwnership(ch); +#else + item->SetOwnership(ch); +#endif + + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); + } + } + } + } + + m_map_kDamage.clear(); +} + +struct TItemDropPenalty +{ + int iInventoryPct; // Range: 1 ~ 1000 + int iInventoryQty; // Range: -- + int iEquipmentPct; // Range: 1 ~ 100 + int iEquipmentQty; // Range: -- +}; + +TItemDropPenalty aItemDropPenalty_kor[9] = +{ + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 25, 1, 5, 1 }, + { 50, 2, 10, 1 }, + { 75, 4, 15, 1 }, + { 100, 8, 20, 1 }, +}; + +void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller) +{ + if (GetMyShop()) + return; + + if (GetLevel() < 50) + return; + + struct TItemDropPenalty * table = &aItemDropPenalty_kor[0]; + + if (GetLevel() < 10) + return; + + int iAlignIndex; + + if (GetRealAlignment() >= 120000) + iAlignIndex = 0; + else if (GetRealAlignment() >= 80000) + iAlignIndex = 1; + else if (GetRealAlignment() >= 40000) + iAlignIndex = 2; + else if (GetRealAlignment() >= 10000) + iAlignIndex = 3; + else if (GetRealAlignment() >= 0) + iAlignIndex = 4; + else if (GetRealAlignment() > -40000) + iAlignIndex = 5; + else if (GetRealAlignment() > -80000) + iAlignIndex = 6; + else if (GetRealAlignment() > -120000) + iAlignIndex = 7; + else + iAlignIndex = 8; + + std::vector > vec_item; + LPITEM pkItem; + int i; + bool isDropAllEquipments = false; + + TItemDropPenalty & r = table[iAlignIndex]; + sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct); + + bool bDropInventory = r.iInventoryPct >= number(1, 1000); + bool bDropEquipment = r.iEquipmentPct >= number(1, 100); + bool bDropAntiDropUniqueItem = false; + + if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)) + { + bDropInventory = false; + bDropEquipment = false; + bDropAntiDropUniqueItem = true; + } + + // @fixme198 BEGIN + if (bDropInventory || bDropEquipment) { + if (pkKiller) + pkKiller->SetExchangeTime(); + SetExchangeTime(); + } + // @fixme198 END + + if (bDropInventory) // Drop Inventory + { + std::vector vec_bSlots; + const auto isQuestRunning = quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning(); + + for (i = 0; i < INVENTORY_MAX_NUM; ++i) + { + auto pkItem = GetInventoryItem(i); + if (!pkItem || (isQuestRunning && pkItem->GetType() == ITEM_QUEST)) // @fixme198 (item_quest items can be used without being consumed) + continue; + vec_bSlots.push_back(i); + } + + if (!vec_bSlots.empty()) + { + msl::random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); + + int iQty = MIN(vec_bSlots.size(), r.iInventoryQty); + + if (iQty) + iQty = number(1, iQty); + + for (i = 0; i < iQty; ++i) + { + pkItem = GetInventoryItem(vec_bSlots[i]); + + if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) + continue; + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); + vec_item.emplace_back(pkItem->RemoveFromCharacter(), INVENTORY); + } + } + else if (iAlignIndex == 8) + isDropAllEquipments = true; + } + + if (bDropEquipment) // Drop Equipment + { + std::vector vec_bSlots; + + for (i = 0; i < WEAR_MAX_NUM; ++i) + if (GetWear(i)) + vec_bSlots.emplace_back(i); + + if (!vec_bSlots.empty()) + { + msl::random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); + int iQty; + + if (isDropAllEquipments) + iQty = vec_bSlots.size(); + else + iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty)); + + if (iQty) + iQty = number(1, iQty); + + for (i = 0; i < iQty; ++i) + { + pkItem = GetWear(vec_bSlots[i]); + + if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) + continue; + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); + vec_item.emplace_back(pkItem->RemoveFromCharacter(), EQUIPMENT); + } + } + } + + if (bDropAntiDropUniqueItem) + { + LPITEM pkItem; + + pkItem = GetWear(WEAR_UNIQUE1); + + if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255); + vec_item.emplace_back(pkItem->RemoveFromCharacter(), EQUIPMENT); + } + + pkItem = GetWear(WEAR_UNIQUE2); + + if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255); + vec_item.emplace_back(pkItem->RemoveFromCharacter(), EQUIPMENT); + } + } + + { + PIXEL_POSITION pos; + pos.x = GetX(); + pos.y = GetY(); + + unsigned int i; + + for (i = 0; i < vec_item.size(); ++i) + { + LPITEM item = vec_item[i].first; + int window = vec_item[i].second; + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); + LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : "")); + + pos.x = GetX() + number(-7, 7) * 20; + pos.y = GetY() + number(-7, 7) * 20; + } + } +} + +class FPartyAlignmentCompute +{ + public: + FPartyAlignmentCompute(int iAmount, int x, int y) + { + m_iAmount = iAmount; + m_iCount = 0; + m_iStep = 0; + m_iKillerX = x; + m_iKillerY = y; + } + + void operator () (LPCHARACTER pkChr) + { + if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE) + { + if (m_iStep == 0) + { + ++m_iCount; + } + else + { + pkChr->UpdateAlignment(m_iAmount / m_iCount); + } + } + } + + int m_iAmount; + int m_iCount; + int m_iStep; + + int m_iKillerX; + int m_iKillerY; +}; + +void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) +{ + if (IsDead()) + return; +#ifndef DISABLE_STOP_RIDING_WHEN_DIE + { + if (IsHorseRiding()) + { + StopRiding(); + } + else if (GetMountVnum()) + { + RemoveAffect(AFFECT_MOUNT_BONUS); + m_dwMountVnum = 0; + UnEquipSpecialRideUniqueItem(); + + UpdatePacket(); + } + + } +#endif + + if (!pkKiller && m_dwKillerPID) + pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID); + + m_dwKillerPID = 0; + + bool isAgreedPVP = false; + bool isUnderGuildWar = false; + bool isDuel = false; + bool isForked = false; + + if (pkKiller && pkKiller->IsPC()) + { + if (pkKiller->m_pkChrTarget == this) + pkKiller->SetTarget(NULL); + + if (!IsPC() && pkKiller->GetDungeon()) + pkKiller->GetDungeon()->IncKillCount(pkKiller, this); + + isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID()); + isDuel = CArenaManager::instance().OnDead(pkKiller, this); + + if (IsPC()) + { + CGuild * g1 = GetGuild(); + CGuild * g2 = pkKiller->GetGuild(); + + if (g1 && g2) + if (g1->UnderWar(g2->GetID())) + isUnderGuildWar = true; + + pkKiller->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC); + CGuildManager::instance().Kill(pkKiller, this); + } + } + +#ifdef ENABLE_QUEST_DIE_EVENT + if (IsPC()) + { + if (pkKiller) + SetQuestNPCID(pkKiller->GetVID()); + // quest::CQuestManager::instance().Die(GetPlayerID(), quest::QUEST_NO_NPC); + quest::CQuestManager::instance().Die(GetPlayerID(), (pkKiller)?pkKiller->GetRaceNum():quest::QUEST_NO_NPC); + } +#endif + + //CHECK_FORKEDROAD_WAR + if (IsPC()) + { + if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex())) + isForked = true; + } + //END_CHECK_FORKEDROAD_WAR + + if (pkKiller && + !isAgreedPVP && + !isUnderGuildWar && + IsPC() && + !isDuel && + !isForked && + !IS_CASTLE_MAP(GetMapIndex())) + { + if (GetGMLevel() == GM_PLAYER || test_server) + { + ItemDropPenalty(pkKiller); + } + } + + // CASTLE_SIEGE + if (IS_CASTLE_MAP(GetMapIndex())) + { + if (CASTLE_FROG_VNUM == GetRaceNum()) + castle_frog_die(this, pkKiller); + else if (castle_is_guard_vnum(GetRaceNum())) + castle_guard_die(this, pkKiller); + else if (castle_is_tower_vnum(GetRaceNum())) + castle_tower_die(this, pkKiller); + } + // CASTLE_SIEGE + + if (true == isForked) + { + CThreeWayWar::instance().onDead( this, pkKiller ); + } + + SetPosition(POS_DEAD); + ClearAffect(true); + + if (pkKiller && IsPC()) + { + if (!pkKiller->IsPC()) + { + if (!isForked) + { + sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this); + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName()); + } + } + else + { + sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller)); + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + + if (GetEmpire() != pkKiller->GetEmpire()) + { + int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT)); + + PointChange(POINT_EMPIRE_POINT, -(iEP / 10)); + pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5); + + if (GetPoint(POINT_EMPIRE_POINT) < 10) + { + } + + char buf[256]; + snprintf(buf, sizeof(buf), + "%d %d %d %s %d %d %d %s", + GetEmpire(), GetAlignment(), GetPKMode(), GetName(), + pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); + + LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); + } + else + { + if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked) + { + int iNoPenaltyProb = 0; + + if (pkKiller->GetAlignment() >= 0) // 1/3 percent down + iNoPenaltyProb = 33; + else // 4/5 percent down + iNoPenaltyProb = 20; + + if (number(1, 100) < iNoPenaltyProb) + pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ʾҽϴ.")); + else + { + if (pkKiller->GetParty()) + { + FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY()); + pkKiller->GetParty()->ForEachOnlineMember(f); + + if (f.m_iCount == 0) + pkKiller->UpdateAlignment(-20000); + else + { + sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount); + + f.m_iStep = 1; + pkKiller->GetParty()->ForEachOnlineMember(f); + } + } + else + pkKiller->UpdateAlignment(-20000); + } + } + + char buf[256]; + snprintf(buf, sizeof(buf), + "%d %d %d %s %d %d %d %s", + GetEmpire(), GetAlignment(), GetPKMode(), GetName(), + pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); + + LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); + } + } + } + else + { + sys_log(1, "DEAD: %s %p", GetName(), this); + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + } + + ClearSync(); + + //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID()); + event_cancel(&m_pkStunEvent); + + if (IsPC()) + { + m_dwLastDeadTime = get_dword_time(); + SetKillerMode(false); + GetDesc()->SetPhase(PHASE_DEAD); + } + else + { + if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD)) + { + if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD))) + { + if (GetMobTable().dwResurrectionVnum) + { + // DUNGEON_MONSTER_REBIRTH_BUG_FIX + LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation()); + if (GetDungeon() && chResurrect) + { + chResurrect->SetDungeon(GetDungeon()); + } + // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX + + Reward(false); + } + else if (IsRevive() == true) + { + Reward(false); + } + else + { + Reward(true); // Drops gold, item, etc.. + } + } + else + { + if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time()) + { + pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000; + pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ߿ ɿ ϴ.")); + } + } + } + } + + // BOSS_KILL_LOG + if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC()) + { + char buf[51]; + snprintf(buf, sizeof(buf), "%d %ld", g_bChannel, pkKiller->GetMapIndex()); + if (IsStone()) + LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf); + else + LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf); + } + // END_OF_BOSS_KILL_LOG + + TPacketGCDead pack; + pack.header = HEADER_GC_DEAD; + pack.vid = m_vid; + PacketAround(&pack, sizeof(pack)); + + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); + + if (GetDesc() != NULL) { + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + SendAffectAddPacket(GetDesc(), *it++); + } + + if (isDuel == false) + { + if (m_pkDeadEvent) + { + sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); + event_cancel(&m_pkDeadEvent); + } + + if (IsStone()) + ClearStone(); + + if (GetDungeon()) + { + GetDungeon()->DeadCharacter(this); + } + + SCharDeadEventInfo* pEventInfo = AllocEventInfo(); + + if (IsPC()) + { + pEventInfo->isPC = true; + pEventInfo->dwID = this->GetPlayerID(); + + m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180)); + } + else + { + pEventInfo->isPC = false; + pEventInfo->dwID = this->GetVID(); + + if (IsRevive() == false && HasReviverInParty() == true) + { + m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3)); + } + else + { + m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10)); + } + } + + sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); + } + + if (m_pkExchange) + m_pkExchange->Cancel(); + + if (IsCubeOpen()) + Cube_close(this); +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + if (IsPC()) + CloseAcce(); +#endif + + CShopManager::instance().StopShopping(this); + CloseMyShop(); + CloseSafebox(); + + if (IsMonster() && 2493 == GetMobTable().dwVnum) + { + if (pkKiller && pkKiller->GetGuild()) + CDragonLairManager::instance().OnDragonDead(this, pkKiller->GetGuild()->GetID()); + else + sys_err("DragonLair: Dragon killed by nobody"); + } +} + +struct FuncSetLastAttacked +{ + FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime) + { + } + + void operator () (LPCHARACTER ch) + { + ch->SetLastAttacked(m_dwTime); + } + + DWORD m_dwTime; +}; + +void CHARACTER::SetLastAttacked(DWORD dwTime) +{ + assert(m_pkMobInst != NULL); + + m_pkMobInst->m_dwLastAttackedTime = dwTime; + m_pkMobInst->m_posLastAttacked = GetXYZ(); +} + +void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag) +{ + if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this)) + { + TPacketGCDamageInfo damageInfo; + memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo)); + + damageInfo.header = HEADER_GC_DAMAGE_INFO; + damageInfo.dwVID = (DWORD)GetVID(); + damageInfo.flag = DamageFlag; + damageInfo.damage = Damage; + + if (GetDesc() != NULL) + { + GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); + } + + if (pAttacker->GetDesc() != NULL) + { + pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); + } + } +} + +// +// Arguments +// +// Return value +// true : dead +// false : not dead yet +// +bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead +{ +#ifdef ENABLE_NEWSTUFF + if (pAttacker && IsStone() && pAttacker->IsPC()) + { + if (GetEmpire() && GetEmpire() == pAttacker->GetEmpire()) + { + SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK); + return false; + } + } +#endif + + if (DAMAGE_TYPE_MAGIC == type) + dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f); + + if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE) + { + if (IsAffectFlag(AFF_TERROR)) + { + int pct = GetSkillPower(SKILL_TERROR) / 400; + + if (number(1, 100) <= pct) + return false; + } + } + +#ifdef ENABLE_NO_DAMAGE_QUEST_RUNNING + if (pAttacker && pAttacker->IsPC()) + { + auto * pPC = quest::CQuestManager::instance().GetPCForce(pAttacker->GetPlayerID()); + if (pPC->IsRunning()) + { + if (test_server) + { + auto & mgr = quest::CQuestManager::instance(); + sys_err("QUEST There's suspended quest state for %s, can't run new quest state (quest: %s pc: %s)", + pAttacker->GetName(), + pPC->GetCurrentQuestName().c_str(), + mgr.GetCurrentCharacterPtr() ? mgr.GetCurrentCharacterPtr()->GetName() : ""); + } + pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't deal damage while running quests")); + return false; + } + } +#endif + + if (GetRaceNum() == 5001) + { + bool bDropMoney = false; + + int iPercent = 0; // @fixme136 + if (GetMaxHP() >= 0) + iPercent = (GetHP() * 100) / GetMaxHP(); + + if (iPercent <= 10 && GetMaxSP() < 5) + { + SetMaxSP(5); + bDropMoney = true; + } + else if (iPercent <= 20 && GetMaxSP() < 4) + { + SetMaxSP(4); + bDropMoney = true; + } + else if (iPercent <= 40 && GetMaxSP() < 3) + { + SetMaxSP(3); + bDropMoney = true; + } + else if (iPercent <= 60 && GetMaxSP() < 2) + { + SetMaxSP(2); + bDropMoney = true; + } + else if (iPercent <= 80 && GetMaxSP() < 1) + { + SetMaxSP(1); + bDropMoney = true; + } + + if (bDropMoney) + { + DWORD dwGold = 1000; + int iSplitCount = number(10, 13); + + sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP()); + + for (int i = 1; i <= iSplitCount; ++i) + { + PIXEL_POSITION pos; + LPITEM item; + + if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount))) + { + if (i != 0) + { + pos.x = (number(-14, 14) + number(-14, 14)) * 20; + pos.y = (number(-14, 14) + number(-14, 14)) * 20; + + pos.x += GetX(); + pos.y += GetY(); + } + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + } + } + + int iCurHP = GetHP(); + int iCurSP = GetSP(); + + bool IsCritical = false; + bool IsPenetrate = false; + bool IsDeathBlow = false; + + if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC) + { + if (pAttacker) + { + int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); + + if (!IsPC()) + iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); + + if (iCriticalPct) + { + if (iCriticalPct >= 10) + iCriticalPct = 5 + (iCriticalPct - 10) / 4; + else + iCriticalPct /= 2; + + iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); + + if (number(1, 100) <= iCriticalPct) + { + IsCritical = true; + dam *= 2; + EffectPacket(SE_CRITICAL); + + if (IsAffectFlag(AFF_MANASHIELD)) + { + RemoveAffect(AFF_MANASHIELD); + } + } + } + + int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); + + if (!IsPC()) + iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); + + if (iPenetratePct) + { + { + CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); + + if (NULL != pkSk) + { + pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); + + iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); + } + } + + if (iPenetratePct >= 10) + { + iPenetratePct = 5 + (iPenetratePct - 10) / 4; + } + else + { + iPenetratePct /= 2; + } + + iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); + + if (number(1, 100) <= iPenetratePct) + { + IsPenetrate = true; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); + + dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; + + if (IsAffectFlag(AFF_MANASHIELD)) + { + RemoveAffect(AFF_MANASHIELD); + } +#ifdef ENABLE_EFFECT_PENETRATE + EffectPacket(SE_PENETRATE); +#endif + } + } + } + } + + else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE) + { + if (type == DAMAGE_TYPE_NORMAL) + { + if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK)) + { + if (test_server) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); + } + + SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK); + return false; + } + } + else if (type == DAMAGE_TYPE_NORMAL_RANGE) + { + if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE)) + { + if (test_server) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); + } + + SendDamagePacket(pAttacker, 0, DAMAGE_DODGE); + return false; + } + } + + if (IsAffectFlag(AFF_JEONGWIHON)) + dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100); + + if (IsAffectFlag(AFF_TERROR)) + dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100); + + if (IsAffectFlag(AFF_HOSIN)) + dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100; + + if (pAttacker) + { + if (type == DAMAGE_TYPE_NORMAL) + { + if (GetPoint(POINT_REFLECT_MELEE)) + { + int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100; + + if (pAttacker->IsImmune(IMMUNE_REFLECT)) + reflectDamage = int(reflectDamage / 3.0f + 0.5f); + + pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL); + } + } + + int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); + + if (!IsPC()) + iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); + + if (iCriticalPct) + { + iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); + + if (number(1, 100) <= iCriticalPct) + { + IsCritical = true; + dam *= 2; + EffectPacket(SE_CRITICAL); + } + } + + int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); + + if (!IsPC()) + iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); + + { + CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); + + if (NULL != pkSk) + { + pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); + + iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); + } + } + + if (iPenetratePct) + { + iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); + + if (number(1, 100) <= iPenetratePct) + { + IsPenetrate = true; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); + dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; +#ifdef ENABLE_EFFECT_PENETRATE + EffectPacket(SE_PENETRATE); +#endif + } + } + + if (pAttacker->GetPoint(POINT_STEAL_HP)) + { + int pct = 1; + + if (number(1, 10) <= pct) + { + int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100; + + if (iHP > 0 && GetHP() >= iHP) + { + CreateFly(FLY_HP_SMALL, pAttacker); + pAttacker->PointChange(POINT_HP, iHP); + PointChange(POINT_HP, -iHP); + } + } + } + + if (pAttacker->GetPoint(POINT_STEAL_SP)) + { + int pct = 1; + + if (number(1, 10) <= pct) + { + int iCur; + + if (IsPC()) + iCur = iCurSP; + else + iCur = iCurHP; + + int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100; + + if (iSP > 0 && iCur >= iSP) + { + CreateFly(FLY_SP_SMALL, pAttacker); + pAttacker->PointChange(POINT_SP, iSP); + + if (IsPC()) + PointChange(POINT_SP, -iSP); + } + } + } + + if (pAttacker->GetPoint(POINT_STEAL_GOLD)) + { + if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD)) + { + int iAmount = number(1, GetLevel()); + pAttacker->PointChange(POINT_GOLD, iAmount); + DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount); + } + } + + if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) + { + int i = ((iCurHP>=0)?MIN(dam, iCurHP):dam) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; //@fixme107 + + if (i) + { + CreateFly(FLY_HP_SMALL, pAttacker); + pAttacker->PointChange(POINT_HP, i); + } + } + + if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) + { + int i = ((iCurHP>=0)?MIN(dam, iCurHP):dam) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; //@fixme107 + + if (i) + { + CreateFly(FLY_SP_SMALL, pAttacker); + pAttacker->PointChange(POINT_SP, i); + } + } + + if (pAttacker->GetPoint(POINT_MANA_BURN_PCT)) + { + if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT)) + PointChange(POINT_SP, -50); + } + } + } + + switch (type) + { + case DAMAGE_TYPE_NORMAL: + case DAMAGE_TYPE_NORMAL_RANGE: + if (pAttacker) + if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) + dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100; + + dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100; + break; + + case DAMAGE_TYPE_MELEE: + case DAMAGE_TYPE_RANGE: + case DAMAGE_TYPE_FIRE: + case DAMAGE_TYPE_ICE: + case DAMAGE_TYPE_ELEC: + case DAMAGE_TYPE_MAGIC: + if (pAttacker) + if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) + dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100; + + dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100; + break; + + default: + break; + } + + if (IsAffectFlag(AFF_MANASHIELD)) + { + int iDamageSPPart = dam / 3; + int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100; + int iSP = GetSP(); + + if (iDamageToSP <= iSP) + { + PointChange(POINT_SP, -iDamageToSP); + dam -= iDamageSPPart; + } + else + { + PointChange(POINT_SP, -GetSP()); + dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1); + } + } + + if (GetPoint(POINT_MALL_DEFBONUS) > 0) + { + int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100); + dam -= dec_dam; + } + + if (pAttacker) + { + if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0) + { + int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100); + dam += add_dam; + } + + if (pAttacker->IsPC()) + { + int iEmpire = pAttacker->GetEmpire(); + long lMapIndex = pAttacker->GetMapIndex(); + int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); + + if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) + { + dam = dam * 9 / 10; + } + + if (!IsPC() && GetMonsterDrainSPPoint()) + { + int iDrain = GetMonsterDrainSPPoint(); + + if (iDrain <= pAttacker->GetSP()) + pAttacker->PointChange(POINT_SP, -iDrain); + else + { + int iSP = pAttacker->GetSP(); + pAttacker->PointChange(POINT_SP, -iSP); + } + } + + } + else if (pAttacker->IsGuardNPC()) + { + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD); + Stun(); + return true; + } + + if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire())) + { + dam += dam / 10; + } + + if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire())) + { + dam -= dam / 10; + } + } + + if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK)) + return false; + + if (!IsPC()) + { + if (m_pkParty && m_pkParty->GetLeader()) + m_pkParty->GetLeader()->SetLastAttacked(get_dword_time()); + else + SetLastAttacked(get_dword_time()); + + MonsterChat(MONSTER_CHAT_ATTACKED); + } + + if (IsStun()) + { + Dead(pAttacker); + return true; + } + + if (IsDead()) + return true; + + if (type == DAMAGE_TYPE_POISON) + { + if (GetHP() - dam <= 0) + { + dam = GetHP() - 1; + } + } +#ifdef ENABLE_WOLFMAN_CHARACTER + else if (type == DAMAGE_TYPE_BLEEDING) + { + if (GetHP() - dam <= 0) + { + dam = GetHP(); + } + } +#endif + // ------------------------ + + // ----------------------- + if (pAttacker && pAttacker->IsPC()) + { + int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker); + dam = dam * iDmgPct / 100; + } + + if (IsMonster() && IsStoneSkinner()) + { + if (GetHPPct() < GetMobTable().bStoneSkinPoint) + dam /= 2; + } + + if (pAttacker) + { + if (pAttacker->IsMonster() && pAttacker->IsDeathBlower()) + { + if (pAttacker->IsDeathBlow()) + { + if (number(JOB_WARRIOR, JOB_MAX_NUM-1) == GetJob()) // @fixme192 (1, 4) + { + IsDeathBlow = true; + dam = dam * 4; + } + } + } + + dam = BlueDragon_Damage(this, pAttacker, dam); + + BYTE damageFlag = 0; + + if (type == DAMAGE_TYPE_POISON) + damageFlag = DAMAGE_POISON; +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON) + else if (type == DAMAGE_TYPE_BLEEDING) + damageFlag = DAMAGE_BLEEDING; +#elif defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_BLEEDING_AS_POISON) + else if (type == DAMAGE_TYPE_BLEEDING) + damageFlag = DAMAGE_POISON; +#endif + else + damageFlag = DAMAGE_NORMAL; + + if (IsCritical == true) + damageFlag |= DAMAGE_CRITICAL; + + if (IsPenetrate == true) + damageFlag |= DAMAGE_PENETRATE; + + float damMul = this->GetDamMul(); + float tempDam = dam; + dam = tempDam * damMul + 0.5f; + + if (pAttacker) + SendDamagePacket(pAttacker, dam, damageFlag); + + if (test_server) + { + int iTmpPercent = 0; // @fixme136 + if (GetMaxHP() >= 0) + iTmpPercent = (GetHP() * 100) / GetMaxHP(); + + if(pAttacker) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s", + GetName(), + dam, + GetHP(), + iTmpPercent, + IsCritical ? "crit " : "", + IsPenetrate ? "pene " : "", + IsDeathBlow ? "deathblow " : ""); + } + + ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s", + pAttacker ? pAttacker->GetName() : 0, + dam, + GetHP(), + iTmpPercent, + IsCritical ? "crit " : "", + IsPenetrate ? "pene " : "", + IsDeathBlow ? "deathblow " : ""); + } + + if (m_bDetailLog) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d] ġ: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY()); + } + } + + if (!cannot_dead) + { + if (GetHP() - dam <= 0) // @fixme137 + dam = GetHP(); + PointChange(POINT_HP, -dam, false); + } + + if (pAttacker && dam > 0 && IsNPC()) + { + TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); + + if (it == m_map_kDamage.end()) + { + m_map_kDamage.emplace(pAttacker->GetVID(), TBattleInfo(dam, 0)); + it = m_map_kDamage.find(pAttacker->GetVID()); + } + else + { + it->second.iTotalDamage += dam; + } + StartRecoveryEvent(); + UpdateAggrPointEx(pAttacker, type, dam, it->second); + } + + if (GetHP() <= 0) + { + Stun(); + + if (pAttacker && !pAttacker->IsNPC()) + m_dwKillerPID = pAttacker->GetPlayerID(); + else + m_dwKillerPID = 0; + } + + return false; +} + +void CHARACTER::DistributeHP(LPCHARACTER pkKiller) +{ + if (pkKiller->GetDungeon()) + return; +} + +#ifdef ENABLE_NEWEXP_CALCULATION +#define NEW_GET_LVDELTA(me, victim) aiPercentByDeltaLev[MINMAX(0, (victim + 15) - me, MAX_EXP_DELTA_OF_LEV - 1)] +typedef long double rate_t; +static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) +{ + if (test_server && iExp < 0) + { + to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) overflow", iExp); + return; + } + // decrease/increase exp based on player<>mob level + rate_t lvFactor = static_cast(NEW_GET_LVDELTA(to->GetLevel(), from->GetLevel())) / 100.0L; + iExp *= lvFactor; + // start calculating rate exp bonus + int iBaseExp = iExp; + rate_t rateFactor = 100; + if (distribution_test_server) + rateFactor *= 3; + + rateFactor += CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT); + if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL)) + rateFactor += 20; + if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) + rateFactor += 20; + if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + rateFactor += 30; + if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP)) + rateFactor += 50; + + switch (to->GetMountVnum()) + { + case 20110: + case 20111: + case 20112: + case 20113: + if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) || + to->IsEquipUniqueItem(71121) ) + { + rateFactor += 10; + } + break; + + case 20114: + case 20120: + case 20121: + case 20122: + case 20123: + case 20124: + case 20125: + rateFactor += 30; + break; + } + + if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + rateFactor += 50; + if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP)) + rateFactor += 50; + rateFactor += to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS); + rateFactor += to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP); + rateFactor += to->GetPoint(POINT_MALL_EXPBONUS); + rateFactor += to->GetPoint(POINT_EXP); + // useless (never used except for china intoxication) = always 100 + rateFactor = rateFactor * static_cast(CHARACTER_MANAGER::instance().GetMobExpRate(to))/100.0L; + // fix underflow formula + iExp = std::max(0, iExp); + rateFactor = std::max(100.0L, rateFactor); + // apply calculated rate bonus + iExp *= (rateFactor/100.0L); + auto iOldExp = iExp; + // you can get at maximum only 10% of the total required exp at once (so, you need to kill at least 10 mobs to level up) (useless) + iExp = MIN(to->GetNextExp() / 10, iExp); + // it recalculate the given exp if the player level is greater than the exp_table size (useless) + iExp = AdjustExpByLevel(to, iExp); + if (test_server) + to->ChatPacket(CHAT_TYPE_INFO, "base_exp(%d) * rate(%Lf) = exp(%d) => exp+minGNE+adjust(%d)", iBaseExp, rateFactor/100.0L, iOldExp, iExp); + // set + to->PointChange(POINT_EXP, iExp, true); + from->CreateFly(FLY_EXP, to); + // marriage + { + LPCHARACTER you = to->GetMarryPartner(); + if (you) + { + // sometimes, this overflows + DWORD dwUpdatePoint = (2000.0L/to->GetLevel()/to->GetLevel()/3)*iExp; + + if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || + you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0) + dwUpdatePoint *= 3; + + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID()); + + // DIVORCE_NULL_BUG_FIX + if (pMarriage && pMarriage->IsNear()) + pMarriage->Update(dwUpdatePoint); + // END_OF_DIVORCE_NULL_BUG_FIX + } + } +} +#else +static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) +{ + iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp); + + if (distribution_test_server) + iExp *= 3; + + int iBaseExp = iExp; + + iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100; + + { + if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL)) + iExp += iExp * 20 /100; + + if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) + iExp += iExp * 20 / 100; + + if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + iExp += iExp * 30 / 100; + + if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP)) + iExp += iExp * 50 / 100; + + switch (to->GetMountVnum()) + { + case 20110: + case 20111: + case 20112: + case 20113: + if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) || + to->IsEquipUniqueItem(71121) ) + { + iExp += iExp * 10 / 100; + } + break; + + case 20114: + case 20120: + case 20121: + case 20122: + case 20123: + case 20124: + case 20125: + + iExp += iExp * 30 / 100; + break; + } + } + + { + if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + { + iExp += (iExp * 50 / 100); + } + + if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) + { + iExp += (iExp * 50 / 100); + } + + iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; + } + + iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100); + iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100); + iExp += (iExp * to->GetPoint(POINT_EXP)/100); + + if (test_server) + { + sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d", + to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP), + to->GetPoint(POINT_MALL_EXPBONUS), + to->GetPoint(POINT_EXP) + ); + } + + iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100; + + iExp = MIN(to->GetNextExp() / 10, iExp); + + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0) + to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp); + to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) base_exp(%d)", iExp, iBaseExp); + } + + iExp = AdjustExpByLevel(to, iExp); + + to->PointChange(POINT_EXP, iExp, true); + from->CreateFly(FLY_EXP, to); + + { + LPCHARACTER you = to->GetMarryPartner(); + + if (you) + { + DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3; + + if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || + you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0) + dwUpdatePoint = (DWORD)(dwUpdatePoint * 3); + + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID()); + + // DIVORCE_NULL_BUG_FIX + if (pMarriage && pMarriage->IsNear()) + pMarriage->Update(dwUpdatePoint); + // END_OF_DIVORCE_NULL_BUG_FIX + } + } +} +#endif + +namespace NPartyExpDistribute +{ + struct FPartyTotaler + { + int total; + int member_count; + int x, y; + + FPartyTotaler(LPCHARACTER center) + : total(0), member_count(0), x(center->GetX()), y(center->GetY()) + {}; + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + total += __GetPartyExpNP(ch->GetLevel()); + + ++member_count; + } + } + }; + + struct FPartyDistributor + { + int total; + LPCHARACTER c; + int x, y; + DWORD _iExp; + int m_iMode; + int m_iMemberCount; + + FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode) + : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count) + { + if (m_iMemberCount == 0) + m_iMemberCount = 1; + }; + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + DWORD iExp2 = 0; + + switch (m_iMode) + { + case PARTY_EXP_DISTRIBUTION_NON_PARITY: + iExp2 = (DWORD) (_iExp * (float) __GetPartyExpNP(ch->GetLevel()) / total); + break; + + case PARTY_EXP_DISTRIBUTION_PARITY: + iExp2 = _iExp / m_iMemberCount; + break; + + default: + sys_err("Unknown party exp distribution mode %d", m_iMode); + return; + } + + GiveExp(c, ch, iExp2); + } + } + }; +} + +typedef struct SDamageInfo +{ + int iDam; + LPCHARACTER pAttacker; + LPPARTY pParty; + + void Clear() + { + pAttacker = NULL; + pParty = NULL; + } + + inline void Distribute(LPCHARACTER ch, int iExp) + { + if (pAttacker) + GiveExp(ch, pAttacker, iExp); + else if (pParty) + { + NPartyExpDistribute::FPartyTotaler f(ch); + pParty->ForEachOnlineMember(f); + + if (pParty->IsPositionNearLeader(ch)) + iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100; + + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent()) + pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent()); + } + + if (pParty->GetExpCentralizeCharacter()) + { + LPCHARACTER tch = pParty->GetExpCentralizeCharacter(); + + if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE) + { + int iExpCenteralize = (int) (iExp * 0.05f); + iExp -= iExpCenteralize; + + GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize); + } + } + + NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode()); + pParty->ForEachOnlineMember(fDist); + } + } +} TDamageInfo; + +#ifdef ENABLE_KILL_EVENT_FIX +LPCHARACTER CHARACTER::GetMostAttacked() { + int iMostDam=-1; + LPCHARACTER pkChrMostAttacked = NULL; + auto it = m_map_kDamage.begin(); + + while (it != m_map_kDamage.end()){ + //* getting information from the iterator + const VID & c_VID = it->first; + const int iDam = it->second.iTotalDamage; + + //* increasing the iterator + ++it; + + //* finding the character from his vid + LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); + + //* if the attacked is now offline + if (!pAttacker) + continue; + + //* if the attacker is not a player + if( pAttacker->IsNPC()) + continue; + + //* if the player is too far + if(DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000) + continue; + + if (iDam > iMostDam){ + pkChrMostAttacked = pAttacker; + iMostDam = iDam; + } + } + + return pkChrMostAttacked; +} +#endif + +LPCHARACTER CHARACTER::DistributeExp() +{ + int iExpToDistribute = GetExp(); + + if (iExpToDistribute <= 0) + return NULL; + + int iTotalDam = 0; + LPCHARACTER pkChrMostAttacked = NULL; + int iMostDam = 0; + + typedef std::vector TDamageInfoTable; + TDamageInfoTable damage_info_table; + std::map map_party_damage; + + damage_info_table.reserve(m_map_kDamage.size()); + + TDamageMap::iterator it = m_map_kDamage.begin(); + + while (it != m_map_kDamage.end()) + { + const VID & c_VID = it->first; + int iDam = it->second.iTotalDamage; + + ++it; + + LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); + + if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000) + continue; + + iTotalDam += iDam; + if (!pkChrMostAttacked || iDam > iMostDam) + { + pkChrMostAttacked = pAttacker; + iMostDam = iDam; + } + + if (pAttacker->GetParty()) + { + std::map::iterator it = map_party_damage.find(pAttacker->GetParty()); + if (it == map_party_damage.end()) + { + TDamageInfo di; + di.iDam = iDam; + di.pAttacker = NULL; + di.pParty = pAttacker->GetParty(); + map_party_damage.emplace(di.pParty, di); + } + else + { + it->second.iDam += iDam; + } + } + else + { + TDamageInfo di; + + di.iDam = iDam; + di.pAttacker = pAttacker; + di.pParty = NULL; + + //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam); + //pq_damage.push(di); + damage_info_table.emplace_back(di); + } + } + + for (std::map::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it) + { + damage_info_table.emplace_back(it->second); + //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam); + } + + SetExp(0); + //m_map_kDamage.clear(); + + if (iTotalDam == 0) + return NULL; + + if (m_pkChrStone) + { + //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1); + int iExp = iExpToDistribute >> 1; + m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp); + iExpToDistribute -= iExp; + } + + sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d", + GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam); + //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d", + //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam); + + if (damage_info_table.empty()) + return NULL; + + DistributeHP(pkChrMostAttacked); + + { + TDamageInfoTable::iterator di = damage_info_table.begin(); + { + TDamageInfoTable::iterator it; + + for (it = damage_info_table.begin(); it != damage_info_table.end();++it) + { + if (it->iDam > di->iDam) + di = it; + } + } + + int iExp = iExpToDistribute / 5; + iExpToDistribute -= iExp; + + float fPercent = (float) di->iDam / iTotalDam; + + if (fPercent > 1.0f) + { + sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName()); + fPercent = 1.0f; + } + + iExp += (int) (iExpToDistribute * fPercent); + + //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam); + + di->Distribute(this, iExp); + + if (fPercent == 1.0f) + return pkChrMostAttacked; + + di->Clear(); + } + + { + TDamageInfoTable::iterator it; + + for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it) + { + TDamageInfo & di = *it; + + float fPercent = (float) di.iDam / iTotalDam; + + if (fPercent > 1.0f) + { + sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName()); + fPercent = 1.0f; + } + + //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam); + di.Distribute(this, (int) (iExpToDistribute * fPercent)); + } + } + + return pkChrMostAttacked; +} + +int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */) +{ + LPITEM pkBow; + + if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW) + { + return 0; + } + + LPITEM pkArrow; + + if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON || + (pkArrow->GetProto()->bSubType != WEAPON_ARROW + #ifdef ENABLE_QUIVER_SYSTEM + && pkArrow->GetSubType() != WEAPON_QUIVER + #endif + ) + ) + { + return 0; + } + + iArrowCount = MIN(iArrowCount, pkArrow->GetCount()); +#ifdef ENABLE_QUIVER_SYSTEM + if (pkArrow->GetSubType() == WEAPON_QUIVER && pkArrow->GetRealUseLimit()) // no arrows if expired - 1 otherwise + iArrowCount = ((pkArrow->GetSocket(0) - time(0)) <= 0) ? 0 : 1; +#endif + + *ppkBow = pkBow; + *ppkArrow = pkArrow; + + return iArrowCount; +} + +void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount) +{ +#ifdef ENABLE_QUIVER_SYSTEM + if (pkArrow->GetSubType() == WEAPON_QUIVER) + return; +#endif + + int iCount = pkArrow->GetCount(); + DWORD dwVnum = pkArrow->GetVnum(); + iCount = iCount - MIN(iCount, dwArrowCount); + pkArrow->SetCount(iCount); + + if (iCount == 0) + { + LPITEM pkNewArrow = FindSpecifyItem(dwVnum); + + sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow)); + + if (pkNewArrow) + EquipItem(pkNewArrow); + } +} + +class CFuncShoot +{ + public: + LPCHARACTER m_me; + BYTE m_bType; + bool m_bSucceed; + + CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(FALSE) + { + } + + void operator () (DWORD dwTargetVID) + { + if (m_bType > 1) + { + if (g_bSkillDisable) + return; + + m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID); + } + + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); + + if (!pkVictim) + return; + + if (!battle_is_attackable(m_me, pkVictim)) + return; + + if (m_me->IsNPC()) + { + if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000) + return; + } + + #ifdef ENABLE_SKILL_COOLDOWN_CHECK + if (m_me->IsPC() && m_bType > 0 && m_me->SkillIsOnCooldown(m_bType)) + return; + #endif + + LPITEM pkBow, pkArrow; + + switch (m_bType) + { + case 0: + { + int iDam = 0; + + if (m_me->IsPC()) + { + if (m_me->GetJob() != JOB_ASSASSIN) + return; + + if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow)) + return; + + if (m_me->GetSkillGroup() != 0) + if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2) + { + if (m_me->GetSP() < 5) + return; + + m_me->PointChange(POINT_SP, -5); + } + + iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow); + m_me->UseArrow(pkArrow, 1); + + // check speed hack + DWORD dwCurrentTime = get_dword_time(); + if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime)) + iDam = 0; + } + else + iDam = CalcMeleeDamage(m_me, pkVictim); + + NormalAttackAffect(m_me, pkVictim); + + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100; + + //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); + + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE); + + } + break; + + case 1: + { + int iDam; + + if (m_me->IsPC()) + return; + + iDam = CalcMagicDamage(m_me, pkVictim); + + NormalAttackAffect(m_me, pkVictim); + +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + const int resist_magic = MINMAX(0, pkVictim->GetPoint(POINT_RESIST_MAGIC), 100); + const int resist_magic_reduction = MINMAX(0, (m_me->GetJob()==JOB_SURA) ? m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION)/2 : m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION), 50); + const int total_res_magic = MINMAX(0, resist_magic - resist_magic_reduction, 100); + iDam = iDam * (100 - total_res_magic) / 100; +#else + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100; +#endif + + //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); + + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC); + + } + break; + + case SKILL_YEONSA: + { + int iUseArrow = 1; + { + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + + if (pkVictim->IsDead()) + break; + + } + else + break; + } + } + break; + + case SKILL_KWANKYEOK: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + break; + + case SKILL_GIGUNG: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + case SKILL_HWAJO: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + + case SKILL_HORSE_WILDATTACK_RANGE: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + + case SKILL_PAERYONG: + case SKILL_MARYUNG: + case SKILL_TUSOK: + case SKILL_BIPABU: + case SKILL_NOEJEON: + case SKILL_GEOMPUNG: + case SKILL_SANGONG: + case SKILL_MAHWAN: + case SKILL_PABEOB: + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + } + break; + + case SKILL_CHAIN: + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + + } + break; + + case SKILL_YONGBI: + { + m_me->OnMove(true); + } + break; + + default: + sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType); + break; + } + + m_bSucceed = TRUE; + } +}; + +bool CHARACTER::Shoot(BYTE bType) +{ + sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size()); + + if (!CanMove()) + { + return false; + } + + CFuncShoot f(this, bType); + + if (m_dwFlyTargetID != 0) + { + f(m_dwFlyTargetID); + m_dwFlyTargetID = 0; + } + + f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f); + m_vec_dwFlyTargets.clear(); + + return f.m_bSucceed; +} + +void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader) +{ + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); + TPacketGCFlyTargeting pack; + + //pack.bHeader = HEADER_GC_FLY_TARGETING; + pack.bHeader = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING; + pack.dwShooterVID = GetVID(); + + if (pkVictim) + { + pack.dwTargetVID = pkVictim->GetVID(); + pack.x = pkVictim->GetX(); + pack.y = pkVictim->GetY(); + + if (bHeader == HEADER_CG_FLY_TARGETING) + m_dwFlyTargetID = dwTargetVID; + else + m_vec_dwFlyTargets.emplace_back(dwTargetVID); + } + else + { + pack.dwTargetVID = 0; + pack.x = x; + pack.y = y; + } + + sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y); + PacketAround(&pack, sizeof(pack), this); +} + +LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr) +{ + if (NULL == pkChr) + pkChr = this; + + float fMinDist = 99999.0f; + LPCHARACTER pkVictim = NULL; + + TDamageMap::iterator it = m_map_kDamage.begin(); + + while (it != m_map_kDamage.end()) + { + const VID & c_VID = it->first; + ++it; + + LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); + + if (!pAttacker) + continue; + + if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || + pAttacker->IsAffectFlag(AFF_INVISIBILITY) || + pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE)) + continue; + + float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY()); + + if (fDist < fMinDist) + { + pkVictim = pAttacker; + fMinDist = fDist; + } + } + + return pkVictim; +} + +void CHARACTER::SetVictim(LPCHARACTER pkVictim) +{ + if (!pkVictim) + { + if (0 != (DWORD)m_kVIDVictim) + MonsterLog(" "); + + m_kVIDVictim.Reset(); + battle_end(this); + } + else + { + if (m_kVIDVictim != pkVictim->GetVID()) + MonsterLog(" : %s", pkVictim->GetName()); + + m_kVIDVictim = pkVictim->GetVID(); + m_dwLastVictimSetTime = get_dword_time(); + } +} + +LPCHARACTER CHARACTER::GetVictim() const +{ + return CHARACTER_MANAGER::instance().Find(m_kVIDVictim); +} + +LPCHARACTER CHARACTER::GetProtege() const +{ + if (m_pkChrStone) + return m_pkChrStone; + + if (m_pkParty) + return m_pkParty->GetLeader(); + + return NULL; +} + +int CHARACTER::GetAlignment() const +{ + return m_iAlignment; +} + +int CHARACTER::GetRealAlignment() const +{ + return m_iRealAlignment; +} + +void CHARACTER::ShowAlignment(bool bShow) +{ + if (bShow) + { + if (m_iAlignment != m_iRealAlignment) + { + m_iAlignment = m_iRealAlignment; + UpdatePacket(); + } + } + else + { + if (m_iAlignment != 0) + { + m_iAlignment = 0; + UpdatePacket(); + } + } +} + +void CHARACTER::UpdateAlignment(int iAmount) +{ + bool bShow = false; + + if (m_iAlignment == m_iRealAlignment) + bShow = true; + + int i = m_iAlignment / 10; + + m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000); + + if (bShow) + { + m_iAlignment = m_iRealAlignment; + + if (i != m_iAlignment / 10) + UpdatePacket(); + } +} + +void CHARACTER::SetKillerMode(bool isOn) +{ + if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER)) + return; + + if (isOn) + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); + else + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); + + m_iKillerModePulse = thecore_pulse(); + UpdatePacket(); + sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID()); +} + +bool CHARACTER::IsKillerMode() const +{ + return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); +} + +void CHARACTER::UpdateKillerMode() +{ + if (!IsKillerMode()) + return; + + if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(30)) + SetKillerMode(false); +} + +void CHARACTER::SetPKMode(BYTE bPKMode) +{ + if (bPKMode >= PK_MODE_MAX_NUM) + return; + + if (m_bPKMode == bPKMode) + return; + + if (bPKMode == PK_MODE_GUILD && !GetGuild()) + bPKMode = PK_MODE_FREE; + + m_bPKMode = bPKMode; + UpdatePacket(); + + sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode); +} + +BYTE CHARACTER::GetPKMode() const +{ + return m_bPKMode; +} + +struct FuncForgetMyAttacker +{ + LPCHARACTER m_ch; + FuncForgetMyAttacker(LPCHARACTER ch) + { + m_ch = ch; + } + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (ch->m_kVIDVictim == m_ch->GetVID()) + ch->SetVictim(NULL); + } + } +}; + +struct FuncAggregateMonster +{ + LPCHARACTER m_ch; + FuncAggregateMonster(LPCHARACTER ch) + { + m_ch = ch; + } + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + if (ch->GetVictim()) + return; + + if (number(1, 100) <= 50) + if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000) + if (ch->CanBeginFight()) + ch->BeginFight(m_ch); + } + } +}; + +struct FuncAttractRanger +{ + LPCHARACTER m_ch; + FuncAttractRanger(LPCHARACTER ch) + { + m_ch = ch; + } + + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + if (ch->GetVictim() && ch->GetVictim() != m_ch) + return; + if (ch->GetMobAttackRange() > 150) + { + int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2); + if (iNewRange < 150) + iNewRange = 150; + + ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false); + } + } + } +}; + +struct FuncPullMonster +{ + LPCHARACTER m_ch; + int m_iLength; + FuncPullMonster(LPCHARACTER ch, int iLength = 300) + { + m_ch = ch; + m_iLength = iLength; + } + + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + //if (ch->GetVictim() && ch->GetVictim() != m_ch) + //return; + float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY()); + if (fDist > 3000 || fDist < 100) + return; + + float fNewDist = fDist - m_iLength; + if (fNewDist < 100) + fNewDist = 100; + + float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY()); + float fx; + float fy; + + GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy); + long tx = (long)(ch->GetX() + fx); + long ty = (long)(ch->GetY() + fy); + + ch->Sync(tx, ty); + ch->Goto(tx, ty); + ch->CalculateMoveDuration(); + + ch->SyncPacket(); + } + } +}; + +void CHARACTER::ForgetMyAttacker() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncForgetMyAttacker f(this); + pSec->ForEachAround(f); + } + ReviveInvisible(5); +} + +void CHARACTER::AggregateMonster() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncAggregateMonster f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::AttractRanger() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncAttractRanger f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::PullMonster() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncPullMonster f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info) +{ + switch (type) + { + case DAMAGE_TYPE_NORMAL_RANGE: + dam = (int) (dam*1.2f); + break; + + case DAMAGE_TYPE_RANGE: + dam = (int) (dam*1.5f); + break; + + case DAMAGE_TYPE_MAGIC: + dam = (int) (dam*1.2f); + break; + + default: + break; + } + + if (pAttacker == GetVictim()) + dam = (int) (dam * 1.2f); + + info.iAggro += dam; + + if (info.iAggro < 0) + info.iAggro = 0; + + //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total); + if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL) + { + LPPARTY pParty = GetParty(); + + int iPartyAggroDist = dam; + + if (pParty->GetLeaderPID() == GetVID()) + iPartyAggroDist /= 2; + else + iPartyAggroDist /= 3; + + pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID()); + } + + ChangeVictimByAggro(info.iAggro, pAttacker); +} + +void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam) +{ + if (IsDead() || IsStun()) + return; + + TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); + + if (it == m_map_kDamage.end()) + { + m_map_kDamage.emplace(pAttacker->GetVID(), TBattleInfo(0, dam)); + it = m_map_kDamage.find(pAttacker->GetVID()); + } + + UpdateAggrPointEx(pAttacker, type, dam, it->second); +} + +void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim) +{ + if (get_dword_time() - m_dwLastVictimSetTime < 3000) + return; + + if (pNewVictim == GetVictim()) + { + if (m_iMaxAggro < iNewAggro) + { + m_iMaxAggro = iNewAggro; + return; + } + + TDamageMap::iterator it; + TDamageMap::iterator itFind = m_map_kDamage.end(); + + for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) + { + if (it->second.iAggro > iNewAggro) + { + LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); + + if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000) + { + itFind = it; + iNewAggro = it->second.iAggro; + } + } + } + + if (itFind != m_map_kDamage.end()) + { + m_iMaxAggro = iNewAggro; + SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first)); + m_dwStateDuration = 1; + } + } + else + { + if (m_iMaxAggro < iNewAggro) + { + m_iMaxAggro = iNewAggro; + SetVictim(pNewVictim); + m_dwStateDuration = 1; + } + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_change_empire.cpp b/source-server/Srcs/Server/game/src/char_change_empire.cpp new file mode 100644 index 000000000..b91d6077c --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_change_empire.cpp @@ -0,0 +1,182 @@ +#include "stdafx.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "db.h" +#include "guild_manager.h" +#include "marriage.h" +#include "../../common/CommonDefines.h" + +int CHARACTER::ChangeEmpire(BYTE empire) +{ + if (GetEmpire() == empire) + return 1; + + char szQuery[1024+1]; + DWORD dwAID; + DWORD dwPID[PLAYER_PER_ACCOUNT]; + memset(dwPID, 0, sizeof(dwPID)); + + { + snprintf(szQuery, sizeof(szQuery), + "SELECT id, pid1, pid2, pid3, pid4" + #ifdef ENABLE_PLAYER_PER_ACCOUNT5 + ", pid5" + #endif + " FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u OR pid5=%u AND empire=%u", + get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + auto msg(DBManager::instance().DirectQuery(szQuery)); + if (msg->Get()->uiNumRows == 0) + { + return 0; + } + + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + str_to_number(dwAID, row[0]); + str_to_number(dwPID[0], row[1]); + str_to_number(dwPID[1], row[2]); + str_to_number(dwPID[2], row[3]); + str_to_number(dwPID[3], row[4]); + #ifdef ENABLE_PLAYER_PER_ACCOUNT5 + str_to_number(dwPID[4], row[5]); + #endif + } + + const int loop = PLAYER_PER_ACCOUNT; + + { + DWORD dwGuildID[PLAYER_PER_ACCOUNT]; + CGuild * pGuild[PLAYER_PER_ACCOUNT]; + std::unique_ptr pMsg; + + for (int i = 0; i < loop; ++i) + { + snprintf(szQuery, sizeof(szQuery), "SELECT guild_id FROM guild_member%s WHERE pid=%u", get_table_postfix(), dwPID[i]); + + pMsg = DBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + str_to_number(dwGuildID[i], row[0]); + + pGuild[i] = CGuildManager::instance().FindGuild(dwGuildID[i]); + + if (pGuild[i] != NULL) + return 2; + } + else + { + dwGuildID[i] = 0; + pGuild[i] = NULL; + } + } + } + } + + { + for (int i = 0; i < loop; ++i) + { + if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true) + return 3; + } + } + + { + snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u" + #ifdef ENABLE_PLAYER_PER_ACCOUNT5 + " OR pid5=%u" + #endif + " AND empire=%u", + get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + + auto msg(DBManager::instance().DirectQuery(szQuery)); + if (msg->Get()->uiAffectedRows > 0) + { + SetChangeEmpireCount(); + + return 999; + } + } + + return 0; +} + +int CHARACTER::GetChangeEmpireCount() const +{ + char szQuery[1024+1]; + DWORD dwAID = GetAID(); + + if (dwAID == 0) + return 0; + + snprintf(szQuery, sizeof(szQuery), "SELECT change_count FROM change_empire WHERE account_id = %u", dwAID); + + auto pMsg = DBManager::instance().DirectQuery(szQuery); + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows == 0) + return 0; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + DWORD count = 0; + str_to_number(count, row[0]); + return count; + } + + return 0; +} + +void CHARACTER::SetChangeEmpireCount() +{ + char szQuery[1024+1]; + + DWORD dwAID = GetAID(); + + if (dwAID == 0) return; + + int count = GetChangeEmpireCount(); + + if (count == 0) + { + count++; + snprintf(szQuery, sizeof(szQuery), "INSERT INTO change_empire VALUES(%u, %d, NOW())", dwAID, count); + } + else + { + count++; + snprintf(szQuery, sizeof(szQuery), "UPDATE change_empire SET change_count=%d WHERE account_id=%u", count, dwAID); + } + + auto pmsg(DBManager::instance().DirectQuery(szQuery)); +} + +DWORD CHARACTER::GetAID() const +{ + char szQuery[1024+1]; + DWORD dwAID = 0; + snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u" + #ifdef ENABLE_PLAYER_PER_ACCOUNT5 + " OR pid5=%u" + #endif + " AND empire=%u", + get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + + auto pMsg = DBManager::instance().DirectQuery(szQuery); + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows == 0) + return 0; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + str_to_number(dwAID, row[0]); + return dwAID; + } + return 0; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_dragonsoul.cpp b/source-server/Srcs/Server/game/src/char_dragonsoul.cpp new file mode 100644 index 000000000..7b6a41181 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_dragonsoul.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "char.h" +#include "item.h" +#include "desc.h" +#include "DragonSoul.h" +#include "log.h" + +void CHARACTER::DragonSoul_Initialize() +{ + for (int i = INVENTORY_MAX_NUM + WEAR_MAX_NUM; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + LPITEM pItem = GetItem(TItemPos(INVENTORY, i)); + if (NULL != pItem) + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 0); + } + + if (FindAffect(AFFECT_DRAGON_SOUL_DECK_0)) + { + DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_0); + } + else if (FindAffect(AFFECT_DRAGON_SOUL_DECK_1)) + { + DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_1); + } +} + +int CHARACTER::DragonSoul_GetActiveDeck() const +{ + return m_pointsInstant.iDragonSoulActiveDeck; +} + +bool CHARACTER::DragonSoul_IsDeckActivated() const +{ + return m_pointsInstant.iDragonSoulActiveDeck >= 0; +} + +bool CHARACTER::DragonSoul_IsQualified() const +{ + #ifdef ENABLE_NO_DSS_QUALIFICATION + return true; + #else + return FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED) != NULL; + #endif +} + +void CHARACTER::DragonSoul_GiveQualification() +{ + if(NULL == FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED)) + { + LogManager::instance().CharLog(this, 0, "DS_QUALIFIED", ""); + } + AddAffect(AFFECT_DRAGON_SOUL_QUALIFIED, APPLY_NONE, 0, AFF_NONE, INFINITE_AFFECT_DURATION, 0, false, false); + //SetQuestFlag("dragon_soul.is_qualified", 1); + //PointChange(POINT_DRAGON_SOUL_IS_QUALIFIED, 1 - GetPoint(POINT_DRAGON_SOUL_IS_QUALIFIED)); +} + +bool CHARACTER::DragonSoul_ActivateDeck(int deck_idx) +{ + if (deck_idx < DRAGON_SOUL_DECK_0 || deck_idx >= DRAGON_SOUL_DECK_MAX_NUM) + { + return false; + } + + if (DragonSoul_GetActiveDeck() == deck_idx) + return true; + + DragonSoul_DeactivateAll(); + + if (!DragonSoul_IsQualified()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ڰ Ȱȭ ʾҽϴ.")); + return false; + } + + AddAffect(AFFECT_DRAGON_SOUL_DECK_0 + deck_idx, APPLY_NONE, 0, 0, INFINITE_AFFECT_DURATION, 0, false); + + m_pointsInstant.iDragonSoulActiveDeck = deck_idx; + + for (int i = DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * deck_idx; + i < DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * (deck_idx + 1); i++) + { + LPITEM pItem = GetInventoryItem(i); + if (NULL != pItem) + DSManager::instance().ActivateDragonSoul(pItem); + } + return true; +} + +void CHARACTER::DragonSoul_DeactivateAll() +{ + for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true); + } + m_pointsInstant.iDragonSoulActiveDeck = -1; + RemoveAffect(AFFECT_DRAGON_SOUL_DECK_0); + RemoveAffect(AFFECT_DRAGON_SOUL_DECK_1); +} + +void CHARACTER::DragonSoul_CleanUp() +{ + for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true); + } +} + +bool CHARACTER::DragonSoul_RefineWindow_Open(LPENTITY pEntity) +{ + if (NULL == m_pointsInstant.m_pDragonSoulRefineWindowOpener) + { + m_pointsInstant.m_pDragonSoulRefineWindowOpener = pEntity; + } + + TPacketGCDragonSoulRefine PDS; + PDS.header = HEADER_GC_DRAGON_SOUL_REFINE; + PDS.bSubType = DS_SUB_HEADER_OPEN; + LPDESC d = GetDesc(); + + if (NULL == d) + { + sys_err ("User(%s)'s DESC is NULL POINT.", GetName()); + return false; + } + + d->Packet(&PDS, sizeof(PDS)); + return true; +} + +bool CHARACTER::DragonSoul_RefineWindow_Close() +{ + m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL; + return true; +} + +bool CHARACTER::DragonSoul_RefineWindow_CanRefine() +{ + return NULL != m_pointsInstant.m_pDragonSoulRefineWindowOpener; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_horse.cpp b/source-server/Srcs/Server/game/src/char_horse.cpp new file mode 100644 index 000000000..80ca25af4 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_horse.cpp @@ -0,0 +1,377 @@ +#include "stdafx.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "packet.h" +#include "guild.h" +#include "vector.h" +#include "questmanager.h" +#include "item.h" +#include "horsename_manager.h" +#include "locale_service.h" +#include "arena.h" + +#include "../../common/VnumHelper.h" + +bool CHARACTER::StartRiding() +{ +#ifdef ENABLE_NEWSTUFF + if (g_NoMountAtGuildWar && GetWarMap()) + { + RemoveAffect(AFFECT_MOUNT); + RemoveAffect(AFFECT_MOUNT_BONUS); + if (IsRiding()) + StopRiding(); + return false; + } +#endif + if (IsDead() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + LPITEM armor = GetWear(WEAR_BODY); + + if (armor && (armor->GetVnum() >= 11901 && armor->GetVnum() <= 11904)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + // @warme005 + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + return false; + + DWORD dwMountVnum = m_chHorse ? m_chHorse->GetRaceNum() : GetMyHorseVnum(); + + if (false == CHorseRider::StartRiding()) + { + if (GetHorseLevel() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϰ ʽϴ.")); + else if (GetHorseHealth() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׾ִ Դϴ.")); + else if (GetHorseStamina() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׹̳ʰ Ͽ Ż ϴ.")); + + return false; + } + + HorseSummon(false); + + MountVnum(dwMountVnum); + + if(test_server) + sys_log(0, "Ride Horse : %s ", GetName()); + + return true; +} + +bool CHARACTER::StopRiding() +{ + if (CHorseRider::StopRiding()) + { + quest::CQuestManager::instance().Unmount(GetPlayerID()); + + if (!IsDead() && !IsStun()) + { + DWORD dwOldVnum = GetMountVnum(); + MountVnum(0); + + HorseSummon(true, false, dwOldVnum); + } + else + { + m_dwMountVnum = 0; + ComputePoints(); + UpdatePacket(); + } + + PointChange(POINT_ST, 0); + PointChange(POINT_DX, 0); + PointChange(POINT_HT, 0); + PointChange(POINT_IQ, 0); + + return true; + } + + return false; +} + +EVENTFUNC(horse_dead_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "horse_dead_event> Null pointer" ); + return 0; + } + + // + LPCHARACTER ch = info->ch; + if (ch == NULL) { + return 0; + } + ch->HorseSummon(false); + return 0; +} + +void CHARACTER::SetRider(LPCHARACTER ch) +{ + if (m_chRider) + m_chRider->ClearHorseInfo(); + + m_chRider = ch; + + if (m_chRider) + m_chRider->SendHorseInfo(); +} + +LPCHARACTER CHARACTER::GetRider() const +{ + return m_chRider; +} + +void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const char* pPetName) +{ + if ( bSummon ) + { + if( m_chHorse != NULL ) + return; + + if (GetHorseLevel() <= 0) + return; + + if (IsRiding()) + return; + + #ifdef ENABLE_MOUNT_COSTUME_EX_SYSTEM + if (GetMountVnum()) + return; + #endif + + sys_log(0, "HorseSummon : %s lv:%d bSummon:%d fromFar:%d", GetName(), GetLevel(), bSummon, bFromFar); + + long x = GetX(); + long y = GetY(); + + if (GetHorseHealth() <= 0) + bFromFar = false; + + if (bFromFar) + { + x += (number(0, 1) * 2 - 1) * number(2000, 2500); + y += (number(0, 1) * 2 - 1) * number(2000, 2500); + } + else + { + x += number(-100, 100); + y += number(-100, 100); + } + + m_chHorse = CHARACTER_MANAGER::instance().SpawnMob( + (0 == dwVnum) ? GetMyHorseVnum() : dwVnum, + GetMapIndex(), + x, y, + GetZ(), false, (int)(GetRotation()+180), false); + + if (!m_chHorse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯ Ͽϴ.")); + return; + } + + if (GetHorseHealth() <= 0) + { + m_chHorse->SetPosition(POS_DEAD); + + char_event_info* info = AllocEventInfo(); + info->ch = this; + m_chHorse->m_pkDeadEvent = event_create(horse_dead_event, info, PASSES_PER_SEC(60)); + } + + m_chHorse->SetLevel(GetHorseLevel()); + + const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID()); + + if ( pHorseName != NULL && strlen(pHorseName) != 0 ) + { + m_chHorse->m_stName = pHorseName; + } + else + { + m_chHorse->m_stName = GetName(); + m_chHorse->m_stName += LC_TEXT(" "); + } + + if (!m_chHorse->Show(GetMapIndex(), x, y, GetZ())) + { + M2_DESTROY_CHARACTER(m_chHorse); + sys_err("cannot show monster"); + m_chHorse = NULL; + return; + } + + if ((GetHorseHealth() <= 0)) + { + TPacketGCDead pack; + pack.header = HEADER_GC_DEAD; + pack.vid = m_chHorse->GetVID(); + PacketAround(&pack, sizeof(pack)); + } + + m_chHorse->SetRider(this); + } + else + { + if (!m_chHorse) + return; + + LPCHARACTER chHorse = m_chHorse; + + chHorse->SetRider(NULL); // m_chHorse assign to NULL + + if ((GetHorseHealth() <= 0)) + bFromFar = false; + + if (!bFromFar) + { + M2_DESTROY_CHARACTER(chHorse); + } + else + { + chHorse->SetNowWalking(false); + float fx, fy; + chHorse->SetRotation(GetDegreeFromPositionXY(chHorse->GetX(), chHorse->GetY(), GetX(), GetY())+180); + GetDeltaByDegree(chHorse->GetRotation(), 3500, &fx, &fy); + chHorse->Goto((long)(chHorse->GetX()+fx), (long) (chHorse->GetY()+fy)); + chHorse->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + + m_chHorse = NULL; + + } +} + +DWORD CHARACTER::GetMyHorseVnum() const +{ + int delta = 0; + + if (GetGuild()) + { + ++delta; + + if (GetGuild()->GetMasterPID() == GetPlayerID()) + ++delta; + } + + return c_aHorseStat[GetHorseLevel()].iNPCRace + delta; +} + +void CHARACTER::HorseDie() +{ + CHorseRider::HorseDie(); + HorseSummon(false); +} + +bool CHARACTER::ReviveHorse() +{ + if (CHorseRider::ReviveHorse()) + { + HorseSummon(false); + HorseSummon(true); + return true; + } + return false; +} + +void CHARACTER::ClearHorseInfo() +{ + if (!IsHorseRiding()) + { + ChatPacket(CHAT_TYPE_COMMAND, "hide_horse_state"); + + m_bSendHorseLevel = 0; + m_bSendHorseHealthGrade = 0; + m_bSendHorseStaminaGrade = 0; + } + + m_chHorse = NULL; +} + +void CHARACTER::SendHorseInfo() +{ + if (m_chHorse || IsHorseRiding()) + { + int iHealthGrade; + int iStaminaGrade; + + if (GetHorseHealth() == 0) + iHealthGrade = 0; + else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 3) + iHealthGrade = 1; + else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 7) + iHealthGrade = 2; + else + iHealthGrade = 3; + + if (GetHorseStamina() * 10 <= GetHorseMaxStamina()) + iStaminaGrade = 0; + else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 3) + iStaminaGrade = 1; + else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 7) + iStaminaGrade = 2; + else + iStaminaGrade = 3; + + if (m_bSendHorseLevel != GetHorseLevel() || + m_bSendHorseHealthGrade != iHealthGrade || + m_bSendHorseStaminaGrade != iStaminaGrade) + { + ChatPacket(CHAT_TYPE_COMMAND, "horse_state %d %d %d", GetHorseLevel(), iHealthGrade, iStaminaGrade); + + m_bSendHorseLevel = GetHorseLevel(); + m_bSendHorseHealthGrade = iHealthGrade; + m_bSendHorseStaminaGrade = iStaminaGrade; + } + } +} + +bool CHARACTER::CanUseHorseSkill() +{ + if(IsRiding()) + { + if (GetHorseGrade() == 3) + return true; + else + return false; + + if(GetMountVnum()) + { + if (GetMountVnum() >= 20209 && GetMountVnum() <= 20212) + return true; + + if (CMobVnumHelper::IsRamadanBlackHorse(GetMountVnum())) + return true; + } + else + return false; + + } + + return false; +} + +void CHARACTER::SetHorseLevel(int iLevel) +{ + CHorseRider::SetHorseLevel(iLevel); + SetSkillLevel(SKILL_HORSE, GetHorseLevel()); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_item.cpp b/source-server/Srcs/Server/game/src/char_item.cpp new file mode 100644 index 000000000..8fcf9043d --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_item.cpp @@ -0,0 +1,7784 @@ +#include "stdafx.h" + +#include + +#include "utils.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "item_manager.h" +#include "desc.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "packet.h" +#include "affect.h" +#include "skill.h" +#include "start_position.h" +#include "mob_manager.h" +#include "db.h" +#include "log.h" +#include "vector.h" +#include "buffer_manager.h" +#include "questmanager.h" +#include "fishing.h" +#include "party.h" +#include "dungeon.h" +#include "refine.h" +#include "unique_item.h" +#include "war_map.h" +#include "xmas_event.h" +#include "marriage.h" +#include "monarch.h" +#include "polymorph.h" +#include "blend_item.h" +#include "castle.h" +#include "arena.h" +#include "threeway_war.h" + +#include "safebox.h" +#include "shop.h" + +#ifdef ENABLE_NEWSTUFF +#include "pvp.h" +#include "../../common/PulseManager.h" +#endif + +#include "../../common/VnumHelper.h" +#include "DragonSoul.h" +#include "buff_on_attributes.h" +#include "belt_inventory_helper.h" +#include "../../common/CommonDefines.h" +#include "PetSystem.h" + +#define ENABLE_EFFECT_EXTRAPOT +#define ENABLE_BOOKS_STACKFIX +#define ENABLE_ITEM_RARE_ATTR_LEVEL_PCT +#define ENABLE_UNIQUE_ITEM_AUTOSPLIT + +enum {ITEM_BROKEN_METIN_VNUM = 28960}; + +// CHANGE_ITEM_ATTRIBUTES +const char CHARACTER::msc_szLastChangeItemAttrFlag[] = "Item.LastChangeItemAttr"; +// END_OF_CHANGE_ITEM_ATTRIBUTES +const BYTE g_aBuffOnAttrPoints[] = { POINT_ENERGY, POINT_COSTUME_ATTR_BONUS }; + +struct FFindStone +{ + std::map m_mapStone; + + void operator()(LPENTITY pEnt) + { + if (pEnt->IsType(ENTITY_CHARACTER) == true) + { + LPCHARACTER pChar = (LPCHARACTER)pEnt; + + if (pChar->IsStone() == true) + { + m_mapStone[(DWORD)pChar->GetVID()] = pChar; + } + } + } +}; + +static bool IS_SUMMON_ITEM(int vnum) +{ + switch (vnum) + { + case 22000: + case 22010: + case 22011: + case 22020: + case ITEM_MARRIAGE_RING: + return true; + } + + return false; +} + +static bool IS_MONKEY_DUNGEON(int map_index) +{ + switch (map_index) + { + case 5: + case 25: + case 45: + case 108: + case 109: + return true;; + } + + return false; +} + +bool IS_SUMMONABLE_ZONE(int map_index) +{ + if (IS_MONKEY_DUNGEON(map_index)) + return false; + if (IS_CASTLE_MAP(map_index)) + return false; + + switch (map_index) + { + case 66 : + case 71 : + case 72 : + case 73 : + case 193 : +#if 0 + case 184 : + case 185 : + case 186 : + case 187 : + case 188 : + case 189 : +#endif + + case 216 : + case 217 : + case 208 : + + case 113 : + return false; + } + + if (map_index > 10000) return false; + + return true; +} + +bool IS_BOTARYABLE_ZONE(int nMapIndex) +{ + if (!g_bEnableBootaryCheck) return true; + + switch (nMapIndex) + { + case 1 : + case 3 : + case 21 : + case 23 : + case 41 : + case 43 : + return true; + } + + return false; +} + +static bool FN_check_item_socket(LPITEM item) +{ + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item->GetSocket(i) != item->GetProto()->alSockets[i]) + return false; + } + + return true; +} + +static void FN_copy_item_socket(LPITEM dest, LPITEM src) +{ + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + dest->SetSocket(i, src->GetSocket(i)); + } +} +static bool FN_check_item_sex(LPCHARACTER ch, LPITEM item) +{ + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_MALE)) + { + if (SEX_MALE==GET_SEX(ch)) + return false; + } + + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_FEMALE)) + { + if (SEX_FEMALE==GET_SEX(ch)) + return false; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// ITEM HANDLING +///////////////////////////////////////////////////////////////////////////// +bool CHARACTER::CanHandleItem(bool bSkipCheckRefine, bool bSkipObserver) +{ + if (!bSkipObserver) + if (m_bIsObserver) + return false; + + if (GetMyShop()) + return false; + + if (!bSkipCheckRefine) + if (m_bUnderRefine) + return false; + + if (IsCubeOpen() || NULL != DragonSoul_RefineWindow_GetOpener()) + return false; + + if (IsWarping()) + return false; + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + if ((m_bAcceCombination) || (m_bAcceAbsorption)) + return false; +#endif + + return true; +} + +LPITEM CHARACTER::GetInventoryItem(WORD wCell) const +{ + return GetItem(TItemPos(INVENTORY, wCell)); +} +LPITEM CHARACTER::GetItem(TItemPos Cell) const +{ + if (!m_PlayerSlots) + return nullptr; + + if (!IsValidItemPosition(Cell)) + return NULL; + + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + switch (window_type) + { + case INVENTORY: + case EQUIPMENT: + if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) + { + sys_err("CHARACTER::GetInventoryItem: invalid item cell %d", wCell); + return NULL; + } + return m_PlayerSlots->pItems[wCell]; + case DRAGON_SOUL_INVENTORY: + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + { + sys_err("CHARACTER::GetInventoryItem: invalid DS item cell %d", wCell); + return NULL; + } + return m_PlayerSlots->pDSItems[wCell]; + + default: + return NULL; + } + return NULL; +} + +void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem + #ifdef ENABLE_HIGHLIGHT_NEW_ITEM + , bool bWereMine + #endif +) +{ + if (!m_PlayerSlots) + return; + + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + if ((unsigned long)((CItem*)pItem) == 0xff || (unsigned long)((CItem*)pItem) == 0xffffffff) + { + sys_err("!!! FATAL ERROR !!! item == 0xff (char: %s cell: %u)", GetName(), wCell); + core_dump(); + return; + } + + if (pItem && pItem->GetOwner()) + { + assert(!"GetOwner exist"); + return; + } + + switch(window_type) + { + case INVENTORY: + case EQUIPMENT: + { + if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) + { + sys_err("CHARACTER::SetItem: invalid item cell %d", wCell); + return; + } + + LPITEM pOld = m_PlayerSlots->pItems[wCell]; + + if (pOld) + { + if (wCell < INVENTORY_MAX_NUM) + { + for (int i = 0; i < pOld->GetSize(); ++i) + { + int p = wCell + (i * 5); + + if (p >= INVENTORY_MAX_NUM) + continue; + + if (m_PlayerSlots->pItems[p] && m_PlayerSlots->pItems[p] != pOld) + continue; + + m_PlayerSlots->bItemGrid[p] = 0; + } + } + else + m_PlayerSlots->bItemGrid[wCell] = 0; + } + + if (pItem) + { + if (wCell < INVENTORY_MAX_NUM) + { + for (int i = 0; i < pItem->GetSize(); ++i) + { + int p = wCell + (i * 5); + + if (p >= INVENTORY_MAX_NUM) + continue; + + m_PlayerSlots->bItemGrid[p] = wCell + 1; + } + } + else + m_PlayerSlots->bItemGrid[wCell] = wCell + 1; + } + + m_PlayerSlots->pItems[wCell] = pItem; + } + break; + + case DRAGON_SOUL_INVENTORY: + { + LPITEM pOld = m_PlayerSlots->pDSItems[wCell]; + + if (pOld) + { + if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) + { + for (int i = 0; i < pOld->GetSize(); ++i) + { + int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + continue; + + if (m_PlayerSlots->pDSItems[p] && m_PlayerSlots->pDSItems[p] != pOld) + continue; + + m_PlayerSlots->wDSItemGrid[p] = 0; + } + } + else + m_PlayerSlots->wDSItemGrid[wCell] = 0; + } + + if (pItem) + { + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + { + sys_err("CHARACTER::SetItem: invalid DS item cell %d", wCell); + return; + } + + if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) + { + for (int i = 0; i < pItem->GetSize(); ++i) + { + int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + continue; + + m_PlayerSlots->wDSItemGrid[p] = wCell + 1; + } + } + else + m_PlayerSlots->wDSItemGrid[wCell] = wCell + 1; + } + + m_PlayerSlots->pDSItems[wCell] = pItem; + } + break; + default: + sys_err ("Invalid Inventory type %d", window_type); + return; + } + + if (GetDesc()) + { + if (pItem) + { + TPacketGCItemSet pack; + pack.header = HEADER_GC_ITEM_SET; + pack.Cell = Cell; + + pack.count = pItem->GetCount(); + pack.vnum = pItem->GetVnum(); + pack.flags = pItem->GetFlag(); + pack.anti_flags = pItem->GetAntiFlag(); +#ifdef ENABLE_HIGHLIGHT_NEW_ITEM + pack.highlight = !bWereMine || (Cell.window_type == DRAGON_SOUL_INVENTORY); +#else + pack.highlight = (Cell.window_type == DRAGON_SOUL_INVENTORY); +#endif + + thecore_memcpy(pack.alSockets, pItem->GetSockets(), sizeof(pack.alSockets)); + thecore_memcpy(pack.aAttr, pItem->GetAttributes(), sizeof(pack.aAttr)); + + GetDesc()->Packet(&pack, sizeof(TPacketGCItemSet)); + } + else + { + TPacketGCItemDelDeprecated pack; + pack.header = HEADER_GC_ITEM_DEL; + pack.Cell = Cell; + pack.count = 0; + pack.vnum = 0; + memset(pack.alSockets, 0, sizeof(pack.alSockets)); + memset(pack.aAttr, 0, sizeof(pack.aAttr)); + + GetDesc()->Packet(&pack, sizeof(TPacketGCItemDelDeprecated)); + } + } + + if (pItem) + { + pItem->SetCell(this, wCell); + switch (window_type) + { + case INVENTORY: + case EQUIPMENT: + if ((wCell < INVENTORY_MAX_NUM) || (BELT_INVENTORY_SLOT_START <= wCell && BELT_INVENTORY_SLOT_END > wCell)) + pItem->SetWindow(INVENTORY); + else + pItem->SetWindow(EQUIPMENT); + break; + case DRAGON_SOUL_INVENTORY: + pItem->SetWindow(DRAGON_SOUL_INVENTORY); + break; + } + } +} + +LPITEM CHARACTER::GetWear(BYTE bCell) const +{ + if (!m_PlayerSlots) + return nullptr; + + if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX) + { + sys_err("CHARACTER::GetWear: invalid wear cell %d", bCell); + return NULL; + } + + return m_PlayerSlots->pItems[INVENTORY_MAX_NUM + bCell]; +} + +void CHARACTER::SetWear(BYTE bCell, LPITEM item) +{ + if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX) + { + sys_err("CHARACTER::SetItem: invalid item cell %d", bCell); + return; + } + + SetItem(TItemPos (INVENTORY, INVENTORY_MAX_NUM + bCell), item); + + if (!item && bCell == WEAR_WEAPON) + { + if (IsAffectFlag(AFF_GWIGUM)) + RemoveAffect(SKILL_GWIGEOM); + + if (IsAffectFlag(AFF_GEOMGYEONG)) + RemoveAffect(SKILL_GEOMKYUNG); + } +} + +void CHARACTER::ClearItem() +{ + int i; + LPITEM item; + + for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + { + if ((item = GetInventoryItem(i))) + { + item->SetSkipSave(true); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + item->RemoveFromCharacter(); + M2_DESTROY_ITEM(item); + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i)))) + { + item->SetSkipSave(true); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + item->RemoveFromCharacter(); + M2_DESTROY_ITEM(item); + } + } +} + +bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, BYTE bSize, int iExceptionCell) const +{ + if (!m_PlayerSlots) + return false; + + switch (Cell.window_type) + { + case INVENTORY: + { + WORD bCell = Cell.cell; + + ++iExceptionCell; + + if (Cell.IsBeltInventoryPosition()) + { + LPITEM beltItem = GetWear(WEAR_BELT); + + if (NULL == beltItem) + return false; + + if (false == CBeltInventoryHelper::IsAvailableCell(bCell - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0))) + return false; + + if (m_PlayerSlots->bItemGrid[bCell]) + { + if (m_PlayerSlots->bItemGrid[bCell] == iExceptionCell) + return true; + + return false; + } + + if (bSize == 1) + return true; + + } + else if (bCell >= INVENTORY_MAX_NUM) + return false; + + if (m_PlayerSlots->bItemGrid[bCell]) + { + if (m_PlayerSlots->bItemGrid[bCell] == iExceptionCell) + { + if (bSize == 1) + return true; + + int j = 1; + BYTE bPage = bCell / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT); + + do + { + BYTE p = bCell + (5 * j); + + if (p >= INVENTORY_MAX_NUM) + return false; + + if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) + return false; + + if (m_PlayerSlots->bItemGrid[p]) + if (m_PlayerSlots->bItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + else + return false; + } + + if (1 == bSize) + return true; + else + { + int j = 1; + BYTE bPage = bCell / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT); + + do + { + BYTE p = bCell + (5 * j); + + if (p >= INVENTORY_MAX_NUM) + return false; + + if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) + return false; + + if (m_PlayerSlots->bItemGrid[p]) + if (m_PlayerSlots->bItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + } + break; + case DRAGON_SOUL_INVENTORY: + { + WORD wCell = Cell.cell; + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + iExceptionCell++; + + if (m_PlayerSlots->wDSItemGrid[wCell]) + { + if (m_PlayerSlots->wDSItemGrid[wCell] == iExceptionCell) + { + if (bSize == 1) + return true; + + int j = 1; + + do + { + int p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + if (m_PlayerSlots->wDSItemGrid[p]) + if (m_PlayerSlots->wDSItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + else + return false; + } + + if (1 == bSize) + return true; + else + { + int j = 1; + + do + { + int p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + if (m_PlayerSlots->wDSItemGrid[p]) // @fixme159 (from bItemGrid) + if (m_PlayerSlots->wDSItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + } + } + return false; +} + +int CHARACTER::GetEmptyInventoryEx(LPITEM item) +{ + if (!item) + return -1; + + int cell = -1; + if (item->IsDragonSoul()) + cell = GetEmptyDragonSoulInventory(item); + // else if (item->IsSpecialInventory()) // example + // cell = GetEmptySpecialInventory(item); + else + cell = GetEmptyInventory(item->GetSize()); + + return cell; +} + +int CHARACTER::GetEmptyInventory(BYTE size) const +{ + for ( int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (IsEmptyItemGrid(TItemPos (INVENTORY, i), size)) + return i; + return -1; +} + +int CHARACTER::GetEmptyDragonSoulInventory(LPITEM pItem) const +{ + if (NULL == pItem || !pItem->IsDragonSoul()) + return -1; + if (!DragonSoul_IsQualified()) + { + return -1; + } + BYTE bSize = pItem->GetSize(); + WORD wBaseCell = DSManager::instance().GetBasePosition(pItem); + + if (WORD_MAX == wBaseCell) + return -1; + + for (int i = 0; i < DRAGON_SOUL_BOX_SIZE; ++i) + if (IsEmptyItemGrid(TItemPos(DRAGON_SOUL_INVENTORY, i + wBaseCell), bSize)) + return i + wBaseCell; + + return -1; +} + +void CHARACTER::CopyDragonSoulItemGrid(std::vector& vDragonSoulItemGrid) const +{ + vDragonSoulItemGrid.resize(DRAGON_SOUL_INVENTORY_MAX_NUM); + + std::copy(m_PlayerSlots->wDSItemGrid.data(), m_PlayerSlots->wDSItemGrid.data() + DRAGON_SOUL_INVENTORY_MAX_NUM, vDragonSoulItemGrid.begin()); +} + +int CHARACTER::CountEmptyInventory() const +{ + int count = 0; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (GetInventoryItem(i)) + count += GetInventoryItem(i)->GetSize(); + + return (INVENTORY_MAX_NUM - count); +} + +void TransformRefineItem(LPITEM pkOldItem, LPITEM pkNewItem) +{ + // ACCESSORY_REFINE + if (pkOldItem->IsAccessoryForSocket()) + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + pkNewItem->SetSocket(i, pkOldItem->GetSocket(i)); + } + //pkNewItem->StartAccessorySocketExpireEvent(); + } + // END_OF_ACCESSORY_REFINE + else + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (!pkOldItem->GetSocket(i)) + break; + else + pkNewItem->SetSocket(i, 1); + } + + int slot = 0; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + long socket = pkOldItem->GetSocket(i); + + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + pkNewItem->SetSocket(slot++, socket); + } + + } + + pkOldItem->CopyAttributeTo(pkNewItem); +} + +void NotifyRefineSuccess(LPCHARACTER ch, LPITEM item, const char* way) +{ + if (NULL != ch && item != NULL) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineSuceeded"); + + LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), 1, way); + } +} + +void NotifyRefineFail(LPCHARACTER ch, LPITEM item, const char* way, int success = 0) +{ + if (NULL != ch && NULL != item) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineFailed"); + + LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), success, way); + } +} + +void CHARACTER::SetRefineNPC(LPCHARACTER ch) +{ + if ( ch != NULL ) + { + m_dwRefineNPCVID = ch->GetVID(); + } + else + { + m_dwRefineNPCVID = 0; + } +} + +bool CHARACTER::DoRefine(LPITEM item, bool bMoneyOnly) +{ + if (!CanHandleItem(true)) + { + ClearRefineMode(); + return false; + } + + if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0) + { + if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5)) + { + sys_log(0, "can't refine %d %s", GetPlayerID(), GetName()); + return false; + } + } + + const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + return false; + + DWORD result_vnum = item->GetRefinedVnum(); + + // REFINE_COST + int cost = ComputeRefineFee(prt->cost); + + int RefineChance = GetQuestFlag("main_quest_lv7.refine_chance"); + + if (RefineChance > 0) + { + if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȸ 20 ⸸ մϴ")); + return false; + } + + cost = 0; + SetQuestFlag("main_quest_lv7.refine_chance", RefineChance - 1); + } + // END_OF_REFINE_COST + + if (result_vnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING) + return false; + + TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum()); + + if (!pProto) + { + sys_err("DoRefine NOT GET ITEM PROTO %d", item->GetRefinedVnum()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + // REFINE_COST + if (GetGold() < cost) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + return false; + } + + if (!bMoneyOnly && !RefineChance) + { + for (int i = 0; i < prt->material_count; ++i) + { + if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count) + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count); + } + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ ᰡ մϴ.")); + return false; + } + } + + for (int i = 0; i < prt->material_count; ++i) + RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count); + } + + int prob = number(1, 100); + + if (IsRefineThroughGuild() || bMoneyOnly) + prob -= 10; + + // END_OF_REFINE_COST + + if (prob <= prt->prob) + { + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + // DETAIL_REFINE_LOG + NotifyRefineSuccess(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)"); + // END_OF_DETAIL_REFINE_LOG + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + + sys_log(0, "Refine Success %d", cost); + pkNewItem->AttrLog(); + //PointChange(POINT_GOLD, -cost); + sys_log(0, "PayPee %d", cost); + PayRefineFee(cost); + sys_log(0, "PayPee End %d", cost); + } + else + { + // DETAIL_REFINE_LOG + + sys_err("cannot create item %u", result_vnum); + NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + // END_OF_DETAIL_REFINE_LOG + } + } + else + { + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost); + NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + item->AttrLog(); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)"); + + //PointChange(POINT_GOLD, -cost); + PayRefineFee(cost); + } + + return true; +} + +enum enum_RefineScrolls +{ + CHUKBOK_SCROLL = 0, + HYUNIRON_CHN = 1, + YONGSIN_SCROLL = 2, + MUSIN_SCROLL = 3, + YAGONG_SCROLL = 4, + MEMO_SCROLL = 5, + BDRAGON_SCROLL = 6, +}; + +bool CHARACTER::DoRefineWithScroll(LPITEM item) +{ + if (!CanHandleItem(true)) + { + ClearRefineMode(); + return false; + } + + ClearRefineMode(); + + if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0) + { + if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5)) + { + sys_log(0, "can't refine %d %s", GetPlayerID(), GetName()); + return false; + } + } + + const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + return false; + + LPITEM pkItemScroll; + + if (m_iRefineAdditionalCell < 0) + return false; + + pkItemScroll = GetInventoryItem(m_iRefineAdditionalCell); + + if (!pkItemScroll) + return false; + + if (!(pkItemScroll->GetType() == ITEM_USE && pkItemScroll->GetSubType() == USE_TUNING)) + return false; + + if (pkItemScroll->GetVnum() == item->GetVnum()) + return false; + + DWORD result_vnum = item->GetRefinedVnum(); + DWORD result_fail_vnum = item->GetRefineFromVnum(); + + if (result_vnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + // MUSIN_SCROLL + if (pkItemScroll->GetValue(0) == MUSIN_SCROLL) + { + if (item->GetRefineLevel() >= 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + } + // END_OF_MUSIC_SCROLL + + else if (pkItemScroll->GetValue(0) == MEMO_SCROLL) + { + if (item->GetRefineLevel() != pkItemScroll->GetValue(1)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + } + else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL) + { + if (item->GetType() != ITEM_METIN || item->GetRefineLevel() != 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + } + + TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum()); + + if (!pProto) + { + sys_err("DoRefineWithScroll NOT GET ITEM PROTO %d", item->GetRefinedVnum()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + if (GetGold() < prt->cost) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + return false; + } + + for (int i = 0; i < prt->material_count; ++i) + { + if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count) + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count); + } + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ ᰡ մϴ.")); + return false; + } + } + + for (int i = 0; i < prt->material_count; ++i) + RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count); + + int prob = number(1, 100); + int success_prob = prt->prob; + bool bDestroyWhenFail = false; + + const char* szRefineType = "SCROLL"; + + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN || + pkItemScroll->GetValue(0) == YONGSIN_SCROLL || + pkItemScroll->GetValue(0) == YAGONG_SCROLL) + { + const char hyuniron_prob[9] = { 100, 75, 65, 55, 45, 40, 35, 25, 20 }; + const char yagong_prob[9] = { 100, 100, 90, 80, 70, 60, 50, 30, 20 }; + + if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL) + { + success_prob = hyuniron_prob[MINMAX(0, item->GetRefineLevel(), 8)]; + } + else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL) + { + success_prob = yagong_prob[MINMAX(0, item->GetRefineLevel(), 8)]; + } + else if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) {} // @fixme121 + else + { + sys_err("REFINE : Unknown refine scroll item. Value0: %d", pkItemScroll->GetValue(0)); + } + + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[Only Test] Success_Prob %d, RefineLevel %d ", success_prob, item->GetRefineLevel()); + } + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) + bDestroyWhenFail = true; + + // DETAIL_REFINE_LOG + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) + { + szRefineType = "HYUNIRON"; + } + else if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL) + { + szRefineType = "GOD_SCROLL"; + } + else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL) + { + szRefineType = "YAGONG_SCROLL"; + } + // END_OF_DETAIL_REFINE_LOG + } + + // DETAIL_REFINE_LOG + if (pkItemScroll->GetValue(0) == MUSIN_SCROLL) + { + success_prob = 100; + + szRefineType = "MUSIN_SCROLL"; + } + // END_OF_DETAIL_REFINE_LOG + else if (pkItemScroll->GetValue(0) == MEMO_SCROLL) + { + success_prob = 100; + szRefineType = "MEMO_SCROLL"; + } + else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL) + { + success_prob = 80; + szRefineType = "BDRAGON_SCROLL"; + } + + pkItemScroll->SetCount(pkItemScroll->GetCount() - 1); + + if (prob <= success_prob) + { + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + NotifyRefineSuccess(this, item, szRefineType); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)"); + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + pkNewItem->AttrLog(); + //PointChange(POINT_GOLD, -prt->cost); + PayRefineFee(prt->cost); + } + else + { + sys_err("cannot create item %u", result_vnum); + NotifyRefineFail(this, item, szRefineType); + } + } + else if (!bDestroyWhenFail && result_fail_vnum) + { + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_fail_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE FAIL", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost); + NotifyRefineFail(this, item, szRefineType, -1); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)"); + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + + pkNewItem->AttrLog(); + + //PointChange(POINT_GOLD, -prt->cost); + PayRefineFee(prt->cost); + } + else + { + sys_err("cannot create item %u", result_fail_vnum); + NotifyRefineFail(this, item, szRefineType); + } + } + else + { + NotifyRefineFail(this, item, szRefineType); + + PayRefineFee(prt->cost); + } + + return true; +} + +bool CHARACTER::RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell) +{ + if (bCell > INVENTORY_MAX_NUM) + return false; + + LPITEM item = GetInventoryItem(bCell); + + if (!item) + return false; + + // REFINE_COST + if (bType == REFINE_TYPE_MONEY_ONLY && !GetQuestFlag("deviltower_zone.can_refine")) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ÿ Ϸ ѹ 밡մϴ.")); + return false; + } + // END_OF_REFINE_COST + + TPacketGCRefineInformation p; + + p.header = HEADER_GC_REFINE_INFORMATION; + p.pos = bCell; + p.src_vnum = item->GetVnum(); + p.result_vnum = item->GetRefinedVnum(); + p.type = bType; + + if (p.result_vnum == 0) + { + sys_err("RefineInformation p.result_vnum == 0"); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING) + { + if (bType == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" δ ϴ.")); + return false; + } + else + { + LPITEM itemScroll = GetInventoryItem(iAdditionalCell); + if (!itemScroll || item->GetVnum() == itemScroll->GetVnum()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ĥ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ູ ö ĥ ֽϴ.")); + return false; + } + } + } + + CRefineManager & rm = CRefineManager::instance(); + + const TRefineTable* prt = rm.GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + { + sys_err("RefineInformation NOT GET REFINE SET %d", item->GetRefineSet()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + // REFINE_COST + + //MAIN_QUEST_LV7 + if (GetQuestFlag("main_quest_lv7.refine_chance") > 0) + { + if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȸ 20 ⸸ մϴ")); + return false; + } + p.cost = 0; + } + else + p.cost = ComputeRefineFee(prt->cost); + + //END_MAIN_QUEST_LV7 + p.prob = prt->prob; + if (bType == REFINE_TYPE_MONEY_ONLY) + { + p.material_count = 0; + memset(p.materials, 0, sizeof(p.materials)); + } + else + { + p.material_count = prt->material_count; + thecore_memcpy(&p.materials, prt->materials, sizeof(prt->materials)); + } + // END_OF_REFINE_COST + + GetDesc()->Packet(&p, sizeof(TPacketGCRefineInformation)); + + SetRefineMode(iAdditionalCell); + return true; +} + +bool CHARACTER::RefineItem(LPITEM pkItem, LPITEM pkTarget) +{ + if (!CanHandleItem()) + return false; + + if (pkItem->GetSubType() == USE_TUNING) + { + // MUSIN_SCROLL + if (pkItem->GetValue(0) == MUSIN_SCROLL) + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_MUSIN, pkItem->GetCell()); + // END_OF_MUSIN_SCROLL + else if (pkItem->GetValue(0) == HYUNIRON_CHN) + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_HYUNIRON, pkItem->GetCell()); + else if (pkItem->GetValue(0) == BDRAGON_SCROLL) + { + if (pkTarget->GetRefineSet() != 702) return false; + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_BDRAGON, pkItem->GetCell()); + } + else + { + if (pkTarget->GetRefineSet() == 501) return false; + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_SCROLL, pkItem->GetCell()); + } + } + else if (pkItem->GetSubType() == USE_DETACHMENT && IS_SET(pkTarget->GetFlag(), ITEM_FLAG_REFINEABLE)) + { + LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT", pkTarget->GetName()); + + bool bHasMetinStone = false; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++) + { + long socket = pkTarget->GetSocket(i); + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + { + bHasMetinStone = true; + break; + } + } + + if (bHasMetinStone) + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + long socket = pkTarget->GetSocket(i); + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + { + AutoGiveItem(socket); + //TItemTable* pTable = ITEM_MANAGER::instance().GetTable(pkTarget->GetSocket(i)); + //pkTarget->SetSocket(i, pTable->alValues[2]); + + pkTarget->SetSocket(i, ITEM_BROKEN_METIN_VNUM); + } + } + pkItem->SetCount(pkItem->GetCount() - 1); + return true; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ƾ ϴ.")); + return false; + } + } + + return false; +} + +EVENTFUNC(kill_campfire_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "kill_campfire_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + ch->m_pkMiningEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +bool CHARACTER::GiveRecallItem(LPITEM item) +{ + int idx = GetMapIndex(); + int iEmpireByMapIndex = -1; + + if (idx < 20) + iEmpireByMapIndex = 1; + else if (idx < 40) + iEmpireByMapIndex = 2; + else if (idx < 60) + iEmpireByMapIndex = 3; + else if (idx < 10000) + iEmpireByMapIndex = 0; + + switch (idx) + { + case 66: + case 216: + iEmpireByMapIndex = -1; + break; + } + + if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ Դϴ.")); + return false; + } + + int pos; + + if (item->GetCount() == 1) + { + item->SetSocket(0, GetX()); + item->SetSocket(1, GetY()); + } + else if ((pos = GetEmptyInventory(item->GetSize())) != -1) + { + LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), 1); + + if (NULL != item2) + { + item->SetCount(item->GetCount() - 1); + item2->SetSocket(0, GetX()); + item2->SetSocket(1, GetY()); + AutoGiveItem(item2); // @fixme316 + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + return false; + } + + return true; +} + +void CHARACTER::ProcessRecallItem(LPITEM item) +{ + int idx; + + if ((idx = SECTREE_MANAGER::instance().GetMapIndex(item->GetSocket(0), item->GetSocket(1))) == 0) + return; + + int iEmpireByMapIndex = -1; + + if (idx < 20) + iEmpireByMapIndex = 1; + else if (idx < 40) + iEmpireByMapIndex = 2; + else if (idx < 60) + iEmpireByMapIndex = 3; + else if (idx < 10000) + iEmpireByMapIndex = 0; + + switch (idx) + { + case 66: + case 216: + iEmpireByMapIndex = -1; + break; + + case 301: + case 302: + case 303: + case 304: + if( GetLevel() < 90 ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return; + } + else + break; + } + + if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ Ÿ ־ ȯ ϴ.")); + item->SetSocket(0, 0); + item->SetSocket(1, 0); + } + else + { + sys_log(1, "Recall: %s %d %d -> %d %d", GetName(), GetX(), GetY(), item->GetSocket(0), item->GetSocket(1)); + WarpSet(item->GetSocket(0), item->GetSocket(1)); + item->SetCount(item->GetCount() - 1); + } +} + +void CHARACTER::__OpenPrivateShop() +{ +#ifdef ENABLE_OPEN_SHOP_WITH_ARMOR + ChatPacket(CHAT_TYPE_COMMAND, "OpenPrivateShop"); +#else + unsigned bodyPart = GetPart(PART_MAIN); + switch (bodyPart) + { + case 0: + case 1: + case 2: + ChatPacket(CHAT_TYPE_COMMAND, "OpenPrivateShop"); + break; + default: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֽϴ.")); + break; + } +#endif +} + +// MYSHOP_PRICE_LIST +void CHARACTER::SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice) +{ + char szLine[256]; + snprintf(szLine, sizeof(szLine), "MyShopPriceList %u %u", dwItemVnum, dwItemPrice); + ChatPacket(CHAT_TYPE_COMMAND, szLine); + sys_log(0, szLine); +} + +// + +// +void CHARACTER::UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p) +{ + const TItemPriceInfo* pInfo = (const TItemPriceInfo*)(p + 1); + + if (!p->byCount) + + SendMyShopPriceListCmd(1, 0); + else { + for (int idx = 0; idx < p->byCount; idx++) + SendMyShopPriceListCmd(pInfo[ idx ].dwVnum, pInfo[ idx ].dwPrice); + } + + __OpenPrivateShop(); +} + +// + +// +void CHARACTER::UseSilkBotary(void) +{ + if (m_bNoOpenedShop) { + DWORD dwPlayerID = GetPlayerID(); + db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_REQ, GetDesc()->GetHandle(), &dwPlayerID, sizeof(DWORD)); + m_bNoOpenedShop = false; + } else { + __OpenPrivateShop(); + } +} +// END_OF_MYSHOP_PRICE_LIST + +int CalculateConsume(LPCHARACTER ch) +{ + static const int WARP_NEED_LIFE_PERCENT = 30; + static const int WARP_MIN_LIFE_PERCENT = 10; + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + int consumeLife = 0; + { + // CheckNeedLifeForWarp + const int curLife = ch->GetHP(); + const int needPercent = WARP_NEED_LIFE_PERCENT; + const int needLife = ch->GetMaxHP() * needPercent / 100; + if (curLife < needLife) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ڶ ϴ.")); + return -1; + } + + consumeLife = needLife; + + const int minPercent = WARP_MIN_LIFE_PERCENT; + const int minLife = ch->GetMaxHP() * minPercent / 100; + if (curLife - needLife < minLife) + consumeLife = curLife - minLife; + + if (consumeLife < 0) + consumeLife = 0; + } + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + return consumeLife; +} + +int CalculateConsumeSP(LPCHARACTER lpChar) +{ + static const int NEED_WARP_SP_PERCENT = 30; + + const int curSP = lpChar->GetSP(); + const int needSP = lpChar->GetMaxSP() * NEED_WARP_SP_PERCENT / 100; + + if (curSP < needSP) + { + lpChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ŷ ڶ ϴ.")); + return -1; + } + + return needSP; +} + +// #define ENABLE_FIREWORK_STUN +#define ENABLE_ADDSTONE_FAILURE +bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell) +{ + int iLimitRealtimeStartFirstUseFlagIndex = -1; + //int iLimitTimerBasedOnWearFlagIndex = -1; + + WORD wDestCell = DestCell.cell; + BYTE bDestInven = DestCell.window_type; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limitValue = item->GetProto()->aLimits[i].lValue; + + switch (item->GetProto()->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limitValue) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return false; + } + break; + + case LIMIT_REAL_TIME_START_FIRST_USE: + iLimitRealtimeStartFirstUseFlagIndex = i; + break; + + case LIMIT_TIMER_BASED_ON_WEAR: + //iLimitTimerBasedOnWearFlagIndex = i; + break; + } + } + + if (test_server) + { + sys_log(0, "USE_ITEM %d %s, Inven %d, Cell %d, DestInven %d, DestCell %d, ItemType %d, SubType %d", item->GetVnum(), item->GetName(), item->GetWindow(), item->GetCell(), bDestInven, wDestCell, item->GetType(), item->GetSubType()); + } + + if ( CArenaManager::instance().IsLimitedItem( GetMapIndex(), item->GetVnum() ) == true ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && IsLimitedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + // @fixme402 (IsLoadedAffect to block affect hacking) + if (!IsLoadedAffect()) + { + ChatPacket(CHAT_TYPE_INFO, "Affects are not loaded yet!"); + return false; + } + + // @fixme141 BEGIN + if (TItemPos(item->GetWindow(), item->GetCell()).IsBeltInventoryPosition()) + { + LPITEM beltItem = GetWear(WEAR_BELT); + + if (NULL == beltItem) + { + ChatPacket(CHAT_TYPE_INFO, " You can't use this item if you have no equipped belt."); + return false; + } + + if (false == CBeltInventoryHelper::IsAvailableCell(item->GetCell() - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0))) + { + ChatPacket(CHAT_TYPE_INFO, " You can't use this item if you don't upgrade your belt."); + return false; + } + } + // @fixme141 END + + if (-1 != iLimitRealtimeStartFirstUseFlagIndex) + { + if (0 == item->GetSocket(1)) + { + long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[iLimitRealtimeStartFirstUseFlagIndex].lValue; + + if (0 == duration) + duration = 60 * 60 * 24 * 7; + + item->SetSocket(0, time(0) + duration); + item->StartRealTimeExpireEvent(); + } + + if (false == item->IsEquipped()) + item->SetSocket(1, item->GetSocket(1) + 1); + } + + switch (item->GetType()) + { + case ITEM_HAIR: + return ItemProcess_Hair(item, wDestCell); + + case ITEM_POLYMORPH: + return ItemProcess_Polymorph(item); + + case ITEM_QUEST: +#ifdef ENABLE_QUEST_DND_EVENT + if (IS_SET(item->GetFlag(), ITEM_FLAG_APPLICABLE)) + { + LPITEM item2; + + if (!GetItem(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + quest::CQuestManager::instance().DND(GetPlayerID(), item, item2, false); + return true; + } +#endif + + if (GetArena() != NULL || IsObserverMode() == true) + { + if (item->GetVnum() == 50051 || item->GetVnum() == 50052 || item->GetVnum() == 50053) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + } + + if (!IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_USE | ITEM_FLAG_QUEST_USE_MULTIPLE)) + { + if (item->GetSIGVnum() == 0) + quest::CQuestManager::instance().UseItem(GetPlayerID(), item, false); + else + quest::CQuestManager::instance().SIGUse(GetPlayerID(), item->GetSIGVnum(), item, false); + } + + break; + + case ITEM_CAMPFIRE: + { + float fx, fy; + GetDeltaByDegree(GetRotation(), 100.0f, &fx, &fy); + + LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy)); + + if (!tree) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ں ǿ Դϴ.")); + return false; + } + + if (tree->IsAttr((long)(GetX()+fx), (long)(GetY()+fy), ATTR_WATER)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ӿ ں ǿ ϴ.")); + return false; + } + + LPCHARACTER campfire = CHARACTER_MANAGER::instance().SpawnMob(fishing::CAMPFIRE_MOB, GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy), 0, false, number(0, 359)); + + char_event_info* info = AllocEventInfo(); + + info->ch = campfire; + + campfire->m_pkMiningEvent = event_create(kill_campfire_event, info, PASSES_PER_SEC(40)); + + item->SetCount(item->GetCount() - 1); + } + break; + + case ITEM_UNIQUE: + { + switch (item->GetSubType()) + { + case USE_ABILITY_UP: + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true, true); + break; + + case APPLY_ATT_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true, true); + break; + + case APPLY_STR: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_DEX: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_CON: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_INT: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_CAST_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_RESIST_MAGIC: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_RESIST_MAGIC, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_ATT_GRADE_BONUS: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_DEF_GRADE_BONUS: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DEF_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + default: + { + if (item->GetSubType() == USE_SPECIAL) + { + sys_log(0, "ITEM_UNIQUE: USE_SPECIAL %u", item->GetVnum()); + + switch (item->GetVnum()) + { + case 71049: + if (g_bEnableBootaryCheck) + { + if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true) + { + UseSilkBotary(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ")); + } + } + else + { + UseSilkBotary(); + } + break; + } + } + else + { + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + } + } + break; + } + } + break; + + case ITEM_COSTUME: + case ITEM_WEAPON: + case ITEM_ARMOR: + case ITEM_ROD: + case ITEM_RING: + case ITEM_BELT: + // MINING + case ITEM_PICK: + // END_OF_MINING + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + break; + + #ifdef ENABLE_PET_SYSTEM_EX + case ITEM_PET: + switch (item->GetSubType()) + { + case PET_UPBRINGING: //1 + case PET_PAY: //12 + { + SummonPetFromItem(item); + } + break; + #endif + + case PET_EGG: //0 + case PET_BAG: //2 + case PET_FEEDSTUFF: //3 + case PET_SKILL: //4 + case PET_SKILL_DEL_BOOK: //5 + case PET_NAME_CHANGE: //6 + case PET_EXPFOOD: //7 + case PET_SKILL_ALL_DEL_BOOK: //8 + case PET_EXPFOOD_PER: //9 + case PET_ATTR_DETERMINE: //10 + case PET_ATTR_CHANGE: //11 + case PET_PRIMIUM_FEEDSTUFF: //13 + break; + } + break; + + case ITEM_DS: + { + if (!item->IsEquipped()) + return false; + return DSManager::instance().PullOut(this, NPOS, item); + break; + } + case ITEM_SPECIAL_DS: + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + break; + + case ITEM_FISH: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + if (item->GetSubType() == FISH_ALIVE) + fishing::UseFish(this, item); + } + break; + + case ITEM_TREASURE_BOX: + { + return false; + + } + break; + + case ITEM_TREASURE_KEY: + { + LPITEM item2; + + if (!GetItem(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetType() != ITEM_TREASURE_BOX) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ƴѰ .")); + return false; + } + + if (item->GetValue(0) == item2->GetValue(0)) + { + DWORD dwBoxVnum = item2->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(0); + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + ITEM_MANAGER::instance().RemoveItem(item); + ITEM_MANAGER::instance().RemoveItem(item2); + + for (int i = 0; i < count; i++){ + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case CSpecialItemGroup::BLEEDING: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#endif + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + + } + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("谡 ʴ .")); + return false; + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("谡 ʴ .")); + return false; + } + } + break; + + case ITEM_GIFTBOX: + { +#ifdef ENABLE_NEWSTUFF + if (g_BoxUseTimeLimitValue && !PulseManager::Instance().IncreaseClock(GetPlayerID(), ePulse::BoxOpening, std::chrono::milliseconds(g_BoxUseTimeLimitValue))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 带 ϴ.")); + return false; + } +#endif + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(0); + int count = 0; + + if( dwBoxVnum > 51500 && dwBoxVnum < 52000 ) + { + if( !(this->DragonSoul_IsQualified()) ) + { + ChatPacket(CHAT_TYPE_INFO,LC_TEXT(" ȥ Ʈ Ϸϼž մϴ.")); + return false; + } + } + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + item->SetCount(item->GetCount()-1); + + for (int i = 0; i < count; i++){ + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case CSpecialItemGroup::BLEEDING: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#endif + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + } + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ƹ͵ ϴ.")); + return false; + } + } + break; + + case ITEM_SKILLFORGET: + { + if (!item->GetSocket(0)) + { + ITEM_MANAGER::instance().RemoveItem(item); + return false; + } + + DWORD dwVnum = item->GetSocket(0); + + if (SkillLevelDown(dwVnum)) + { + ITEM_MANAGER::instance().RemoveItem(item); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ų µ Ͽϴ.")); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ų ϴ.")); + } + break; + + case ITEM_SKILLBOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + } + + DWORD dwVnum = 0; + + if (item->GetVnum() == 50300) + { + dwVnum = item->GetSocket(0); + } + else + { + dwVnum = item->GetValue(0); + } + + if (0 == dwVnum) + { + ITEM_MANAGER::instance().RemoveItem(item); + + return false; + } + + if (true == LearnSkillByBook(dwVnum)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + + if (distribution_test_server) + iReadDelay /= 3; + + SetSkillNextReadTime(dwVnum, get_global_time() + iReadDelay); + } + } + break; + + case ITEM_USE: + { + if (item->GetVnum() > 50800 && item->GetVnum() <= 50820) + { + if (test_server) + sys_log (0, "ADD addtional effect : vnum(%d) subtype(%d)", item->GetOriginalVnum(), item->GetSubType()); + + int affect_type = AFFECT_EXP_BONUS_EURO_FREE; + int apply_type = aApplyInfo[item->GetValue(0)].bPointType; + int apply_value = item->GetValue(2); + int apply_duration = item->GetValue(1); + + switch (item->GetSubType()) + { + case USE_ABILITY_UP: + if (FindAffect(affect_type, apply_type)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + return false; + } + + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(affect_type, apply_type, apply_value, AFF_MOV_SPEED_POTION, apply_duration, 0, true, true); + break; + + case APPLY_ATT_SPEED: + AddAffect(affect_type, apply_type, apply_value, AFF_ATT_SPEED_POTION, apply_duration, 0, true, true); + break; + + case APPLY_STR: + case APPLY_DEX: + case APPLY_CON: + case APPLY_INT: + case APPLY_CAST_SPEED: + case APPLY_RESIST_MAGIC: + case APPLY_ATT_GRADE_BONUS: + case APPLY_DEF_GRADE_BONUS: + AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, true, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_AFFECT : + { + if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + AddAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false, true); + item->SetCount(item->GetCount() - 1); + } + } + break; + + case USE_POTION_NODELAY: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 70020 : + case 71018 : + case 71019 : + case 71020 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + break; + } + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + bool used = false; + + if (item->GetValue(0) != 0) + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(1) != 0) + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (item->GetValue(3) != 0) + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(4) != 0) + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (used) + { + if (item->GetVnum() == 50085 || item->GetVnum() == 50086) + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǵ Ͽϴ")); + SetUseSeedOrMoonBottleTime(); + } + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + m_nPotionLimit--; + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + item->SetCount(item->GetCount() - 1); + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + } + } + break; + } + + return true; + } + + if (item->GetVnum() >= 27863 && item->GetVnum() <= 27883) + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + } + + if (test_server) + { + sys_log (0, "USE_ITEM %s Type %d SubType %d vnum %d", item->GetName(), item->GetType(), item->GetSubType(), item->GetOriginalVnum()); + } + + switch (item->GetSubType()) + { + case USE_TIME_CHARGE_PER: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + + if (pDestItem->IsDragonSoul()) + { + int ret; + char buf[128]; + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + ret = pDestItem->GiveMoreTime_Per((float)item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + ret = pDestItem->GiveMoreTime_Per((float)item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + if (ret > 0) + { + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + sprintf(buf, "Inc %ds by item{VN:%d SOC%d:%ld}", ret, item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + sprintf(buf, "Inc %ds by item{VN:%d VAL%d:%ld}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ŭ Ǿϴ."), ret); + item->SetCount(item->GetCount() - 1); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf); + return true; + } + else + { + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + sprintf(buf, "No change by item{VN:%d SOC%d:%ld}", item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + sprintf(buf, "No change by item{VN:%d VAL%d:%ld}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf); + return false; + } + } + else + return false; + } + break; + case USE_TIME_CHARGE_FIX: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + + if (pDestItem->IsDragonSoul()) + { + int ret = pDestItem->GiveMoreTime_Fix(item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + char buf[128]; + if (ret) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ŭ Ǿϴ."), ret); + sprintf(buf, "Increase %ds by item{VN:%d VAL%d:%ld}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf); + item->SetCount(item->GetCount() - 1); + return true; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + sprintf(buf, "No change by item{VN:%d VAL%d:%ld}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf); + return false; + } + } + else + return false; + } + break; + case USE_SPECIAL: + + switch (item->GetVnum()) + { + case ITEM_NOG_POCKET: + { + if (FindAffect(AFFECT_NOG_ABILITY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + return false; + } + long time = item->GetValue(0); + long moveSpeedPer = item->GetValue(1); + long attPer = item->GetValue(2); + long expPer = item->GetValue(3); + AddAffect(AFFECT_NOG_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true); + AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true); + AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true); + item->SetCount(item->GetCount() - 1); + } + break; + + case ITEM_RAMADAN_CANDY: + { + // @fixme147 BEGIN + if (FindAffect(AFFECT_RAMADAN_ABILITY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + return false; + } + // @fixme147 END + long time = item->GetValue(0); + long moveSpeedPer = item->GetValue(1); + long attPer = item->GetValue(2); + long expPer = item->GetValue(3); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true); + item->SetCount(item->GetCount() - 1); + } + break; + case ITEM_MARRIAGE_RING: + { + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID()); + if (pMarriage) + { + if (pMarriage->ch1 != NULL) + { + if (CArenaManager::instance().IsArenaMap(pMarriage->ch1->GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + break; + } + } + + if (pMarriage->ch2 != NULL) + { + if (CArenaManager::instance().IsArenaMap(pMarriage->ch2->GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + break; + } + } + + int consumeSP = CalculateConsumeSP(this); + + if (consumeSP < 0) + return false; + + PointChange(POINT_SP, -consumeSP, false); + + WarpToPID(pMarriage->GetOther(GetPlayerID())); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ° ƴϸ ȥ ϴ.")); + } + break; + + case UNIQUE_ITEM_CAPE_OF_COURAGE: + case 70057: + case REWARD_BOX_UNIQUE_ITEM_CAPE_OF_COURAGE: + AggregateMonster(); + item->SetCount(item->GetCount()-1); + break; + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + case ACCE_REVERSAL_VNUM_1: + case ACCE_REVERSAL_VNUM_2: + { + LPITEM item2; + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (!CleanAcceAttr(item, item2)) + return false; + item->SetCount(item->GetCount()-1); + break; + } +#endif + + case UNIQUE_ITEM_WHITE_FLAG: + ForgetMyAttacker(); + item->SetCount(item->GetCount()-1); + break; + + case UNIQUE_ITEM_TREASURE_BOX: + break; + + case 30093: + case 30094: + case 30095: + case 30096: + + { + const int MAX_BAG_INFO = 26; + static struct LuckyBagInfo + { + DWORD count; + int prob; + DWORD vnum; + } b1[MAX_BAG_INFO] = + { + { 1000, 302, 1 }, + { 10, 150, 27002 }, + { 10, 75, 27003 }, + { 10, 100, 27005 }, + { 10, 50, 27006 }, + { 10, 80, 27001 }, + { 10, 50, 27002 }, + { 10, 80, 27004 }, + { 10, 50, 27005 }, + { 1, 10, 50300 }, + { 1, 6, 92 }, + { 1, 2, 132 }, + { 1, 6, 1052 }, + { 1, 2, 1092 }, + { 1, 6, 2082 }, + { 1, 2, 2122 }, + { 1, 6, 3082 }, + { 1, 2, 3122 }, + { 1, 6, 5052 }, + { 1, 2, 5082 }, + { 1, 6, 7082 }, + { 1, 2, 7122 }, + { 1, 1, 11282 }, + { 1, 1, 11482 }, + { 1, 1, 11682 }, + { 1, 1, 11882 }, + }; + + LuckyBagInfo * bi = NULL; + bi = b1; + + int pct = number(1, 1000); + + int i; + for (i=0;i=MAX_BAG_INFO) + return false; + + if (bi[i].vnum == 50300) + { + GiveRandomSkillBook(); + } + else if (bi[i].vnum == 1) + { + PointChange(POINT_GOLD, 1000, true); + } + else + { + AutoGiveItem(bi[i].vnum, bi[i].count); + } + ITEM_MANAGER::instance().RemoveItem(item); + } + break; + + case 50004: + { + if (item->GetSocket(0)) + { + item->SetSocket(0, item->GetSocket(0) + 1); + } + else + { + int iMapIndex = GetMapIndex(); + + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRandomLocation(iMapIndex, pos, 700)) + { + item->SetSocket(0, 1); + item->SetSocket(1, pos.x); + item->SetSocket(2, pos.y); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̺Ʈ Ⱑ ʴ° ϴ.")); + return false; + } + } + + int dist = 0; + float distance = (DISTANCE_SQRT(GetX()-item->GetSocket(1), GetY()-item->GetSocket(2))); + + if (distance < 1000.0f) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̺Ʈ Ⱑ źο ϴ.")); + + struct TEventStoneInfo + { + DWORD dwVnum; + int count; + int prob; + }; + const int EVENT_STONE_MAX_INFO = 15; + TEventStoneInfo info_10[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 8 }, + { 27004, 10, 6 }, + { 27002, 10, 12 }, + { 27005, 10, 12 }, + { 27100, 1, 9 }, + { 27103, 1, 9 }, + { 27101, 1, 10 }, + { 27104, 1, 10 }, + { 27999, 1, 12 }, + + { 25040, 1, 4 }, + + { 27410, 1, 0 }, + { 27600, 1, 0 }, + { 25100, 1, 0 }, + + { 50001, 1, 0 }, + { 50003, 1, 1 }, + }; + TEventStoneInfo info_7[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 1 }, + { 27004, 10, 1 }, + { 27004, 10, 9 }, + { 27005, 10, 9 }, + { 27100, 1, 5 }, + { 27103, 1, 5 }, + { 27101, 1, 10 }, + { 27104, 1, 10 }, + { 27999, 1, 14 }, + + { 25040, 1, 5 }, + + { 27410, 1, 5 }, + { 27600, 1, 5 }, + { 25100, 1, 5 }, + + { 50001, 1, 0 }, + { 50003, 1, 5 }, + + }; + TEventStoneInfo info_4[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 0 }, + { 27004, 10, 0 }, + { 27002, 10, 0 }, + { 27005, 10, 0 }, + { 27100, 1, 0 }, + { 27103, 1, 0 }, + { 27101, 1, 0 }, + { 27104, 1, 0 }, + { 27999, 1, 25 }, + + { 25040, 1, 0 }, + + { 27410, 1, 0 }, + { 27600, 1, 0 }, + { 25100, 1, 15 }, + + { 50001, 1, 10 }, + { 50003, 1, 50 }, + + }; + + { + TEventStoneInfo* info; + if (item->GetSocket(0) <= 4) + info = info_4; + else if (item->GetSocket(0) <= 7) + info = info_7; + else + info = info_10; + + int prob = number(1, 100); + + for (int i = 0; i < EVENT_STONE_MAX_INFO; ++i) + { + if (!info[i].prob) + continue; + + if (prob <= info[i].prob) + { + AutoGiveItem(info[i].dwVnum, info[i].count); + break; + } + prob -= info[i].prob; + } + } + + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "StoneDetect %u 0 0", (DWORD)GetVID()); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + pack_chat.bEmpire = GetDesc()->GetEmpire(); + //pack_chat.id = vid; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + PacketAround(buf.read_peek(), buf.size()); + + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 1"); + return true; + } + else if (distance < 20000) + dist = 1; + else if (distance < 70000) + dist = 2; + else + dist = 3; + + const int STONE_DETECT_MAX_TRY = 10; + if (item->GetSocket(0) >= STONE_DETECT_MAX_TRY) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̺Ʈ Ⱑ ϴ.")); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 0"); + AutoGiveItem(27002); + return true; + } + + if (dist) + { + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), + "StoneDetect %u %d %d", + (DWORD)GetVID(), dist, (int)GetDegreeFromPositionXY(GetX(), item->GetSocket(2), item->GetSocket(1), GetY())); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + pack_chat.bEmpire = GetDesc()->GetEmpire(); + //pack_chat.id = vid; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + PacketAround(buf.read_peek(), buf.size()); + } + + } + break; + + case 27989: + case 76006: + { + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex()); + + if (pMap != NULL) + { + item->SetSocket(0, item->GetSocket(0) + 1); + + FFindStone f; + + // SECTREE::for_each -> SECTREE::for_each_entity + pMap->for_each(f); + + if (f.m_mapStone.size() > 0) + { + std::map::iterator stone = f.m_mapStone.begin(); + + DWORD max = UINT_MAX; + LPCHARACTER pTarget = stone->second; + + while (stone != f.m_mapStone.end()) + { + DWORD dist = (DWORD)DISTANCE_SQRT(GetX()-stone->second->GetX(), GetY()-stone->second->GetY()); + + if (dist != 0 && max > dist) + { + max = dist; + pTarget = stone->second; + } + stone++; + } + + if (pTarget != NULL) + { + int val = 3; + + if (max < 10000) val = 2; + else if (max < 70000) val = 1; + + ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u %d %d", (DWORD)GetVID(), val, + (int)GetDegreeFromPositionXY(GetX(), pTarget->GetY(), pTarget->GetX(), GetY())); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("⸦ ۿϿ Ǵ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("⸦ ۿϿ Ǵ ϴ.")); + } + + if (item->GetSocket(0) >= 6) + { + ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u 0 0", (DWORD)GetVID()); + ITEM_MANAGER::instance().RemoveItem(item); + } + } + break; + } + break; + + case 27996: + item->SetCount(item->GetCount() - 1); + AttackedByPoison(NULL); // @warme008 + break; + + case 27987: + + { + item->SetCount(item->GetCount() - 1); + + int r = number(1, 100); + + if (r <= 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Խϴ.")); + AutoGiveItem(27990); + } + else + { + const int prob_table_gb2312[] = + { + 95, 97, 99 + }; + + const int * prob_table = prob_table_gb2312; + + if (r <= prob_table[0]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + else if (r <= prob_table[1]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ְ Խϴ.")); + AutoGiveItem(27992); + } + else if (r <= prob_table[2]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ûְ Խϴ.")); + AutoGiveItem(27993); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ְ Խϴ.")); + AutoGiveItem(27994); + } + } + } + break; + + case 71013: + CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), this); + item->SetCount(item->GetCount() - 1); + break; + + case 50100: + case 50101: + case 50102: + case 50103: + case 50104: + case 50105: + case 50106: + CreateFly(item->GetVnum() - 50100 + FLY_FIREWORK1, this); + item->SetCount(item->GetCount() - 1); + break; + + case 50200: + if (g_bEnableBootaryCheck) + { + if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true) + { + __OpenPrivateShop(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ")); + } + } + else + { + __OpenPrivateShop(); + } + break; + + case fishing::FISH_MIND_PILL_VNUM: + AddAffect(AFFECT_FISH_MIND_PILL, POINT_NONE, 0, AFF_FISH_MIND, 20*60, 0, true); + item->SetCount(item->GetCount() - 1); + break; + + case 50301: + case 50302: + case 50303: + { + if (IsPolymorphed() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return false; + } + + int lv = GetSkillLevel(SKILL_LEADERSHIP); + + if (lv < item->GetValue(0)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ʹ ϱⰡ ϴ.")); + return false; + } + + if (lv >= item->GetValue(1)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ƹ ʽϴ.")); + return false; + } + + if (LearnSkillByBook(SKILL_LEADERSHIP)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(SKILL_LEADERSHIP, get_global_time() + iReadDelay); + } + } + break; + + case 50304: + case 50305: + case 50306: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + if (GetSkillLevel(SKILL_COMBO) == 0 && GetLevel() < 30) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 30 DZ ʽϴ.")); + return false; + } + + if (GetSkillLevel(SKILL_COMBO) == 1 && GetLevel() < 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 50 DZ ʽϴ.")); + return false; + } + + if (GetSkillLevel(SKILL_COMBO) >= 2) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + int iPct = item->GetValue(0); + + if (LearnSkillByBook(SKILL_COMBO, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(SKILL_COMBO, get_global_time() + iReadDelay); + } + } + break; + case 50311: + case 50312: + case 50313: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = item->GetValue(0); + int iPct = MINMAX(0, item->GetValue(1), 100); + if (GetSkillLevel(dwSkillVnum)>=20 || dwSkillVnum-SKILL_LANGUAGE1+1 == GetEmpire()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ϻϰ ˾Ƶ ִ ̴.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50061 : + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = item->GetValue(0); + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum) >= 10) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50314: case 50315: case 50316: + case 50323: case 50324: + case 50325: case 50326: + { + if (IsPolymorphed() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return false; + } + + int iSkillLevelLowLimit = item->GetValue(0); + int iSkillLevelHighLimit = item->GetValue(1); + int iPct = MINMAX(0, item->GetValue(2), 100); + int iLevelLimit = item->GetValue(3); + DWORD dwSkillVnum = 0; + + switch (item->GetVnum()) + { + case 50314: case 50315: case 50316: + dwSkillVnum = SKILL_POLYMORPH; + break; + + case 50323: case 50324: + dwSkillVnum = SKILL_ADD_HP; + break; + + case 50325: case 50326: + dwSkillVnum = SKILL_RESIST_PENETRATE; + break; + + default: + return false; + } + + if (0 == dwSkillVnum) + return false; + + if (GetLevel() < iLevelLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ÷ մϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) >= 40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) < iSkillLevelLowLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ʹ ϱⰡ ϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) >= iSkillLevelHighLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" åδ ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50902: + case 50903: + case 50904: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_CREATE; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum)>=40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Success to learn skill "); + } + } + else + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Failed to learn skill "); + } + } + } + break; + + // MINING + case ITEM_MINING_SKILL_TRAIN_BOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_MINING; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum)>=40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + // END_OF_MINING + + case ITEM_HORSE_SKILL_TRAIN_BOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_HORSE; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetLevel() < 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¸ ų ִ ƴմϴ.")); + return false; + } + + if (!test_server && get_global_time() < GetSkillNextReadTime(dwSkillVnum)) + { + if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY)) + { + RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("־ȼ ȭԸ Խϴ.")); + } + else + { + SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time()); + return false; + } + } + + if (GetPoint(POINT_HORSE_SKILL) >= 20 || + GetSkillLevel(SKILL_HORSE_WILDATTACK) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60 || + GetSkillLevel(SKILL_HORSE_WILDATTACK_RANGE) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ¸ ü ϴ.")); + return false; + } + + if (number(1, 100) <= iPct) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¸ ü о ¸ ų Ʈ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ʈδ ¸ ų ø ֽϴ.")); + PointChange(POINT_HORSE_SKILL, 1); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + if (!test_server) + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¸ ü ؿ Ͽϴ.")); + } +#ifdef ENABLE_BOOKS_STACKFIX + item->SetCount(item->GetCount() - 1); +#else + ITEM_MANAGER::instance().RemoveItem(item); +#endif + } + break; + + case 70102: + case 70103: + { + if (GetAlignment() >= 0) + return false; + + int delta = MIN(-GetAlignment(), item->GetValue(0)); + + sys_log(0, "%s ALIGNMENT ITEM %d", GetName(), delta); + + UpdateAlignment(delta); + item->SetCount(item->GetCount() - 1); + + if (delta / 10 > 0) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ±. 𰡰 ̾.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ %d Ͽϴ."), delta/10); + } + } + break; + + case 71107: + case 39032: // @fixme169 mythical peach alternative vnum + { + int val = item->GetValue(0); + int interval = item->GetValue(1); + quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID()); + if (!pPC) // @fixme169 missing check + return false; + int last_use_time = pPC->GetFlag("mythical_peach.last_use_time"); + + if (get_global_time() - last_use_time < interval * 60 * 60) + { + if (test_server == false) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׽Ʈ ð ")); + } + } + + if (GetAlignment() == 200000) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ ̻ ø ϴ.")); + return false; + } + + if (200000 - GetAlignment() < val * 10) + { + val = (200000 - GetAlignment()) / 10; + } + + int old_alignment = GetAlignment() / 10; + + UpdateAlignment(val*10); + + item->SetCount(item->GetCount()-1); + pPC->SetFlag("mythical_peach.last_use_time", get_global_time()); + + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ±. 𰡰 ̾.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ %d Ͽϴ."), val); + + char buf[256 + 1]; + snprintf(buf, sizeof(buf), "%d %d", old_alignment, GetAlignment() / 10); + LogManager::instance().CharLog(this, val, "MYTHICAL_PEACH", buf); + } + break; + + case 71109: + case 72719: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetSocketCount() == 0) + return false; + + switch( item2->GetType() ) + { + case ITEM_WEAPON: + break; + case ITEM_ARMOR: + switch (item2->GetSubType()) + { + case ARMOR_EAR: + case ARMOR_WRIST: + case ARMOR_NECK: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + return false; + } + break; + + default: + return false; + } + + std::stack socket; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + socket.push(item2->GetSocket(i)); + + int idx = ITEM_SOCKET_MAX_NUM - 1; + + while (socket.size() > 0) + { + if (socket.top() > 2 && socket.top() != ITEM_BROKEN_METIN_VNUM) + break; + + idx--; + socket.pop(); + } + + if (socket.size() == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + return false; + } + + LPITEM pItemReward = AutoGiveItem(socket.top()); + + if (pItemReward != NULL) + { + item2->SetSocket(idx, 1); + + char buf[256+1]; + snprintf(buf, sizeof(buf), "%s(%u) %s(%u)", + item2->GetName(), item2->GetID(), pItemReward->GetName(), pItemReward->GetID()); + LogManager::instance().ItemLog(this, item, "USE_DETACHMENT_ONE", buf); + + item->SetCount(item->GetCount() - 1); + } + } + break; + + case 70201: + case 70202: + case 70203: + case 70204: + case 70205: + case 70206: + { + // NEW_HAIR_STYLE_ADD + if (GetPart(PART_HAIR) >= 1001) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ŸϿ Ż Ұմϴ.")); + } + // END_NEW_HAIR_STYLE_ADD + else + { + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC) + { + int last_dye_level = pPC->GetFlag("dyeing_hair.last_dye_level"); + + if (last_dye_level == 0 || + last_dye_level+3 <= GetLevel() || + item->GetVnum() == 70201) + { + SetPart(PART_HAIR, item->GetVnum() - 70201); + + if (item->GetVnum() == 70201) + pPC->SetFlag("dyeing_hair.last_dye_level", 0); + else + pPC->SetFlag("dyeing_hair.last_dye_level", GetLevel()); + + item->SetCount(item->GetCount() - 1); + UpdatePacket(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d Ǿ ٽ Ͻ ֽϴ."), last_dye_level+3); + } + } + } + } + break; + + case ITEM_NEW_YEAR_GREETING_VNUM: + { + DWORD dwBoxVnum = ITEM_NEW_YEAR_GREETING_VNUM; + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets; + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + for (int i = 0; i < count; i++) + { + if (dwVnums[i] == CSpecialItemGroup::GOLD) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + } + + item->SetCount(item->GetCount() - 1); + } + } + break; + + case ITEM_VALENTINE_ROSE: + case ITEM_VALENTINE_CHOCOLATE: + { + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(0); + int count = 0; + + if (((item->GetVnum() == ITEM_VALENTINE_ROSE) && (SEX_MALE==GET_SEX(this))) || + ((item->GetVnum() == ITEM_VALENTINE_CHOCOLATE) && (SEX_FEMALE==GET_SEX(this)))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + item->SetCount(item->GetCount()-1); + } + break; + + case ITEM_WHITEDAY_CANDY: + case ITEM_WHITEDAY_ROSE: + { + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(0); + int count = 0; + + if (((item->GetVnum() == ITEM_WHITEDAY_CANDY) && (SEX_MALE==GET_SEX(this))) || + ((item->GetVnum() == ITEM_WHITEDAY_ROSE) && (SEX_FEMALE==GET_SEX(this)))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + item->SetCount(item->GetCount()-1); + } + break; + + case 50011: + { + DWORD dwBoxVnum = 50011; + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(0); + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + for (int i = 0; i < count; i++) + { + char buf[50 + 1]; + snprintf(buf, sizeof(buf), "%u %u", dwVnums[i], dwCounts[i]); + LogManager::instance().ItemLog(this, item, "MOONLIGHT_GET", buf); + + //ITEM_MANAGER::instance().RemoveItem(item); + item->SetCount(item->GetCount() - 1); + + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case CSpecialItemGroup::BLEEDING: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; +#endif + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + } + break; + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ƹ͵ ϴ.")); + return false; + } + } + break; + + case ITEM_GIVE_STAT_RESET_COUNT_VNUM: + { + //PointChange(POINT_GOLD, -iCost); + PointChange(POINT_STAT_RESET_COUNT, 1); + item->SetCount(item->GetCount()-1); + } + break; + + case 50107: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + EffectPacket(SE_CHINA_FIREWORK); +#ifdef ENABLE_FIREWORK_STUN + + AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true); +#endif + item->SetCount(item->GetCount()-1); + } + break; + + case 50108: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + EffectPacket(SE_SPIN_TOP); +#ifdef ENABLE_FIREWORK_STUN + + AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true); +#endif + item->SetCount(item->GetCount()-1); + } + break; + + case ITEM_WONSO_BEAN_VNUM: + PointChange(POINT_HP, GetMaxHP() - GetHP()); + item->SetCount(item->GetCount()-1); + break; + + case ITEM_WONSO_SUGAR_VNUM: + PointChange(POINT_SP, GetMaxSP() - GetSP()); + item->SetCount(item->GetCount()-1); + break; + + case ITEM_WONSO_FRUIT_VNUM: + PointChange(POINT_STAMINA, GetMaxStamina()-GetStamina()); + item->SetCount(item->GetCount()-1); + break; + + case ITEM_ELK_VNUM: + { + int iGold = item->GetSocket(0); + ITEM_MANAGER::instance().RemoveItem(item); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), iGold); + PointChange(POINT_GOLD, iGold); + } + break; + + case 70021: + { + int HealPrice = quest::CQuestManager::instance().GetEventFlag("MonarchHealGold"); + if (HealPrice == 0) + HealPrice = 2000000; + + if (CMonarch::instance().HealMyEmpire(this, HealPrice)) + { + char szNotice[256]; + snprintf(szNotice, sizeof(szNotice), LC_TEXT(" ູ %s HP,SP äϴ."), EMPIRE_NAME(GetEmpire())); + SendNoticeMap(szNotice, GetMapIndex(), false); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ູ Ͽϴ.")); + } + } + break; + + case 27995: + { + } + break; + + case 71092 : + { + if (m_pkChrTarget != NULL) + { + if (m_pkChrTarget->IsPolymorphed()) + { + m_pkChrTarget->SetPolymorph(0); + m_pkChrTarget->RemoveAffect(AFFECT_POLYMORPH); + } + } + else + { + if (IsPolymorphed()) + { + SetPolymorph(0); + RemoveAffect(AFFECT_POLYMORPH); + } + } + } + break; + + case 71051 : + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetInventoryItem(wDestCell))) + return false; + + if (ITEM_COSTUME == item2->GetType()) // @fixme124 + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + +#ifdef ENABLE_ITEM_RARE_ATTR_LEVEL_PCT + if (item2->AddRareAttribute2()) +#else + if (item2->AddRareAttribute()) +#endif + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӽ ߰ Ǿϴ")); + + int iAddedIdx = item2->GetRareAttrCount() + 4; + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_RARE_ATTR", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ Ӽ ߰ ϴ")); + } + } + break; + + case 71052 : + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (ITEM_COSTUME == item2->GetType()) // @fixme124 + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + +#ifdef ENABLE_ITEM_RARE_ATTR_LEVEL_PCT + if (item2->ChangeRareAttribute2()) +#else + if (item2->ChangeRareAttribute()) +#endif + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CHANGE_RARE_ATTR", buf); + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų Ӽ ϴ")); + } + } + break; + + case ITEM_AUTO_HP_RECOVERY_S: + case ITEM_AUTO_HP_RECOVERY_M: + case ITEM_AUTO_HP_RECOVERY_L: + case ITEM_AUTO_HP_RECOVERY_X: + case ITEM_AUTO_SP_RECOVERY_S: + case ITEM_AUTO_SP_RECOVERY_M: + case ITEM_AUTO_SP_RECOVERY_L: + case ITEM_AUTO_SP_RECOVERY_X: + + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + EAffectTypes type = AFFECT_NONE; + bool isSpecialPotion = false; + + switch (item->GetVnum()) + { + case ITEM_AUTO_HP_RECOVERY_X: + isSpecialPotion = true; + + case ITEM_AUTO_HP_RECOVERY_S: + case ITEM_AUTO_HP_RECOVERY_M: + case ITEM_AUTO_HP_RECOVERY_L: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S: + type = AFFECT_AUTO_HP_RECOVERY; + break; + + case ITEM_AUTO_SP_RECOVERY_X: + isSpecialPotion = true; + + case ITEM_AUTO_SP_RECOVERY_S: + case ITEM_AUTO_SP_RECOVERY_M: + case ITEM_AUTO_SP_RECOVERY_L: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S: + type = AFFECT_AUTO_SP_RECOVERY; + break; + } + + if (AFFECT_NONE == type) + break; + + if (item->GetCount() > 1) + { + int pos = GetEmptyInventory(item->GetSize()); + + if (-1 == pos) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + break; + } + + item->SetCount( item->GetCount() - 1 ); + + LPITEM item2 = ITEM_MANAGER::instance().CreateItem( item->GetVnum(), 1 ); + item2->AddToCharacter(this, TItemPos(INVENTORY, pos)); + + if (item->GetSocket(1) != 0) + { + item2->SetSocket(1, item->GetSocket(1)); + } + + item = item2; + } + + CAffect* pAffect = FindAffect( type ); + + if (NULL == pAffect) + { + EPointTypes bonus = POINT_NONE; + + if (true == isSpecialPotion) + { + if (type == AFFECT_AUTO_HP_RECOVERY) + { + bonus = POINT_MAX_HP_PCT; + } + else if (type == AFFECT_AUTO_SP_RECOVERY) + { + bonus = POINT_MAX_SP_PCT; + } + } + + AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false); + + item->Lock(true); + item->SetSocket(0, true); + + AutoRecoveryItemProcess( type ); + } + else + { + if (item->GetID() == pAffect->dwFlag) + { + RemoveAffect( pAffect ); + + item->Lock(false); + item->SetSocket(0, false); + } + else + { + LPITEM old = FindItemByID( pAffect->dwFlag ); + + if (NULL != old) + { + old->Lock(false); + old->SetSocket(0, false); + } + + RemoveAffect( pAffect ); + + EPointTypes bonus = POINT_NONE; + + if (true == isSpecialPotion) + { + if (type == AFFECT_AUTO_HP_RECOVERY) + { + bonus = POINT_MAX_HP_PCT; + } + else if (type == AFFECT_AUTO_SP_RECOVERY) + { + bonus = POINT_MAX_SP_PCT; + } + } + + AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false); + + item->Lock(true); + item->SetSocket(0, true); + + AutoRecoveryItemProcess( type ); + } + } + } + break; + } + break; + + case USE_CLEAR: + { + switch (item->GetVnum()) + { +#ifdef ENABLE_WOLFMAN_CHARACTER + case 27124: // Bandage + RemoveBleeding(); + break; +#endif + case 27874: // Grilled Perch + default: + RemoveBadAffect(); + break; + } + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_INVISIBILITY: + { + if (item->GetVnum() == 70026) + { + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC != NULL) + { + int last_use_time = pPC->GetFlag("mirror_of_disapper.last_use_time"); + + if (get_global_time() - last_use_time < 10*60) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + pPC->SetFlag("mirror_of_disapper.last_use_time", get_global_time()); + } + } + + AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, 300, 0, true); + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_POTION_NODELAY: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 70020 : + case 71018 : + case 71019 : + case 71020 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + bool used = false; + + if (item->GetValue(0) != 0) + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(1) != 0) + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (item->GetValue(3) != 0) + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(4) != 0) + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (used) + { + if (item->GetVnum() == 50085 || item->GetVnum() == 50086) + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǵ Ͽϴ")); + SetUseSeedOrMoonBottleTime(); + } + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + m_nPotionLimit--; + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + item->SetCount(item->GetCount() - 1); + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + } + } + break; + + case USE_POTION: + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 27001 : + case 27002 : + case 27003 : + case 27004 : + case 27005 : + case 27006 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + if (item->GetValue(1) != 0) + { + if (GetPoint(POINT_SP_RECOVERY) + GetSP() >= GetMaxSP()) + { + return false; + } + + PointChange(POINT_SP_RECOVERY, item->GetValue(1) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100); + StartAffectEvent(); + EffectPacket(SE_SPUP_BLUE); + } + + if (item->GetValue(0) != 0) + { + if (GetPoint(POINT_HP_RECOVERY) + GetHP() >= GetMaxHP()) + { + return false; + } + + PointChange(POINT_HP_RECOVERY, item->GetValue(0) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100); + StartAffectEvent(); + EffectPacket(SE_HPUP_RED); + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + m_nPotionLimit--; + break; + + case USE_POTION_CONTINUE: + { + if (item->GetValue(0) != 0) + { + AddAffect(AFFECT_HP_RECOVER_CONTINUE, POINT_HP_RECOVER_CONTINUE, item->GetValue(0), 0, item->GetValue(2), 0, true); + } + else if (item->GetValue(1) != 0) + { + AddAffect(AFFECT_SP_RECOVER_CONTINUE, POINT_SP_RECOVER_CONTINUE, item->GetValue(1), 0, item->GetValue(2), 0, true); + } + else + return false; + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_ABILITY_UP: + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(AFFECT_MOV_SPEED, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true); +#ifdef ENABLE_EFFECT_EXTRAPOT + EffectPacket(SE_DXUP_PURPLE); +#endif + break; + + case APPLY_ATT_SPEED: + AddAffect(AFFECT_ATT_SPEED, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true); +#ifdef ENABLE_EFFECT_EXTRAPOT + EffectPacket(SE_SPEEDUP_GREEN); +#endif + break; + + case APPLY_STR: + AddAffect(AFFECT_STR, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_DEX: + AddAffect(AFFECT_DEX, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_CON: + AddAffect(AFFECT_CON, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_INT: + AddAffect(AFFECT_INT, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_CAST_SPEED: + AddAffect(AFFECT_CAST_SPEED, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_ATT_GRADE_BONUS: + AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_DEF_GRADE_BONUS: + AddAffect(AFFECT_DEF_GRADE, POINT_DEF_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_TALISMAN: + { + const int TOWN_PORTAL = 1; + const int MEMORY_PORTAL = 2; + + if (GetMapIndex() == 200 || GetMapIndex() == 113) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ ϴ.")); + return false; + } + + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + else if (g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(item->GetVnum())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } +#endif + + if (m_pkWarpEvent) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̵ غ Ǿ ȯθ Ҽ ϴ")); + return false; + } + + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + int consumeLife = CalculateConsume(this); + + if (consumeLife < 0) + return false; + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + if (item->GetValue(0) == TOWN_PORTAL) + { + if (item->GetSocket(0) == 0) + { + if (!GetDungeon()) + if (!GiveRecallItem(item)) + return false; + + PIXEL_POSITION posWarp; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp)) + { + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + PointChange(POINT_HP, -consumeLife, false); + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + WarpSet(posWarp.x, posWarp.y); + } + else + { + sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", GetName(), GetX(), GetY()); + } + } + else + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ ")); + + ProcessRecallItem(item); + } + } + else if (item->GetValue(0) == MEMORY_PORTAL) + { + if (item->GetSocket(0) == 0) + { + if (GetDungeon()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȿ %s%s ϴ."), + item->GetName(), + ""); + return false; + } + + if (!GiveRecallItem(item)) + return false; + } + else + { + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + PointChange(POINT_HP, -consumeLife, false); + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + ProcessRecallItem(item); + } + } + } + break; + + case USE_TUNING: + case USE_DETACHMENT: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetVnum() >= 28330 && item2->GetVnum() <= 28343) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("+3 ϴ")); + return false; + } + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + if (item->GetValue(0) == ACCE_CLEAN_ATTR_VALUE0 + || item->GetVnum() == ACCE_REVERSAL_VNUM_1 + || item->GetVnum() == ACCE_REVERSAL_VNUM_2 + ) + { + if (!CleanAcceAttr(item, item2)) + return false; + item->SetCount(item->GetCount()-1); + return true; + } +#endif + + if (item2->GetVnum() >= 28430 && item2->GetVnum() <= 28443) + { + if (item->GetVnum() == 71056) + { + RefineItem(item, item2); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + } + } + else + { + RefineItem(item, item2); + } + } + break; + + case USE_CHANGE_COSTUME_ATTR: + case USE_RESET_COSTUME_ATTR: + { + LPITEM item2; + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsEquipped()) + { + BuffOnAttr_RemoveBuffsFromItem(item2); + } + + if (ITEM_COSTUME != item2->GetType()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӽ ϴ.")); + return false; + } + + switch (item->GetSubType()) + { + case USE_CHANGE_COSTUME_ATTR: + item2->ChangeAttribute(); + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CHANGE_COSTUME_ATTR", buf); + } + break; + case USE_RESET_COSTUME_ATTR: + item2->ClearAttribute(); + item2->AlterToMagicItem(); + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "RESET_COSTUME_ATTR", buf); + } + break; + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Ͽϴ.")); + + item->SetCount(item->GetCount() - 1); + break; + } + + // ACCESSORY_REFINE & ADD/CHANGE_ATTRIBUTES + case USE_PUT_INTO_BELT_SOCKET: + case USE_PUT_INTO_RING_SOCKET: + case USE_PUT_INTO_ACCESSORY_SOCKET: + case USE_ADD_ACCESSORY_SOCKET: + case USE_CLEAN_SOCKET: + case USE_CHANGE_ATTRIBUTE: + case USE_CHANGE_ATTRIBUTE2 : + case USE_ADD_ATTRIBUTE: + case USE_ADD_ATTRIBUTE2: + { + LPITEM item2; + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsEquipped()) + { + BuffOnAttr_RemoveBuffsFromItem(item2); + } + + if (ITEM_COSTUME == item2->GetType()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + switch (item->GetSubType()) + { + case USE_CLEAN_SOCKET: + { + int i; + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item2->GetSocket(i) == ITEM_BROKEN_METIN_VNUM) + break; + } + + if (i == ITEM_SOCKET_MAX_NUM) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("û ʽϴ.")); + return false; + } + + int j = 0; + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item2->GetSocket(i) != ITEM_BROKEN_METIN_VNUM && item2->GetSocket(i) != 0) + item2->SetSocket(j++, item2->GetSocket(i)); + } + + for (; j < ITEM_SOCKET_MAX_NUM; ++j) + { + if (item2->GetSocket(j) > 0) + item2->SetSocket(j, 1); + } + + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CLEAN_SOCKET", buf); + } + + item->SetCount(item->GetCount() - 1); + + } + break; + + case USE_CHANGE_ATTRIBUTE : + case USE_CHANGE_ATTRIBUTE2 : // @fixme123 + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӽ ϴ.")); + return false; + } + + if ((GM_PLAYER == GetGMLevel()) && (false == test_server) && (g_dwItemBonusChangeTime > 0)) + { + DWORD dwChangeItemAttrCycle = g_dwItemBonusChangeTime; + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID()); + + if (pPC) + { + DWORD dwNowSec = get_global_time(); + + DWORD dwLastChangeItemAttrSec = pPC->GetFlag(msc_szLastChangeItemAttrFlag); + + if (dwLastChangeItemAttrSec + dwChangeItemAttrCycle > dwNowSec) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ٲ %d ̳ ٽ ϴ.(%d )"), + dwChangeItemAttrCycle, dwChangeItemAttrCycle - (dwNowSec - dwLastChangeItemAttrSec)); + return false; + } + + pPC->SetFlag(msc_szLastChangeItemAttrFlag, dwNowSec); + } + } + + if (item->GetSubType() == USE_CHANGE_ATTRIBUTE2) + { + int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] = + { + 0, 0, 30, 40, 3 + }; + + item2->ChangeAttribute(aiChangeProb); + } + else if (item->GetVnum() == 76014) + { + int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] = + { + 0, 10, 50, 39, 1 + }; + + item2->ChangeAttribute(aiChangeProb); + } + + else + { + if (item->GetVnum() == 71151 || item->GetVnum() == 76023) + { + if ((item2->GetType() == ITEM_WEAPON) + || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY)) + { + bool bCanUse = true; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40) + { + bCanUse = false; + break; + } + } + if (false == bCanUse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ұմϴ.")); + break; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ մϴ.")); + break; + } + } + item2->ChangeAttribute(); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Ͽϴ.")); + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CHANGE_ATTRIBUTE", buf); + } + + item->SetCount(item->GetCount() - 1); + break; + + case USE_ADD_ATTRIBUTE : + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() < 4) + { + if (item->GetVnum() == 71152 || item->GetVnum() == 76024) + { + if ((item2->GetType() == ITEM_WEAPON) + || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY)) + { + bool bCanUse = true; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40) + { + bCanUse = false; + break; + } + } + if (false == bCanUse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ұմϴ.")); + break; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ մϴ.")); + break; + } + } + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()]) + { + item2->AddAttribute(); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + + int iAddedIdx = item2->GetAttributeCount() - 1; + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_ATTRIBUTE_SUCCESS", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̻ ̿Ͽ Ӽ ߰ ϴ.")); + } + break; + + case USE_ADD_ATTRIBUTE2 : + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() == 4) + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()]) + { + item2->AddAttribute(); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + + int iAddedIdx = item2->GetAttributeCount() - 1; + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_ATTRIBUTE2_SUCCESS", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE2_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else if (item2->GetAttributeCount() == 5) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ̿Ͽ Ӽ ߰ ϴ.")); + } + else if (item2->GetAttributeCount() < 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 簡񼭸 ̿Ͽ Ӽ ߰ ּ.")); + } + else + { + // wtf ?! + sys_err("ADD_ATTRIBUTE2 : Item has wrong AttributeCount(%d)", item2->GetAttributeCount()); + } + break; + + case USE_ADD_ACCESSORY_SOCKET: + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (item2->IsAccessoryForSocket()) + { + if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM) + { +#ifdef ENABLE_ADDSTONE_FAILURE + if (number(1, 100) <= 50) +#else + if (1) +#endif + { + item2->SetAccessorySocketMaxGrade(item2->GetAccessorySocketMaxGrade() + 1); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰Ǿϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_SOCKET_SUCCESS", buf); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_SOCKET_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ߰ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ Դϴ.")); + } + } + break; + + case USE_PUT_INTO_BELT_SOCKET: + case USE_PUT_INTO_ACCESSORY_SOCKET: + if (item2->IsAccessoryForSocket() && item->CanPutInto(item2)) + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (item2->GetAccessorySocketGrade() < item2->GetAccessorySocketMaxGrade()) + { + if (number(1, 100) <= aiAccessorySocketPutPct[item2->GetAccessorySocketGrade()]) + { + item2->SetAccessorySocketGrade(item2->GetAccessorySocketGrade() + 1); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "PUT_SOCKET_SUCCESS", buf); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "PUT_SOCKET_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + if (item2->GetAccessorySocketMaxGrade() == 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̾Ƹ Ǽ ߰ؾմϴ.")); + else if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̾Ƹ ߰ؾմϴ.")); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + } + if (item2->IsEquipped()) + { + BuffOnAttr_AddBuffsFromItem(item2); + } + } + break; + // END_OF_ACCESSORY_REFINE & END_OF_ADD_ATTRIBUTES & END_OF_CHANGE_ATTRIBUTES + + case USE_BAIT: + { + if (m_pkFishingEvent) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̳ Ƴ ϴ.")); + return false; + } + + LPITEM weapon = GetWear(WEAR_WEAPON); + + if (!weapon || weapon->GetType() != ITEM_ROD) + return false; + + if (weapon->GetSocket(2)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ִ ̳ %s ϴ."), item->GetName()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ô뿡 %s ̳ ϴ."), item->GetName()); + } + + weapon->SetSocket(2, item->GetValue(0)); + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_MOVE: + case USE_TREASURE_BOX: + case USE_MONEYBAG: + break; + + case USE_AFFECT : + { + if (FindAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + AddAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false); + item->SetCount(item->GetCount() - 1); + } + } + break; + + case USE_CREATE_STONE: + AutoGiveItem(number(28000, 28013)); + item->SetCount(item->GetCount() - 1); + break; + + case USE_RECIPE : + { + LPITEM pSource1 = FindSpecifyItem(item->GetValue(1)); + DWORD dwSourceCount1 = item->GetValue(2); + + LPITEM pSource2 = FindSpecifyItem(item->GetValue(3)); + DWORD dwSourceCount2 = item->GetValue(4); + + if (dwSourceCount1 != 0) + { + if (pSource1 == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ᰡ մϴ.")); + return false; + } + } + + if (dwSourceCount2 != 0) + { + if (pSource2 == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ᰡ մϴ.")); + return false; + } + } + + if (pSource1 != NULL) + { + if (pSource1->GetCount() < dwSourceCount1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) մϴ."), pSource1->GetName()); + return false; + } + + pSource1->SetCount(pSource1->GetCount() - dwSourceCount1); + } + + if (pSource2 != NULL) + { + if (pSource2->GetCount() < dwSourceCount2) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) մϴ."), pSource2->GetName()); + return false; + } + + pSource2->SetCount(pSource2->GetCount() - dwSourceCount2); + } + + LPITEM pBottle = FindSpecifyItem(50901); + + if (!pBottle || pBottle->GetCount() < 1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ڸϴ.")); + return false; + } + + pBottle->SetCount(pBottle->GetCount() - 1); + + if (number(1, 100) > item->GetValue(5)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + return false; + } + + AutoGiveItem(item->GetValue(0)); + } + break; + } + } + break; + + case ITEM_METIN: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() || item2->IsEquipped()) // @fixme114 + return false; + + if (item2->GetType() == ITEM_PICK) return false; + if (item2->GetType() == ITEM_ROD) return false; + + int i; + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + DWORD dwVnum; + + if ((dwVnum = item2->GetSocket(i)) <= 2) + continue; + + TItemTable * p = ITEM_MANAGER::instance().GetTable(dwVnum); + + if (!p) + continue; + + if (item->GetValue(5) == p->alValues[5]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ϴ.")); + return false; + } + } + + if (item2->GetType() == ITEM_ARMOR) + { + if (!IS_SET(item->GetWearFlag(), WEARABLE_BODY) || !IS_SET(item2->GetWearFlag(), WEARABLE_BODY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ϴ.")); + return false; + } + } + else if (item2->GetType() == ITEM_WEAPON) + { + if (!IS_SET(item->GetWearFlag(), WEARABLE_WEAPON)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ⿡ ϴ.")); + return false; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + if (item2->GetSocket(i) >= 1 && item2->GetSocket(i) <= 2 && item2->GetSocket(i) >= item->GetValue(2)) + { +#ifdef ENABLE_ADDSTONE_FAILURE + if (number(1, 100) <= 30) +#else + if (1) +#endif + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ƾ Ͽϴ.")); + item2->SetSocket(i, item->GetVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ƾ Ͽϴ.")); + item2->SetSocket(i, ITEM_BROKEN_METIN_VNUM); + } + + LogManager::instance().ItemLog(this, item2, "SOCKET", item->GetName()); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (METIN)"); + break; + } + + if (i == ITEM_SOCKET_MAX_NUM) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + } + break; + + case ITEM_AUTOUSE: + case ITEM_MATERIAL: + case ITEM_SPECIAL: + case ITEM_TOOL: + case ITEM_LOTTERY: + break; + + case ITEM_TOTEM: + { + if (!item->IsEquipped()) + EquipItem(item); + } + break; + + case ITEM_BLEND: + + sys_log(0,"ITEM_BLEND!!"); + if (Blend_Item_find(item->GetVnum())) + { + int affect_type = AFFECT_BLEND; + int apply_type = aApplyInfo[item->GetSocket(0)].bPointType; + int apply_value = item->GetSocket(1); + int apply_duration = item->GetSocket(2); + + if (FindAffect(affect_type, apply_type)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, POINT_RESIST_MAGIC)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, false); + item->SetCount(item->GetCount() - 1); + } + } + } + break; + case ITEM_EXTRACT: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + switch (item->GetSubType()) + { + case EXTRACT_DRAGON_SOUL: + if (pDestItem->IsDragonSoul()) + { + return DSManager::instance().PullOut(this, NPOS, pDestItem, item); + } + return false; + case EXTRACT_DRAGON_HEART: + if (pDestItem->IsDragonSoul()) + { + return DSManager::instance().ExtractDragonHeart(this, pDestItem, item); + } + return false; + default: + return false; + } + } + break; + + case ITEM_NONE: + sys_err("Item type NONE %s", item->GetName()); + break; + + default: + sys_log(0, "UseItemEx: Unknown type %s %d", item->GetName(), item->GetType()); + return false; + } + + return true; +} + +int g_nPortalLimitTime = 10; + +bool CHARACTER::UseItem(TItemPos Cell, TItemPos DestCell) +{ + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + //WORD wDestCell = DestCell.cell; + //BYTE bDestInven = DestCell.window_type; + LPITEM item; + + if (!CanHandleItem()) + return false; + + if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) + return false; + + sys_log(0, "%s: USE_ITEM %s (inven %d, cell: %d)", GetName(), item->GetName(), window_type, wCell); + + if (item->IsExchanging()) + return false; + + if (!item->CanUsedBy(this)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + if (IsStun()) + return false; + + if (false == FN_check_item_sex(this, item)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + //PREVENT_TRADE_WINDOW + if (IS_SUMMON_ITEM(item->GetVnum())) + { + if (false == IS_SUMMONABLE_ZONE(GetMapIndex())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ҽ ϴ.")); + return false; + } + + if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ÿ ߿ ȯ,ȯθ Ҽ ϴ.")); + return false; + } + int iPulse = thecore_pulse(); + + if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("â %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(g_nPortalLimitTime)); + return false; + } + + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ȯ,ȯ Ҽ ϴ.")); + return false; + } + + //PREVENT_REFINE_HACK + + { + if (iPulse - GetRefineTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + return false; + } + } + //END_PREVENT_REFINE_HACK + + //PREVENT_ITEM_COPY + { + if (iPulse - GetMyShopTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("λ %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + return false; + } + + } + //END_PREVENT_ITEM_COPY + + if (item->GetVnum() != 70302) + { + PIXEL_POSITION posWarp; + + int x = 0; + int y = 0; + + double nDist = 0; + const double nDistant = 5000.0; + + if (item->GetVnum() == 22010) + { + x = item->GetSocket(0) - GetX(); + y = item->GetSocket(1) - GetY(); + } + + else if (item->GetVnum() == 22000) + { + SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp); + + if (item->GetSocket(0) == 0) + { + x = posWarp.x - GetX(); + y = posWarp.y - GetY(); + } + else + { + x = item->GetSocket(0) - GetX(); + y = item->GetSocket(1) - GetY(); + } + } + + nDist = sqrt(pow((float)x,2) + pow((float)y,2)); + + if (nDistant > nDist) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̵ Ǿ ġ ʹ ȯθ Ҽ ϴ.")); + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "PossibleDistant %f nNowDist %f", nDistant,nDist); + return false; + } + } + + //PREVENT_PORTAL_AFTER_EXCHANGE + + if (iPulse - GetExchangeTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷ %d ̳ ȯ,ȯε ϴ."), g_nPortalLimitTime); + return false; + } + //END_PREVENT_PORTAL_AFTER_EXCHANGE + + } + + if ((item->GetVnum() == 50200) || (item->GetVnum() == 71049)) + { + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ,ܺ Ҽ ϴ.")); + return false; + } + + } + //END_PREVENT_TRADE_WINDOW + + // @fixme150 BEGIN + if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot use this item if you're using quests")); + return false; + } + // @fixme150 END + + if (IS_SET(item->GetFlag(), ITEM_FLAG_LOG)) + { + DWORD vid = item->GetVID(); + DWORD oldCount = item->GetCount(); + DWORD vnum = item->GetVnum(); + + char hint[ITEM_NAME_MAX_LEN + 32 + 1]; + int len = snprintf(hint, sizeof(hint) - 32, "%s", item->GetName()); + + if (len < 0 || len >= (int) sizeof(hint) - 32) + len = (sizeof(hint) - 32) - 1; + + bool ret = UseItemEx(item, DestCell); + + if (NULL == ITEM_MANAGER::instance().FindByVID(vid)) + { + LogManager::instance().ItemLog(this, vid, vnum, "REMOVE", hint); + } + else if (oldCount != item->GetCount()) + { + snprintf(hint + len, sizeof(hint) - len, " %u", oldCount - 1); + LogManager::instance().ItemLog(this, vid, vnum, "USE_ITEM", hint); + } + return (ret); + } + else + return UseItemEx(item, DestCell); +} + +bool CHARACTER::DropItem(TItemPos Cell, BYTE bCount) +{ + LPITEM item = NULL; + + if (!CanHandleItem()) + { + if (NULL != DragonSoul_RefineWindow_GetOpener()) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭâ ¿ ű ϴ.")); + return false; + } +#ifdef ENABLE_NEWSTUFF + if (g_ItemDropTimeLimitValue && !PulseManager::Instance().IncreaseClock(GetPlayerID(), ePulse::ItemDrop, std::chrono::milliseconds(g_ItemDropTimeLimitValue))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 带 ϴ.")); + return false; + } +#endif + if (IsDead()) + return false; + + if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) + return false; + + if (item->IsExchanging()) + return false; + + if (true == item->isLocked()) + return false; + + if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) + return false; + + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP | ITEM_ANTIFLAG_GIVE)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ.")); + return false; + } + + if (bCount == 0 || bCount > item->GetCount()) + bCount = item->GetCount(); + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, 255); + + LPITEM pkItemToDrop; + + if (bCount == item->GetCount()) + { + item->RemoveFromCharacter(); + pkItemToDrop = item; + } + else + { + if (bCount == 0) + { + if (test_server) + sys_log(0, "[DROP_ITEM] drop item count == 0"); + return false; + } + + item->SetCount(item->GetCount() - bCount); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + pkItemToDrop = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), bCount); + + // copy item socket -- by mhh + FN_copy_item_socket(pkItemToDrop, item); + + char szBuf[51 + 1]; + snprintf(szBuf, sizeof(szBuf), "%u %u", pkItemToDrop->GetID(), pkItemToDrop->GetCount()); + LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf); + } + + PIXEL_POSITION pxPos = GetXYZ(); + + if (pkItemToDrop->AddToGround(GetMapIndex(), pxPos)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 3 ϴ.")); +#ifdef ENABLE_NEWSTUFF + pkItemToDrop->StartDestroyEvent(g_aiItemDestroyTime[ITEM_DESTROY_TIME_DROPITEM]); +#else + pkItemToDrop->StartDestroyEvent(); +#endif + + ITEM_MANAGER::instance().FlushDelayedSave(pkItemToDrop); + + char szHint[32 + 1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", pkItemToDrop->GetName(), pkItemToDrop->GetCount(), pkItemToDrop->GetOriginalVnum()); + LogManager::instance().ItemLog(this, pkItemToDrop, "DROP", szHint); + //Motion(MOTION_PICKUP); + } + + return true; +} + +bool CHARACTER::DropGold(int gold) +{ + if (gold <= 0 || gold > GetGold()) + return false; + + if (!CanHandleItem()) + return false; + +#ifdef ENABLE_NEWSTUFF + if (g_GoldDropTimeLimitValue && !PulseManager::Instance().IncreaseClock(GetPlayerID(), ePulse::BoxOpening, std::chrono::milliseconds(g_GoldDropTimeLimitValue))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 带 ϴ.")); + return false; + } +#endif + + LPITEM item = ITEM_MANAGER::instance().CreateItem(1, gold); + + if (item) + { + PIXEL_POSITION pos = GetXYZ(); + + if (item->AddToGround(GetMapIndex(), pos)) + { + //Motion(MOTION_PICKUP); + PointChange(POINT_GOLD, -gold, true); + + if (gold > 1000) + LogManager::instance().CharLog(this, gold, "DROP_GOLD", ""); + +#ifdef ENABLE_NEWSTUFF + item->StartDestroyEvent(g_aiItemDestroyTime[ITEM_DESTROY_TIME_DROPGOLD]); +#else + item->StartDestroyEvent(); +#endif + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ϴ."), 150/60); + } + + Save(); + return true; + } + + return false; +} + +#ifdef ENABLE_CHEQUE_SYSTEM +bool CHARACTER::DropCheque(int cheque) +{ +#ifdef DISABLE_CHEQUE_DROP + return false; +#else + if (cheque <= 0 || cheque > GetCheque()) + return false; + + if (cheque >= CHEQUE_MAX) + return false; + + if (!CanHandleItem()) + return false; + +#ifdef ENABLE_NEWSTUFF + if (g_GoldDropTimeLimitValue && !PulseManager::Instance().IncreaseClock(GetPlayerID(), ePulse::BoxOpening, std::chrono::milliseconds(g_GoldDropTimeLimitValue))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 带 ϴ.")); + return false; + } +#endif + + LPITEM item = ITEM_MANAGER::instance().CreateItem(CHEQUE_VNUM, cheque); + if (item) + { + PIXEL_POSITION pos = GetXYZ(); + if (item->AddToGround(GetMapIndex(), pos)) + { + PointChange(POINT_CHEQUE, -cheque, true); + LogManager::instance().CharLog(this, cheque, "DROP_CHEQUE", ""); + #ifdef ENABLE_NEWSTUFF + item->StartDestroyEvent(g_aiItemDestroyTime[ITEM_DESTROY_TIME_DROPGOLD]); + #else + item->StartDestroyEvent(); + #endif + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ϴ."), 150/60); + } + Save(); + return true; + } + return false; +#endif +} +#endif + +bool CHARACTER::MoveItem(TItemPos Cell, TItemPos DestCell, BYTE count) +{ + if (Cell.IsSamePosition(DestCell)) // @fixme196 (check same slot n same window aliases) + return false; + + if (!IsValidItemPosition(Cell)) + return false; + + LPITEM item = NULL; + if (!(item = GetItem(Cell))) + return false; + + if (item->IsExchanging()) + return false; + + if (item->GetCount() < count) + return false; + + if (INVENTORY == Cell.window_type && Cell.cell >= INVENTORY_MAX_NUM && IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + return false; + + if (true == item->isLocked()) + return false; + + if (!IsValidItemPosition(DestCell)) + { + return false; + } + + if (!CanHandleItem()) + { + if (NULL != DragonSoul_RefineWindow_GetOpener()) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭâ ¿ ű ϴ.")); + return false; + } + + if (DestCell.IsBeltInventoryPosition() && false == CBeltInventoryHelper::CanMoveIntoBeltInventory(item)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ʈ κ丮 ű ϴ.")); + return false; + } + + if (Cell.IsEquipPosition()) + { + if (!CanUnequipNow(item)) + return false; + +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + int iWearCell = item->FindEquipCell(this); + if (iWearCell == WEAR_WEAPON) + { + LPITEM costumeWeapon = GetWear(WEAR_COSTUME_WEAPON); + if (costumeWeapon && !UnequipItem(costumeWeapon)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot unequip the costume weapon. Not enough space.")); + return false; + } + + if (!IsEmptyItemGrid(DestCell, item->GetSize(), Cell.cell)) + return UnequipItem(item); + } +#endif + } + + if (DestCell.IsEquipPosition()) + { + if (GetItem(DestCell)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ϰ ֽϴ.")); + + return false; + } + + EquipItem(item, DestCell.cell - INVENTORY_MAX_NUM); + } + else + { + if (item->IsDragonSoul()) + { + if (item->IsEquipped()) + { + return DSManager::instance().PullOut(this, DestCell, item); + } + else + { + if (DestCell.window_type != DRAGON_SOUL_INVENTORY) + { + return false; + } + + if (!DSManager::instance().IsValidCellForThisItem(item, DestCell)) + return false; + } + } + + else if (DRAGON_SOUL_INVENTORY == DestCell.window_type) + return false; + + LPITEM item2; + + if ((item2 = GetItem(DestCell)) && item != item2 && item2->IsStackable() && + !IS_SET(item2->GetAntiFlag(), ITEM_ANTIFLAG_STACK) && + item2->GetVnum() == item->GetVnum()) + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + if (item2->GetSocket(i) != item->GetSocket(i)) + return false; + + if (count == 0) + count = item->GetCount(); + + sys_log(0, "%s: ITEM_STACK %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + count = MIN(g_bItemCountLimit - item2->GetCount(), count); + + item->SetCount(item->GetCount() - count); + item2->SetCount(item2->GetCount() + count); + return true; + } + + if (!IsEmptyItemGrid(DestCell, item->GetSize(), Cell.cell)) + return false; + + if (count == 0 || count >= item->GetCount() || !item->IsStackable() || IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) + { + sys_log(0, "%s: ITEM_MOVE %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + item->RemoveFromCharacter(); +#ifdef ENABLE_HIGHLIGHT_NEW_ITEM + SetItem(DestCell, item, true); +#else + SetItem(DestCell, item); +#endif + if (INVENTORY == Cell.window_type && INVENTORY == DestCell.window_type) + SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, DestCell.cell); + } + else if (count < item->GetCount()) + { + sys_log(0, "%s: ITEM_SPLIT %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + item->SetCount(item->GetCount() - count); + LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), count); + + // copy socket -- by mhh + FN_copy_item_socket(item2, item); + + item2->AddToCharacter(this, DestCell); + + char szBuf[51+1]; + snprintf(szBuf, sizeof(szBuf), "%u %u %u %u ", item2->GetID(), item2->GetCount(), item->GetCount(), item->GetCount() + item2->GetCount()); + LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf); + } + } + + return true; +} + +namespace NPartyPickupDistribute +{ + struct FFindOwnership + { + LPITEM item; + LPCHARACTER owner; + + FFindOwnership(LPITEM item) + : item(item), owner(NULL) + { + } + + void operator () (LPCHARACTER ch) + { + if (item->IsOwnership(ch)) + owner = ch; + } + }; + + struct FCountNearMember + { + int total; + int x, y; + + FCountNearMember(LPCHARACTER center ) + : total(0), x(center->GetX()), y(center->GetY()) + { + } + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + total += 1; + } + }; + + struct FMoneyDistributor + { + int total; + LPCHARACTER c; + int x, y; + int iMoney; + + FMoneyDistributor(LPCHARACTER center, int iMoney) + : total(0), c(center), x(center->GetX()), y(center->GetY()), iMoney(iMoney) + { + } + + void operator ()(LPCHARACTER ch) + { + if (ch!=c) + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + ch->PointChange(POINT_GOLD, iMoney, true); + + if (iMoney > 1000) + { + LOG_LEVEL_CHECK(LOG_LEVEL_MAX, LogManager::instance().CharLog(ch, iMoney, "GET_GOLD", "")); + } + } + } + }; +} + +void CHARACTER::GiveGold(int iAmount) +{ + if (iAmount <= 0) + return; + + sys_log(0, "GIVE_GOLD: %s %d", GetName(), iAmount); + + if (GetParty()) + { + LPPARTY pParty = GetParty(); + + DWORD dwTotal = iAmount; + DWORD dwMyAmount = dwTotal; + + NPartyPickupDistribute::FCountNearMember funcCountNearMember(this); + pParty->ForEachOnlineMember(funcCountNearMember); + + if (funcCountNearMember.total > 1) + { + DWORD dwShare = dwTotal / funcCountNearMember.total; + dwMyAmount -= dwShare * (funcCountNearMember.total - 1); + + NPartyPickupDistribute::FMoneyDistributor funcMoneyDist(this, dwShare); + + pParty->ForEachOnlineMember(funcMoneyDist); + } + + PointChange(POINT_GOLD, dwMyAmount, true); + + if (dwMyAmount > 1000) + { + LOG_LEVEL_CHECK(LOG_LEVEL_MAX, LogManager::instance().CharLog(this, dwMyAmount, "GET_GOLD", "")); + } + } + else + { + PointChange(POINT_GOLD, iAmount, true); + + if (iAmount > 1000) + { + LOG_LEVEL_CHECK(LOG_LEVEL_MAX, LogManager::instance().CharLog(this, iAmount, "GET_GOLD", "")); + } + } +} + +#ifdef ENABLE_CHEQUE_SYSTEM +void CHARACTER::GiveCheque(int iAmount) +{ + if (iAmount <= 0) + return; + + PointChange(POINT_CHEQUE, iAmount, true); +} +#endif + +bool CHARACTER::PickupItem(DWORD dwVID) +{ + LPITEM item = ITEM_MANAGER::instance().FindByVID(dwVID); + + if (IsObserverMode()) + return false; + + if (!item || !item->GetSectree()) + return false; + + if (item->DistanceValid(this)) + { + // @fixme150 BEGIN + if (item->GetType() == ITEM_QUEST) + { + if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot pickup this item if you're using quests")); + return false; + } + } + // @fixme150 END + + if (item->IsOwnership(this)) + { + if (item->GetType() == ITEM_ELK) + { + GiveGold(item->GetCount()); + item->RemoveFromGround(); + + M2_DESTROY_ITEM(item); + + Save(); + } +#if defined(ENABLE_CHEQUE_SYSTEM) && !defined(DISABLE_CHEQUE_DROP) + else if (item->GetVnum() == CHEQUE_VNUM) + { + if (item->GetCount() + GetCheque() > CHEQUE_MAX - 1) + return false; + GiveCheque(item->GetCount()); + item->RemoveFromGround(); + M2_DESTROY_ITEM(item); + Save(); + } +#endif + + else + { + auto finalItem = AutoStackItem(item); + if (finalItem) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), finalItem->GetName()); + if (finalItem->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem(GetPlayerID(), finalItem); + return true; + } + + int iEmptyCell = GetEmptyInventoryEx(item); + if (iEmptyCell == -1) + { + sys_log(0, "No empty inventory pid %u size %ud itemid %u", GetPlayerID(), item->GetSize(), item->GetID()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + + item->RemoveFromGround(); + + item->AddToCharacter(this, TItemPos(item->GetWindowInventoryEx(), iEmptyCell)); + + char szHint[32+1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum()); + LogManager::instance().ItemLog(this, item, "GET", szHint); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + + if (item->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem(GetPlayerID(), item); + } + + //Motion(MOTION_PICKUP); + return true; + } + else if (!IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_DROP) && GetParty()) + { + NPartyPickupDistribute::FFindOwnership funcFindOwnership(item); + + GetParty()->ForEachOnlineMember(funcFindOwnership); + + LPCHARACTER owner = funcFindOwnership.owner; + // @fixme115 + if (!owner) + return false; + + int iEmptyCell = -1; + if (!(owner && (iEmptyCell = owner->GetEmptyInventoryEx(item)) != -1)) + { + owner = this; + + if ((iEmptyCell = GetEmptyInventoryEx(item)) == -1) + { + owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + } + + item->RemoveFromGround(); + + item->AddToCharacter(owner, TItemPos(item->GetWindowInventoryEx(), iEmptyCell)); + + char szHint[32+1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum()); + LogManager::instance().ItemLog(owner, item, "GET", szHint); + + if (owner == this) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + else + { + owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s κ %s"), GetName(), item->GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" : %s Կ %s"), owner->GetName(), item->GetName()); + } + + if (item->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem (owner->GetPlayerID(), item); + + return true; + } + } + + return false; +} + +bool CHARACTER::SwapItem(BYTE bCell, BYTE bDestCell) +{ + if (!CanHandleItem()) + return false; + + TItemPos srcCell(INVENTORY, bCell), destCell(INVENTORY, bDestCell); + + //if (bCell >= INVENTORY_MAX_NUM + WEAR_MAX_NUM || bDestCell >= INVENTORY_MAX_NUM + WEAR_MAX_NUM) + if (srcCell.IsDragonSoulEquipPosition() || destCell.IsDragonSoulEquipPosition()) + return false; + + if (bCell == bDestCell) + return false; + + if (srcCell.IsEquipPosition() && destCell.IsEquipPosition()) + return false; + + LPITEM item1, item2; + + if (srcCell.IsEquipPosition()) + { + item1 = GetInventoryItem(bDestCell); + item2 = GetInventoryItem(bCell); + } + else + { + item1 = GetInventoryItem(bCell); + item2 = GetInventoryItem(bDestCell); + } + + if (!item1 || !item2) + return false; + + if (item1 == item2) + { + sys_log(0, "[WARNING][WARNING][HACK USER!] : %s %d %d", m_stName.c_str(), bCell, bDestCell); + return false; + } + + if (!IsEmptyItemGrid(TItemPos (INVENTORY, item1->GetCell()), item2->GetSize(), item1->GetCell())) + return false; + + if (TItemPos(EQUIPMENT, item2->GetCell()).IsEquipPosition()) + { + BYTE bEquipCell = item2->GetCell() - INVENTORY_MAX_NUM; + BYTE bInvenCell = item1->GetCell(); + + if (item2->IsDragonSoul() || item2->GetType() == ITEM_BELT) // @fixme117 + { + if (false == CanUnequipNow(item2) || false == CanEquipNow(item1)) + return false; + } + + if (bEquipCell != item1->FindEquipCell(this)) + return false; + + item2->RemoveFromCharacter(); + + if (item1->EquipTo(this, bEquipCell)) + item2->AddToCharacter(this, TItemPos(INVENTORY, bInvenCell)); + else + sys_err("SwapItem cannot equip %s! item1 %s", item2->GetName(), item1->GetName()); + } + else + { + BYTE bCell1 = item1->GetCell(); + BYTE bCell2 = item2->GetCell(); + + item1->RemoveFromCharacter(); + item2->RemoveFromCharacter(); + + item1->AddToCharacter(this, TItemPos(INVENTORY, bCell2)); + item2->AddToCharacter(this, TItemPos(INVENTORY, bCell1)); + } + + return true; +} + +bool CHARACTER::UnequipItem(LPITEM item) +{ +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + int iWearCell = item->FindEquipCell(this); + if (iWearCell == WEAR_WEAPON) + { + LPITEM costumeWeapon = GetWear(WEAR_COSTUME_WEAPON); + if (costumeWeapon && !UnequipItem(costumeWeapon)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot unequip the costume weapon. Not enough space.")); + return false; + } + } +#endif + + if (false == CanUnequipNow(item)) + return false; + + int pos = GetEmptyInventoryEx(item); + + // HARD CODING + if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE) + ShowAlignment(true); + + item->RemoveFromCharacter(); + item->AddToCharacter(this, TItemPos(item->GetWindowInventoryEx(), pos)); + + CheckMaximumPoints(); + + return true; +} + +bool CHARACTER::EquipItem(LPITEM item, int iCandidateCell) +{ + if (item->IsExchanging()) + return false; + + if (false == item->IsEquipable()) + return false; + + if (false == CanEquipNow(item)) + return false; + + int iWearCell = item->FindEquipCell(this, iCandidateCell); + + if (iWearCell < 0) + return false; + + if (iWearCell == WEAR_BODY && IsRiding() && (item->GetVnum() >= 11901 && item->GetVnum() <= 11904)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ϴ.")); + return false; + } + + if (iWearCell != WEAR_ARROW && IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ϴ.")); + return false; + } + + if (FN_check_item_sex(this, item) == false) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + // @fixme314 BEGIN + if (item && item->GetType() == ITEM_UNIQUE && item->GetCount() > 1) + { + #ifdef ENABLE_UNIQUE_ITEM_AUTOSPLIT + if (!item->IsStackable()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The unique item is not stackable.")); + return false; + } + else if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The unique item has the antiflag stack.")); + return false; + } + + const int iEmptyPos = GetEmptyInventoryEx(item); + if (iEmptyPos == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You don't have enough space to auto-split the unique item.")); + return false; + } + + const auto newCount = item->GetCount() - 1; + MoveItem(item->GetItemPos(), TItemPos(item->GetWindowInventoryEx(), iEmptyPos), newCount); + + if (item->GetCount() != 1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You need to split the item to 1 unit to equip it.")); + return false; + } + #else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You need to split the item to 1 unit to equip it.")); + return false; + #endif + } + // @fixme314 END + + if (item->IsRideItem()) + { + #ifdef ENABLE_MOUNT_COSTUME_EX_SYSTEM + #ifdef ENABLE_NEWSTUFF + // block mount spawn + if (g_NoMountAtGuildWar && GetWarMap()) + { + if (IsRiding()) + StopRiding(); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ż ̿Դϴ.")); + return false; + } + #endif + + // unsummon horse first + if (GetHorse() || IsHorseRiding()) { + StopRiding(); + HorseSummon(false); + } + + if (GetHorse() || IsHorseRiding()) + return false; + #else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ż ̿Դϴ.")); + return false; + #endif + } + + DWORD dwCurTime = get_dword_time(); + + if (iWearCell != WEAR_ARROW + && (dwCurTime - GetLastAttackTime() <= 1500 || dwCurTime - m_dwLastSkillTime <= 1500)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֽϴ.")); + return false; + } + +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + if (iWearCell == WEAR_WEAPON) + { + if (item->GetType() == ITEM_WEAPON) + { + LPITEM costumeWeapon = GetWear(WEAR_COSTUME_WEAPON); + if (costumeWeapon && costumeWeapon->GetValue(3) != item->GetSubType() && !UnequipItem(costumeWeapon)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot unequip the costume weapon. Not enough space.")); + return false; + } + } + else //fishrod/pickaxe + { + LPITEM costumeWeapon = GetWear(WEAR_COSTUME_WEAPON); + if (costumeWeapon && !UnequipItem(costumeWeapon)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot unequip the costume weapon. Not enough space.")); + return false; + } + } + } + else if (iWearCell == WEAR_COSTUME_WEAPON) + { + if (item->GetType() == ITEM_COSTUME && item->GetSubType() == COSTUME_WEAPON) + { + LPITEM pkWeapon = GetWear(WEAR_WEAPON); + if (!pkWeapon || pkWeapon->GetType() != ITEM_WEAPON || item->GetValue(3) != pkWeapon->GetSubType()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot equip the costume weapon. Wrong equipped weapon.")); + return false; + } + } + } +#endif + + if (item->IsDragonSoul()) + { + if(GetInventoryItem(INVENTORY_MAX_NUM + iWearCell)) + { + ChatPacket(CHAT_TYPE_INFO, "̹ ȥ ϰ ֽϴ."); + return false; + } + + if (!item->EquipTo(this, iWearCell)) + { + return false; + } + } + + else + { + if (GetWear(iWearCell) && !IS_SET(GetWear(iWearCell)->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + { + if (item->GetWearFlag() == WEARABLE_ABILITY) + return false; + + if (false == SwapItem(item->GetCell(), INVENTORY_MAX_NUM + iWearCell)) + { + return false; + } + } + else + { + BYTE bOldCell = item->GetCell(); + + if (item->EquipTo(this, iWearCell)) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, bOldCell, iWearCell); + } + } + } + + if (true == item->IsEquipped()) + { + if (-1 != item->GetProto()->cLimitRealTimeFirstUseIndex) + { + if (0 == item->GetSocket(1)) + { + long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[(unsigned char)(item->GetProto()->cLimitRealTimeFirstUseIndex)].lValue; + + if (0 == duration) + duration = 60 * 60 * 24 * 7; + + item->SetSocket(0, time(0) + duration); + item->StartRealTimeExpireEvent(); + } + + item->SetSocket(1, item->GetSocket(1) + 1); + } + + if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE) + ShowAlignment(false); + + const DWORD& dwVnum = item->GetVnum(); + + if (true == CItemVnumHelper::IsRamadanMoonRing(dwVnum)) + { + this->EffectPacket(SE_EQUIP_RAMADAN_RING); + } + + else if (true == CItemVnumHelper::IsHalloweenCandy(dwVnum)) + { + this->EffectPacket(SE_EQUIP_HALLOWEEN_CANDY); + } + + else if (true == CItemVnumHelper::IsHappinessRing(dwVnum)) + { + this->EffectPacket(SE_EQUIP_HAPPINESS_RING); + } + + else if (true == CItemVnumHelper::IsLovePendant(dwVnum)) + { + this->EffectPacket(SE_EQUIP_LOVE_PENDANT); + } + else if (ITEM_UNIQUE == item->GetType() && 0 != item->GetSIGVnum()) + { + const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(item->GetSIGVnum()); + if (NULL != pGroup) + { + const CSpecialAttrGroup* pAttrGroup = ITEM_MANAGER::instance().GetSpecialAttrGroup(pGroup->GetAttrVnum(item->GetVnum())); + if (NULL != pAttrGroup) + { + const std::string& std = pAttrGroup->m_stEffectFileName; + SpecificEffectPacket(std.c_str()); + } + } + } + #ifdef ENABLE_ACCE_COSTUME_SYSTEM + else if ((item->GetType() == ITEM_COSTUME) && (item->GetSubType() == COSTUME_ACCE)) + this->EffectPacket(SE_EFFECT_ACCE_EQUIP); + #endif + + if (item->IsOldMountItem()) // @fixme152 + quest::CQuestManager::instance().SIGUse(GetPlayerID(), quest::QUEST_NO_NPC, item, false); + #ifdef ENABLE_MOUNT_COSTUME_EX_SYSTEM + else if (item->IsNewMountItem()) { + const auto mountVnum = GetPoint(POINT_MOUNT); + MountVnum(mountVnum); + } + #endif + + } + + return true; +} + +void CHARACTER::BuffOnAttr_AddBuffsFromItem(LPITEM pItem) +{ + for (size_t i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++) + { + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]); + if (it != m_map_buff_on_attrs.end()) + { + it->second->AddBuffFromItem(pItem); + } + } +} + +void CHARACTER::BuffOnAttr_RemoveBuffsFromItem(LPITEM pItem) +{ + for (size_t i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++) + { + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]); + if (it != m_map_buff_on_attrs.end()) + { + it->second->RemoveBuffFromItem(pItem); + } + } +} + +void CHARACTER::BuffOnAttr_ClearAll() +{ + for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++) + { + CBuffOnAttributes* pBuff = it->second; + if (pBuff) + { + pBuff->Initialize(); + } + } +} + +void CHARACTER::BuffOnAttr_ValueChange(BYTE bType, BYTE bOldValue, BYTE bNewValue) +{ + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(bType); + + if (0 == bNewValue) + { + if (m_map_buff_on_attrs.end() == it) + return; + else + it->second->Off(); + } + else if(0 == bOldValue) + { + CBuffOnAttributes* pBuff = NULL; + if (m_map_buff_on_attrs.end() == it) + { + switch (bType) + { + case POINT_ENERGY: + { + static BYTE abSlot[] = { WEAR_BODY, WEAR_HEAD, WEAR_FOOTS, WEAR_WRIST, WEAR_WEAPON, WEAR_NECK, WEAR_EAR, WEAR_SHIELD }; + static std::vector vec_slots (abSlot, abSlot + _countof(abSlot)); + pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots); + } + break; + case POINT_COSTUME_ATTR_BONUS: + { + static BYTE abSlot[] = { + WEAR_COSTUME_BODY, + WEAR_COSTUME_HAIR, + #ifdef ENABLE_MOUNT_COSTUME_SYSTEM + WEAR_COSTUME_MOUNT, + #endif + #ifdef ENABLE_WEAPON_COSTUME_SYSTEM + WEAR_COSTUME_WEAPON, + #endif + }; + static std::vector vec_slots (abSlot, abSlot + _countof(abSlot)); + pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots); + } + break; + default: + break; + } + m_map_buff_on_attrs.emplace(bType, pBuff); + + } + else + pBuff = it->second; + if (pBuff != NULL) + pBuff->On(bNewValue); + } + else + { + assert (m_map_buff_on_attrs.end() != it); + it->second->ChangeBuffValue(bNewValue); + } +} + +LPITEM CHARACTER::FindSpecifyItem(DWORD vnum) const +{ + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (GetInventoryItem(i) && GetInventoryItem(i)->GetVnum() == vnum) + return GetInventoryItem(i); + + return NULL; +} + +LPITEM CHARACTER::FindItemByID(DWORD id) const +{ + for (int i=0 ; i < INVENTORY_MAX_NUM ; ++i) + { + if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id) + return GetInventoryItem(i); + } + + for (int i=BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END ; ++i) + { + if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id) + return GetInventoryItem(i); + } + + return NULL; +} + +int CHARACTER::CountSpecifyItem(DWORD vnum) const +{ + int count = 0; + LPITEM item; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + item = GetInventoryItem(i); + if (NULL != item && item->GetVnum() == vnum) + { + if (m_pkMyShop && m_pkMyShop->IsSellingItem(item->GetID())) + { + continue; + } + else + { + count += item->GetCount(); + } + } + } + + return count; +} + +void CHARACTER::RemoveSpecifyItem(DWORD vnum, DWORD count) +{ + if (0 == count) + return; + + for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i) + { + if (NULL == GetInventoryItem(i)) + continue; + + if (GetInventoryItem(i)->GetVnum() != vnum) + continue; + + if(m_pkMyShop) + { + bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID()); + if (isItemSelling) + continue; + } + + if (vnum >= 80003 && vnum <= 80007) + LogManager::instance().GoldBarLog(GetPlayerID(), GetInventoryItem(i)->GetID(), QUEST, "RemoveSpecifyItem"); + + if (count >= GetInventoryItem(i)->GetCount()) + { + count -= GetInventoryItem(i)->GetCount(); + GetInventoryItem(i)->SetCount(0); + + if (0 == count) + return; + } + else + { + GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count); + return; + } + } + + if (count) + sys_log(0, "CHARACTER::RemoveSpecifyItem cannot remove enough item vnum %u, still remain %d", vnum, count); +} + +int CHARACTER::CountSpecifyTypeItem(BYTE type) const +{ + int count = 0; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM pItem = GetInventoryItem(i); + if (pItem != NULL && pItem->GetType() == type) + { + count += pItem->GetCount(); + } + } + + return count; +} + +void CHARACTER::RemoveSpecifyTypeItem(BYTE type, DWORD count) +{ + if (0 == count) + return; + + for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i) + { + if (NULL == GetInventoryItem(i)) + continue; + + if (GetInventoryItem(i)->GetType() != type) + continue; + + if(m_pkMyShop) + { + bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID()); + if (isItemSelling) + continue; + } + + if (count >= GetInventoryItem(i)->GetCount()) + { + count -= GetInventoryItem(i)->GetCount(); + GetInventoryItem(i)->SetCount(0); + + if (0 == count) + return; + } + else + { + GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count); + return; + } + } +} + +void CHARACTER::AutoGiveItem(LPITEM item, bool longOwnerShip) +{ + if (NULL == item) + { + sys_err ("NULL point."); + return; + } + if (item->GetOwner()) + { + sys_err ("item %d 's owner exists!",item->GetID()); + return; + } + + int cell = GetEmptyInventoryEx(item); + if (cell != -1) + { + item->AddToCharacter(this, TItemPos(item->GetWindowInventoryEx(), cell)); + + LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName()); + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION) + { + TQuickslot * pSlot; + + if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE) + { + TQuickslot slot; + slot.type = QUICKSLOT_TYPE_ITEM; + slot.pos = cell; + SetQuickslot(0, slot); + } + } + } + else + { + item->AddToGround (GetMapIndex(), GetXYZ()); +#ifdef ENABLE_NEWSTUFF + item->StartDestroyEvent(g_aiItemDestroyTime[ITEM_DESTROY_TIME_AUTOGIVE]); +#else + item->StartDestroyEvent(); +#endif + + if (longOwnerShip) + item->SetOwnership (this, 300); + else + item->SetOwnership (this, 60); + LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName()); + } +} + +std::pair CHARACTER::AutoStackItemProto(DWORD dwItemVnum, BYTE & bCount) +{ + TItemTable * p = ITEM_MANAGER::instance().GetTable(dwItemVnum); + if (!p) + return {false, nullptr}; + + if (p->dwFlags & ITEM_FLAG_STACKABLE && p->bType != ITEM_BLEND) + { + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM item = GetInventoryItem(i); + if (!item) + continue; + + if (item->GetVnum() == dwItemVnum && FN_check_item_socket(item)) + { + if (IS_SET(p->dwFlags, ITEM_FLAG_MAKECOUNT)) + { + if (bCount < p->alValues[1]) + bCount = p->alValues[1]; + } + + auto bCount2 = MIN(g_bItemCountLimit - item->GetCount(), bCount); + bCount -= bCount2; + item->SetCount(item->GetCount() + bCount2); + + if (bCount == 0) + return {true, item}; + } + } + } + + return {true, nullptr}; +} + +LPITEM CHARACTER::AutoStackItem(LPITEM item) +{ + if (item->IsStackable() && !IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) + { + auto bCount = item->GetCount(); + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM item2 = GetInventoryItem(i); + if (!item2) + continue; + + if (item == item2) + continue; + + if (item2->GetVnum() == item->GetVnum()) + { + int j = 0; + for (; j < ITEM_SOCKET_MAX_NUM; ++j) + if (item2->GetSocket(j) != item->GetSocket(j)) + break; + + if (j != ITEM_SOCKET_MAX_NUM) + continue; + + auto bCount2 = MIN(g_bItemCountLimit - item2->GetCount(), bCount); + bCount -= bCount2; + item2->SetCount(item2->GetCount() + bCount2); + + if (bCount == 0) + { + M2_DESTROY_ITEM(item); + return item2; + } + } + } + + item->SetCount(bCount); + } + + return nullptr; +} + +LPITEM CHARACTER::AutoGiveItem(DWORD dwItemVnum, BYTE bCount, int iRarePct, bool bMsg) +{ + auto [foundTable, foundItem] = AutoStackItemProto(dwItemVnum, bCount); + if (!foundTable) + return nullptr; + + DBManager::instance().SendMoneyLog(MONEY_LOG_DROP, dwItemVnum, bCount); + + if (foundItem) + { + if (bMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), foundItem->GetName()); + return foundItem; + } + + LPITEM item = ITEM_MANAGER::instance().CreateItem(dwItemVnum, bCount, 0, true); + if (!item) + { + sys_err("cannot create item by vnum %u (name: %s)", dwItemVnum, GetName()); + return NULL; + } + + auto finalItem = AutoStackItem(item); // @fixme316 + if (finalItem) + { + if (bMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), finalItem->GetName()); + return finalItem; + } + + if (item->GetType() == ITEM_BLEND) + { + for (int i=0; i < INVENTORY_MAX_NUM; i++) + { + LPITEM inv_item = GetInventoryItem(i); + + if (inv_item == NULL) continue; + + if (inv_item->GetType() == ITEM_BLEND) + { + if (inv_item->GetVnum() == item->GetVnum()) + { + if (inv_item->GetSocket(0) == item->GetSocket(0) && + inv_item->GetSocket(1) == item->GetSocket(1) && + inv_item->GetSocket(2) == item->GetSocket(2) && + inv_item->GetCount() < g_bItemCountLimit) + { + inv_item->SetCount(inv_item->GetCount() + item->GetCount()); + return inv_item; + } + } + } + } + } + + int iEmptyCell = GetEmptyInventoryEx(item); + if (iEmptyCell != -1) + { + if (bMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + + item->AddToCharacter(this, TItemPos(item->GetWindowInventoryEx(), iEmptyCell)); + LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName()); + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION) + { + TQuickslot * pSlot; + + if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE) + { + TQuickslot slot; + slot.type = QUICKSLOT_TYPE_ITEM; + slot.pos = iEmptyCell; + SetQuickslot(0, slot); + } + } + } + else + { + item->AddToGround(GetMapIndex(), GetXYZ()); +#ifdef ENABLE_NEWSTUFF + item->StartDestroyEvent(g_aiItemDestroyTime[ITEM_DESTROY_TIME_AUTOGIVE]); +#else + item->StartDestroyEvent(); +#endif + + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP)) + item->SetOwnership(this, 300); + else + item->SetOwnership(this, 60); + LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName()); + } + + sys_log(0, + "7: %d %d", dwItemVnum, bCount); + return item; +} + +bool CHARACTER::GiveItem(LPCHARACTER victim, TItemPos Cell) +{ + if (!CanHandleItem()) + return false; + + // @fixme150 BEGIN + if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot take this item if you're using quests")); + return false; + } + // @fixme150 END + + LPITEM item = GetItem(Cell); + + if (item && !item->IsExchanging()) + { + if (victim->CanReceiveItem(this, item)) + { + victim->ReceiveItem(this, item); + return true; + } + } + + return false; +} + +bool CHARACTER::CanReceiveItem(LPCHARACTER from, LPITEM item) const +{ + if (IsPC()) + return false; + + // TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX + if (DISTANCE_APPROX(GetX() - from->GetX(), GetY() - from->GetY()) > 2000) + return false; + // END_OF_TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX + + switch (GetRaceNum()) + { + case fishing::CAMPFIRE_MOB: + if (item->GetType() == ITEM_FISH && + (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD)) + return true; + break; + + case fishing::FISHER_MOB: + if (item->GetType() == ITEM_ROD) + return true; + break; + + // BUILDING_NPC + case BLACKSMITH_WEAPON_MOB: + case DEVILTOWER_BLACKSMITH_WEAPON_MOB: + if (item->GetType() == ITEM_WEAPON && + item->GetRefinedVnum()) + return true; + else + return false; + break; + + case BLACKSMITH_ARMOR_MOB: + case DEVILTOWER_BLACKSMITH_ARMOR_MOB: + if (item->GetType() == ITEM_ARMOR && + (item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) && + item->GetRefinedVnum()) + return true; + else + return false; + break; + + case BLACKSMITH_ACCESSORY_MOB: + case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB: + if (item->GetType() == ITEM_ARMOR && + !(item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) && + item->GetRefinedVnum()) + return true; + else + return false; + break; + // END_OF_BUILDING_NPC + + case BLACKSMITH_MOB: + if (item->GetRefinedVnum() && item->GetRefineSet() < 500) + { + return true; + } + else + { + return false; + } + + case BLACKSMITH2_MOB: + if (item->GetRefineSet() >= 500) + { + return true; + } + else + { + return false; + } + + case ALCHEMIST_MOB: + if (item->GetRefinedVnum()) + return true; + break; + + case 20101: + case 20102: + case 20103: + + if (item->GetVnum() == ITEM_REVIVE_HORSE_1) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_2 || item->GetVnum() == ITEM_HORSE_FOOD_3) + { + return false; + } + break; + case 20104: + case 20105: + case 20106: + + if (item->GetVnum() == ITEM_REVIVE_HORSE_2) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_2) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_3) + { + return false; + } + break; + case 20107: + case 20108: + case 20109: + + if (item->GetVnum() == ITEM_REVIVE_HORSE_3) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_3) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_2) + { + return false; + } + break; + } + + //if (IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_GIVE)) + { + return true; + } + + return false; +} + +void CHARACTER::ReceiveItem(LPCHARACTER from, LPITEM item) +{ + if (IsPC()) + return; + + switch (GetRaceNum()) + { + case fishing::CAMPFIRE_MOB: + if (item->GetType() == ITEM_FISH && (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD)) + fishing::Grill(from, item); + else + { + // TAKE_ITEM_BUG_FIX + from->SetQuestNPCID(GetVID()); + // END_OF_TAKE_ITEM_BUG_FIX + quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item); + } + break; + + // DEVILTOWER_NPC + case DEVILTOWER_BLACKSMITH_WEAPON_MOB: + case DEVILTOWER_BLACKSMITH_ARMOR_MOB: + case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB: + if (item->GetRefinedVnum() != 0 && item->GetRefineSet() != 0 && item->GetRefineSet() < 500) + { + from->SetRefineNPC(this); + from->RefineInformation(item->GetCell(), REFINE_TYPE_MONEY_ONLY); + } + else + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + // END_OF_DEVILTOWER_NPC + + case BLACKSMITH_MOB: + case BLACKSMITH2_MOB: + case BLACKSMITH_WEAPON_MOB: + case BLACKSMITH_ARMOR_MOB: + case BLACKSMITH_ACCESSORY_MOB: + if (item->GetRefinedVnum()) + { + from->SetRefineNPC(this); + from->RefineInformation(item->GetCell(), REFINE_TYPE_NORMAL); + } + else + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + + case 20101: + case 20102: + case 20103: + case 20104: + case 20105: + case 20106: + case 20107: + case 20108: + case 20109: + if (item->GetVnum() == ITEM_REVIVE_HORSE_1 || + item->GetVnum() == ITEM_REVIVE_HORSE_2 || + item->GetVnum() == ITEM_REVIVE_HORSE_3) + { + from->ReviveHorse(); + item->SetCount(item->GetCount()-1); + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ־ϴ.")); + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || + item->GetVnum() == ITEM_HORSE_FOOD_2 || + item->GetVnum() == ITEM_HORSE_FOOD_3) + { + from->FeedHorse(); + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ־ϴ.")); + item->SetCount(item->GetCount()-1); + EffectPacket(SE_HPUP_RED); + } + break; + + default: + sys_log(0, "TakeItem %s %d %s", from->GetName(), GetRaceNum(), item->GetName()); + from->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item); + break; + } +} + +bool CHARACTER::IsEquipUniqueItem(DWORD dwItemVnum) const +{ + { + LPITEM u = GetWear(WEAR_UNIQUE1); + + if (u && u->GetVnum() == dwItemVnum) + return true; + } + + { + LPITEM u = GetWear(WEAR_UNIQUE2); + + if (u && u->GetVnum() == dwItemVnum) + return true; + } + + if (dwItemVnum == UNIQUE_ITEM_RING_OF_LANGUAGE) + return IsEquipUniqueItem(UNIQUE_ITEM_RING_OF_LANGUAGE_SAMPLE); + + return false; +} + +// CHECK_UNIQUE_GROUP +bool CHARACTER::IsEquipUniqueGroup(DWORD dwGroupVnum) const +{ + { + LPITEM u = GetWear(WEAR_UNIQUE1); + + if (u && u->GetSpecialGroup() == (int) dwGroupVnum) + return true; + } + + { + LPITEM u = GetWear(WEAR_UNIQUE2); + + if (u && u->GetSpecialGroup() == (int) dwGroupVnum) + return true; + } + + return false; +} +// END_OF_CHECK_UNIQUE_GROUP + +void CHARACTER::SetRefineMode(int iAdditionalCell) +{ + m_iRefineAdditionalCell = iAdditionalCell; + m_bUnderRefine = true; +} + +void CHARACTER::ClearRefineMode() +{ + m_bUnderRefine = false; + SetRefineNPC( NULL ); +} + +bool CHARACTER::GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector &dwItemVnums, + std::vector &dwItemCounts, std::vector &item_gets, int &count) +{ + const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(dwGroupNum); + + if (!pGroup) + { + sys_err("cannot find special item group %d", dwGroupNum); + return false; + } + + std::vector idxes; + int n = pGroup->GetMultiIndex(idxes); + + bool bSuccess; + + for (int i = 0; i < n; i++) + { + bSuccess = false; + int idx = idxes[i]; + DWORD dwVnum = pGroup->GetVnum(idx); + DWORD dwCount = pGroup->GetCount(idx); + int iRarePct = pGroup->GetRarePct(idx); + LPITEM item_get = NULL; + switch (dwVnum) + { + case CSpecialItemGroup::GOLD: + PointChange(POINT_GOLD, dwCount); + LogManager::instance().CharLog(this, dwCount, "TREASURE_GOLD", ""); + + bSuccess = true; + break; + case CSpecialItemGroup::EXP: + { + PointChange(POINT_EXP, dwCount); + LogManager::instance().CharLog(this, dwCount, "TREASURE_EXP", ""); + + bSuccess = true; + } + break; + + case CSpecialItemGroup::MOB: + { + sys_log(0, "CSpecialItemGroup::MOB %d", dwCount); + int x = GetX() + number(-500, 500); + int y = GetY() + number(-500, 500); + + LPCHARACTER ch = CHARACTER_MANAGER::instance().SpawnMob(dwCount, GetMapIndex(), x, y, 0, true, -1); + if (ch) + ch->SetAggressive(); + bSuccess = true; + } + break; + case CSpecialItemGroup::SLOW: + { + sys_log(0, "CSpecialItemGroup::SLOW %d", -(int)dwCount); + AddAffect(AFFECT_SLOW, POINT_MOV_SPEED, -(int)dwCount, AFF_SLOW, 300, 0, true); + bSuccess = true; + } + break; + case CSpecialItemGroup::DRAIN_HP: + { + int iDropHP = GetMaxHP()*dwCount/100; + sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP); + iDropHP = MIN(iDropHP, GetHP()-1); + sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP); + PointChange(POINT_HP, -iDropHP); + bSuccess = true; + } + break; + case CSpecialItemGroup::POISON: + { + AttackedByPoison(NULL); + bSuccess = true; + } + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case CSpecialItemGroup::BLEEDING: + { + AttackedByBleeding(NULL); + bSuccess = true; + } + break; +#endif + case CSpecialItemGroup::MOB_GROUP: + { + int sx = GetX() - number(300, 500); + int sy = GetY() - number(300, 500); + int ex = GetX() + number(300, 500); + int ey = GetY() + number(300, 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwCount, GetMapIndex(), sx, sy, ex, ey, NULL, true); + + bSuccess = true; + } + break; + default: + { + item_get = AutoGiveItem(dwVnum, dwCount, iRarePct); + + if (item_get) + { + bSuccess = true; + } + } + break; + } + + if (bSuccess) + { + dwItemVnums.emplace_back(dwVnum); + dwItemCounts.emplace_back(dwCount); + item_gets.emplace_back(item_get); + count++; + + } + else + { + return false; + } + } + return bSuccess; +} + +// NEW_HAIR_STYLE_ADD +bool CHARACTER::ItemProcess_Hair(LPITEM item, int iDestCell) +{ + if (item->CheckItemUseLevel(GetLevel()) == false) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӹ Դϴ.")); + return false; + } + + DWORD hair = item->GetVnum(); + + switch (GetJob()) + { + case JOB_WARRIOR : + hair -= 72000; + break; + + case JOB_ASSASSIN : + hair -= 71250; + break; + + case JOB_SURA : + hair -= 70500; + break; + + case JOB_SHAMAN : + hair -= 69750; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + break; +#endif + default : + return false; + break; + } + + if (hair == GetPart(PART_HAIR)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӹ ŸϷδ ü ϴ.")); + return true; + } + + item->SetCount(item->GetCount() - 1); + + SetPart(PART_HAIR, hair); + UpdatePacket(); + + return true; +} +// END_NEW_HAIR_STYLE_ADD + +bool CHARACTER::ItemProcess_Polymorph(LPITEM item) +{ + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ а Դϴ.")); + return false; + } + + if (true == IsRiding()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а Դϴ.")); + return false; + } + + DWORD dwVnum = item->GetSocket(0); + + if (dwVnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߸ а Դϴ.")); + item->SetCount(item->GetCount()-1); + return false; + } + + const CMob* pMob = CMobManager::instance().Get(dwVnum); + + if (pMob == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߸ а Դϴ.")); + item->SetCount(item->GetCount()-1); + return false; + } + + switch (item->GetVnum()) + { + case 70104 : + case 70105 : + case 70106 : + case 70107 : + case 71093 : + { + sys_log(0, "USE_POLYMORPH_BALL PID(%d) vnum(%d)", GetPlayerID(), dwVnum); + + int iPolymorphLevelLimit = MAX(0, 20 - GetLevel() * 3 / 10); + if (pMob->m_table.bLevel >= GetLevel() + iPolymorphLevelLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʹ ͷδ ϴ.")); + return false; + } + + int iDuration = GetSkillLevel(POLYMORPH_SKILL_ID) == 0 ? 5 : (5 + (5 + GetSkillLevel(POLYMORPH_SKILL_ID)/40 * 25)); + iDuration *= 60; + + DWORD dwBonus = 0; + + dwBonus = (2 + GetSkillLevel(POLYMORPH_SKILL_ID)/40) * 100; + + AddAffect(AFFECT_POLYMORPH, POINT_POLYMORPH, dwVnum, AFF_POLYMORPH, iDuration, 0, true); + AddAffect(AFFECT_POLYMORPH, POINT_ATT_BONUS, dwBonus, AFF_POLYMORPH, iDuration, 0, false); + + item->SetCount(item->GetCount()-1); + } + break; + + case 50322: + { + sys_log(0, "USE_POLYMORPH_BOOK: %s(%u) vnum(%u)", GetName(), GetPlayerID(), dwVnum); + + if (CPolymorphUtils::instance().PolymorphCharacter(this, item, pMob) == true) + { + CPolymorphUtils::instance().UpdateBookPracticeGrade(this, item); + } + else + { + } + } + break; + + default : + sys_err("POLYMORPH invalid item passed PID(%d) vnum(%d)", GetPlayerID(), item->GetOriginalVnum()); + return false; + } + + return true; +} + +bool CHARACTER::CanDoCube() const +{ + if (m_bIsObserver) return false; + if (GetShop()) return false; + if (GetMyShop()) return false; + if (m_bUnderRefine) return false; + if (IsWarping()) return false; + + return true; +} + +bool CHARACTER::UnEquipSpecialRideUniqueItem() +{ + LPITEM Unique1 = GetWear(WEAR_UNIQUE1); + LPITEM Unique2 = GetWear(WEAR_UNIQUE2); +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + LPITEM MountCostume = GetWear(WEAR_COSTUME_MOUNT); +#endif + + if( NULL != Unique1 ) + { + if( UNIQUE_GROUP_SPECIAL_RIDE == Unique1->GetSpecialGroup() ) + { + return UnequipItem(Unique1); + } + } + + if( NULL != Unique2 ) + { + if( UNIQUE_GROUP_SPECIAL_RIDE == Unique2->GetSpecialGroup() ) + { + return UnequipItem(Unique2); + } + } + +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + if (MountCostume) + return UnequipItem(MountCostume); +#endif + + return true; +} + +void CHARACTER::AutoRecoveryItemProcess(const EAffectTypes type) +{ + if (true == IsDead() || true == IsStun()) + return; + + if (false == IsPC()) + return; + + if (AFFECT_AUTO_HP_RECOVERY != type && AFFECT_AUTO_SP_RECOVERY != type) + return; + + if (NULL != FindAffect(AFFECT_STUN)) + return; + + { + const DWORD stunSkills[] = { SKILL_TANHWAN, SKILL_GEOMPUNG, SKILL_BYEURAK, SKILL_GIGUNG }; + + for (size_t i=0 ; i < sizeof(stunSkills)/sizeof(DWORD) ; ++i) + { + const CAffect* p = FindAffect(stunSkills[i]); + + if (NULL != p && AFF_STUN == p->dwFlag) + return; + } + } + + const CAffect* pAffect = FindAffect(type); + const size_t idx_of_amount_of_used = 1; + const size_t idx_of_amount_of_full = 2; + + if (NULL != pAffect) + { + LPITEM pItem = FindItemByID(pAffect->dwFlag); + + if (NULL != pItem && true == pItem->GetSocket(0)) + { + if (!CArenaManager::instance().IsArenaMap(GetMapIndex()) +#ifdef ENABLE_NEWSTUFF + && !(g_NoPotionsOnPVP && CPVPManager::instance().IsFighting(GetPlayerID()) && !IsAllowedPotionOnPVP(pItem->GetVnum())) +#endif + ) + { + const long amount_of_used = pItem->GetSocket(idx_of_amount_of_used); + const long amount_of_full = pItem->GetSocket(idx_of_amount_of_full); + + const int32_t avail = amount_of_full - amount_of_used; + + int32_t amount = 0; + + if (AFFECT_AUTO_HP_RECOVERY == type) + { + amount = GetMaxHP() - (GetHP() + GetPoint(POINT_HP_RECOVERY)); + } + else if (AFFECT_AUTO_SP_RECOVERY == type) + { + amount = GetMaxSP() - (GetSP() + GetPoint(POINT_SP_RECOVERY)); + } + + if (amount > 0) + { + if (avail > amount) + { + const int pct_of_used = amount_of_used * 100 / amount_of_full; + const int pct_of_will_used = (amount_of_used + amount) * 100 / amount_of_full; + + bool bLog = false; + + if ((pct_of_will_used / 10) - (pct_of_used / 10) >= 1) + bLog = true; + pItem->SetSocket(idx_of_amount_of_used, amount_of_used + amount, bLog); + } + else + { + amount = avail; + + ITEM_MANAGER::instance().RemoveItem( pItem ); + } + + if (AFFECT_AUTO_HP_RECOVERY == type) + { + PointChange( POINT_HP_RECOVERY, amount ); + EffectPacket( SE_AUTO_HPUP ); + } + else if (AFFECT_AUTO_SP_RECOVERY == type) + { + PointChange( POINT_SP_RECOVERY, amount ); + EffectPacket( SE_AUTO_SPUP ); + } + } + } + else + { + pItem->Lock(false); + pItem->SetSocket(0, false); + RemoveAffect( const_cast(pAffect) ); + } + } + else + { + RemoveAffect( const_cast(pAffect) ); + } + } +} + +bool CHARACTER::IsValidItemPosition(TItemPos Pos) const +{ + BYTE window_type = Pos.window_type; + WORD cell = Pos.cell; + + switch (window_type) + { + case RESERVED_WINDOW: + return false; + + case INVENTORY: + case EQUIPMENT: + return cell < (INVENTORY_AND_EQUIP_SLOT_MAX); + + case DRAGON_SOUL_INVENTORY: + return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM); + + case SAFEBOX: + if (NULL != m_pkSafebox) + return m_pkSafebox->IsValidPosition(cell); + else + return false; + + case MALL: + if (NULL != m_pkMall) + return m_pkMall->IsValidPosition(cell); + else + return false; + default: + return false; + } +} + +#define VERIFY_MSG(exp, msg) \ + if (true == (exp)) { \ + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(msg)); \ + return false; \ + } + +bool CHARACTER::CanEquipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/ +{ + const TItemTable* itemTable = item->GetProto(); + //BYTE itemType = item->GetType(); + //BYTE itemSubType = item->GetSubType(); + + switch (GetJob()) + { + case JOB_WARRIOR: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR) + return false; + break; + + case JOB_ASSASSIN: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN) + return false; + break; + + case JOB_SHAMAN: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN) + return false; + break; + + case JOB_SURA: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_SURA) + return false; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_WOLFMAN) + return false; + break; +#endif + } + + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limit = itemTable->aLimits[i].lValue; + switch (itemTable->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + break; + + case LIMIT_STR: + if (GetPoint(POINT_ST) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٷ ϴ.")); + return false; + } + break; + + case LIMIT_INT: + if (GetPoint(POINT_IQ) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + break; + + case LIMIT_DEX: + if (GetPoint(POINT_DX) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ø ϴ.")); + return false; + } + break; + + case LIMIT_CON: + if (GetPoint(POINT_HT) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ü ϴ.")); + return false; + } + break; + } + } + + if (item->GetWearFlag() & WEARABLE_UNIQUE) + { + if ((GetWear(WEAR_UNIQUE1) && GetWear(WEAR_UNIQUE1)->IsSameSpecialGroup(item)) || + (GetWear(WEAR_UNIQUE2) && GetWear(WEAR_UNIQUE2)->IsSameSpecialGroup(item))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ũ ÿ ϴ.")); + return false; + } + + if (marriage::CManager::instance().IsMarriageUniqueItem(item->GetVnum()) && + !marriage::CManager::instance().IsMarried(GetPlayerID())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ¿ ϴ.")); + return false; + } + + } + + return true; +} + +bool CHARACTER::CanUnequipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/ +{ + if (ITEM_BELT == item->GetType()) + VERIFY_MSG(CBeltInventoryHelper::IsExistItemInBeltInventory(this), "Ʈ κ丮 ϸ ϴ."); + + if (IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + return false; + + int pos = GetEmptyInventoryEx(item); + VERIFY_MSG(-1 == pos, "ǰ ϴ."); + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_manager.cpp b/source-server/Srcs/Server/game/src/char_manager.cpp new file mode 100644 index 000000000..a63a8bc27 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_manager.cpp @@ -0,0 +1,1118 @@ +#include "stdafx.h" +#include "constants.h" +#include "utils.h" +#include "desc.h" +#include "char.h" +#include "char_manager.h" +#include "mob_manager.h" +#include "party.h" +#include "regen.h" +#include "p2p.h" +#include "dungeon.h" +#include "db.h" +#include "config.h" +#include "xmas_event.h" +#include "questmanager.h" +#include "questlua.h" +#include "locale_service.h" +#include "shutdown_manager.h" +#include "../../common/CommonDefines.h" + +#if (!defined(__GNUC__) || defined(__clang__)) && !defined(CXX11_ENABLED) +# include + +#elif defined(CXX11_ENABLED) +# include + template + decltype(std::bind(&T::second, std::placeholders::_1)) select2nd() + { + return std::bind(&T::second, std::placeholders::_1); + } +#endif + +CHARACTER_MANAGER::CHARACTER_MANAGER() : + m_iVIDCount(0), + m_pkChrSelectedStone(NULL), + m_bUsePendingDestroy(false) +{ + RegisterRaceNum(xmas::MOB_XMAS_FIRWORK_SELLER_VNUM); + RegisterRaceNum(xmas::MOB_SANTA_VNUM); + RegisterRaceNum(xmas::MOB_XMAS_TREE_VNUM); + + m_iMobItemRate = 100; + m_iMobDamageRate = 100; + m_iMobGoldAmountRate = 100; + m_iMobGoldDropRate = 100; + m_iMobExpRate = 100; + + m_iMobItemRatePremium = 100; + m_iMobGoldAmountRatePremium = 100; + m_iMobGoldDropRatePremium = 100; + m_iMobExpRatePremium = 100; + + m_iUserDamageRate = 100; + m_iUserDamageRatePremium = 100; +} + +CHARACTER_MANAGER::~CHARACTER_MANAGER() +{ + Destroy(); +} + +void CHARACTER_MANAGER::Destroy() +{ + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.begin(); + while (it != m_map_pkChrByVID.end()) { + LPCHARACTER ch = it->second; + M2_DESTROY_CHARACTER(ch); // m_map_pkChrByVID is changed here + it = m_map_pkChrByVID.begin(); + } +} + +void CHARACTER_MANAGER::GracefulShutdown() +{ + NAME_MAP::iterator it = m_map_pkPCChr.begin(); + + while (it != m_map_pkPCChr.end()) + (it++)->second->Disconnect("GracefulShutdown"); +} + +DWORD CHARACTER_MANAGER::AllocVID() +{ + ++m_iVIDCount; + return m_iVIDCount; +} + +LPCHARACTER CHARACTER_MANAGER::CreateCharacter(const char * name, DWORD dwPID) +{ + DWORD dwVID = AllocVID(); + +#ifdef M2_USE_POOL + LPCHARACTER ch = pool_.Construct(); +#else + LPCHARACTER ch = M2_NEW CHARACTER; +#endif + ch->Create(name, dwVID, dwPID ? true : false); + + m_map_pkChrByVID.emplace(dwVID, ch); + + if (dwPID) + { + char szName[CHARACTER_NAME_MAX_LEN + 1]; + str_lower(name, szName, sizeof(szName)); + + m_map_pkPCChr.emplace(szName, ch); + m_map_pkChrByPID.emplace(dwPID, ch); + } + + return (ch); +} + +#ifndef DEBUG_ALLOC +void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch) +#else +void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch, const char* file, size_t line) +#endif +{ + if (!ch) + return; + + // Check whether it has been already deleted or not. + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(ch->GetVID()); + if (it == m_map_pkChrByVID.end()) { + sys_err("[CHARACTER_MANAGER::DestroyCharacter] %d not found", (long)(ch->GetVID())); + return; // prevent duplicated destrunction + } + + if (ch->IsNPC() && !ch->IsPet() && ch->GetRider() == NULL) + { + if (ch->GetDungeon()) + { + ch->GetDungeon()->DeadCharacter(ch); + } + } + + if (m_bUsePendingDestroy) + { + m_set_pkChrPendingDestroy.emplace(ch); + return; + } + + m_map_pkChrByVID.erase(it); + + if (true == ch->IsPC()) + { + char szName[CHARACTER_NAME_MAX_LEN + 1]; + + str_lower(ch->GetName(), szName, sizeof(szName)); + + NAME_MAP::iterator it = m_map_pkPCChr.find(szName); + + if (m_map_pkPCChr.end() != it) + m_map_pkPCChr.erase(it); + } + + if (0 != ch->GetPlayerID()) + { + itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(ch->GetPlayerID()); + + if (m_map_pkChrByPID.end() != it) + { + m_map_pkChrByPID.erase(it); + } + } + + UnregisterRaceNumMap(ch); + + RemoveFromStateList(ch); + +#ifdef M2_USE_POOL + pool_.Destroy(ch); +#else +#ifndef DEBUG_ALLOC + M2_DELETE(ch); +#else + M2_DELETE_EX(ch, file, line); +#endif +#endif +} + +LPCHARACTER CHARACTER_MANAGER::Find(DWORD dwVID) +{ + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(dwVID); + + if (m_map_pkChrByVID.end() == it) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && dwVID != (DWORD)found->GetVID()) { + sys_err("[CHARACTER_MANAGER::Find] %u != %u", dwVID, (DWORD)found->GetVID()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::Find(const VID & vid) +{ + LPCHARACTER tch = Find((DWORD) vid); + + if (!tch || tch->GetVID() != vid) + return NULL; + + return tch; +} + +LPCHARACTER CHARACTER_MANAGER::FindByPID(DWORD dwPID) +{ + itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(dwPID); + + if (m_map_pkChrByPID.end() == it) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && dwPID != found->GetPlayerID()) { + sys_err("[CHARACTER_MANAGER::FindByPID] %u != %u", dwPID, found->GetPlayerID()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::FindPC(const char * name) +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + str_lower(name, szName, sizeof(szName)); + NAME_MAP::iterator it = m_map_pkPCChr.find(szName); + + if (it == m_map_pkPCChr.end()) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && strncasecmp(szName, found->GetName(), CHARACTER_NAME_MAX_LEN) != 0) { + sys_err("[CHARACTER_MANAGER::FindPC] %s != %s", name, found->GetName()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex, bool is_aggressive) +{ + { + if (dwVnum == 5001 && !quest::CQuestManager::instance().GetEventFlag("japan_regen")) + { + sys_log(1, "WAEGU[5001] regen disabled."); + return NULL; + } + } + + { + if (dwVnum == 5002 && !quest::CQuestManager::instance().GetEventFlag("newyear_mob")) + { + sys_log(1, "HAETAE (new-year-mob) [5002] regen disabled."); + return NULL; + } + } + + { + if (dwVnum == 5004 && !quest::CQuestManager::instance().GetEventFlag("independence_day")) + { + sys_log(1, "INDEPENDECE DAY [5004] regen disabled."); + return NULL; + } + } + + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + + if (!pkMob) + { + sys_err("no mob data for vnum %u", dwVnum); + return NULL; + } + + if (!map_allow_find(lMapIndex)) + { + sys_err("not allowed map %u", lMapIndex); + return NULL; + } + + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + if (pkSectreeMap == NULL) { + return NULL; + } + + int i; + long x, y; + for (i=0; i<2000; i++) + { + x = number(1, (pkSectreeMap->m_setting.iWidth / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseX; + y = number(1, (pkSectreeMap->m_setting.iHeight / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseY; + //LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + LPSECTREE tree = pkSectreeMap->Find(x, y); + + if (!tree) + continue; + + DWORD dwAttr = tree->GetAttribute(x, y); + + if (IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT)) + continue; + + if (IS_SET(dwAttr, ATTR_BANPK)) + continue; + + break; + } + + if (i == 2000) + { + sys_err("cannot find valid location"); + return NULL; + } + + LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!sectree) + { + sys_log(0, "SpawnMobRandomPosition: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex); + return NULL; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName); + + if (!ch) + { + sys_log(0, "SpawnMobRandomPosition: cannot create new character"); + return NULL; + } + + ch->SetProto(pkMob); + + // if mob is npc with no empire assigned, assign to empire of map + if (pkMob->m_table.bType == CHAR_TYPE_NPC) + if (ch->GetEmpire() == 0) + ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex)); + + ch->SetRotation(number(0, 360)); + if (is_aggressive) //@fixme195 + ch->SetAggressive(); + + if (!ch->Show(lMapIndex, x, y, 0, false)) + { + M2_DESTROY_CHARACTER(ch); + sys_err(0, "SpawnMobRandomPosition: cannot show monster"); + return NULL; + } + + char buf[512+1]; + long local_x = x - pkSectreeMap->m_setting.iBaseX; + long local_y = y - pkSectreeMap->m_setting.iBaseY; + snprintf(buf, sizeof(buf), "spawn %s[%d] random position at %ld %ld %ld %ld (time: %d)", ch->GetName(), dwVnum, x, y, local_x, local_y, get_global_time()); + + if (test_server) + SendNotice(buf); + + sys_log(0, buf); + return (ch); +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion, int iRot, bool bShow) +{ + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + if (!pkMob) + { + sys_err("SpawnMob: no mob data for vnum %u", dwVnum); + return NULL; + } + + if (!(pkMob->m_table.bType == CHAR_TYPE_NPC || pkMob->m_table.bType == CHAR_TYPE_WARP || pkMob->m_table.bType == CHAR_TYPE_GOTO) || mining::IsVeinOfOre (dwVnum)) + { + LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!tree) + { + sys_log(0, "no sectree for spawn at %d %d mobvnum %d mapindex %d", x, y, dwVnum, lMapIndex); + return NULL; + } + + DWORD dwAttr = tree->GetAttribute(x, y); + + bool is_set = false; + + if ( mining::IsVeinOfOre (dwVnum) ) is_set = IS_SET(dwAttr, ATTR_BLOCK); + else is_set = IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT); + + if ( is_set ) + { + // SPAWN_BLOCK_LOG + static bool s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log"); + static DWORD s_nextTime=get_global_time()+10000; + + DWORD curTime=get_global_time(); + + if (curTime>s_nextTime) + { + s_nextTime=curTime; + s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log"); + + } + + if (s_isLog) + sys_log(0, "SpawnMob: BLOCKED position for spawn %s %u at %d %d (attr %u)", pkMob->m_table.szName, dwVnum, x, y, dwAttr); + // END_OF_SPAWN_BLOCK_LOG + return NULL; + } + + if (IS_SET(dwAttr, ATTR_BANPK)) + { + sys_log(0, "SpawnMob: BAN_PK position for mob spawn %s %u at %d %d", pkMob->m_table.szName, dwVnum, x, y); + return NULL; + } + } + + LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!sectree) + { + sys_log(0, "SpawnMob: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex); + return NULL; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName); + + if (!ch) + { + sys_log(0, "SpawnMob: cannot create new character"); + return NULL; + } + + if (iRot == -1) + iRot = number(0, 360); + + ch->SetProto(pkMob); + + // if mob is npc with no empire assigned, assign to empire of map + if (pkMob->m_table.bType == CHAR_TYPE_NPC) + if (ch->GetEmpire() == 0) + ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex)); + + ch->SetRotation(iRot); + + if (bShow && !ch->Show(lMapIndex, x, y, z, bSpawnMotion)) + { + M2_DESTROY_CHARACTER(ch); + sys_log(0, "SpawnMob: cannot show monster"); + return NULL; + } + + return (ch); +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException, bool bSpawnMotion, bool bAggressive ) +{ + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + + if (!pkMob) + return NULL; + + if (pkMob->m_table.bType == CHAR_TYPE_STONE) + bSpawnMotion = true; + + int i = 16; + + while (i--) + { + int x = number(sx, ex); + int y = number(sy, ey); + /* + if (bIsException) + if (is_regen_exception(x, y)) + continue; + */ + LPCHARACTER ch = SpawnMob(dwVnum, lMapIndex, x, y, 0, bSpawnMotion); + + if (ch) + { + sys_log(1, "MOB_SPAWN: %s(%d) %dx%d", ch->GetName(), (DWORD) ch->GetVID(), ch->GetX(), ch->GetY()); + if (bAggressive) + ch->SetAggressive(); + return (ch); + } + } + + return NULL; +} + +void CHARACTER_MANAGER::SelectStone(LPCHARACTER pkChr) +{ + m_pkChrSelectedStone = pkChr; +} + +bool CHARACTER_MANAGER::SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen, bool bAggressive_) +{ + CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum); + + if (!pkGroup) + { + sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex); + return false; + } + + LPCHARACTER pkChrMaster = NULL; + LPPARTY pkParty = NULL; + + const std::vector & c_rdwMembers = pkGroup->GetMemberVector(); + + bool bSpawnedByStone = false; + bool bAggressive = bAggressive_; + + if (m_pkChrSelectedStone) + { + bSpawnedByStone = true; + if (m_pkChrSelectedStone->GetDungeon()) + bAggressive = true; + } + + for (DWORD i = 0; i < c_rdwMembers.size(); ++i) + { + LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone); + + if (!tch) + { + if (i == 0) + return false; + + continue; + } + + sx = tch->GetX() - number(300, 500); + sy = tch->GetY() - number(300, 500); + ex = tch->GetX() + number(300, 500); + ey = tch->GetY() + number(300, 500); + + if (m_pkChrSelectedStone) + tch->SetStone(m_pkChrSelectedStone); + else if (pkParty) + { + pkParty->Join(tch->GetVID()); + pkParty->Link(tch); + } + else if (!pkChrMaster) + { + pkChrMaster = tch; + pkChrMaster->SetRegen(pkRegen); + + pkParty = CPartyManager::instance().CreateParty(pkChrMaster); + } + if (bAggressive) + tch->SetAggressive(); + + if (tch->Goto(tx, ty)) + tch->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + + return true; +} + +bool CHARACTER_MANAGER::SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon) +{ + const DWORD dwGroupID = CMobManager::Instance().GetGroupFromGroupGroup(dwVnum); + + if( dwGroupID != 0 ) + { + return SpawnGroup(dwGroupID, lMapIndex, sx, sy, ex, ey, pkRegen, bAggressive_, pDungeon); + } + else + { + sys_err( "NOT_EXIST_GROUP_GROUP_VNUM(%u) MAP(%ld)", dwVnum, lMapIndex ); + return false; + } +} + +LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon) +{ + CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum); + + if (!pkGroup) + { + sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex); + return NULL; + } + + LPCHARACTER pkChrMaster = NULL; + LPPARTY pkParty = NULL; + + const std::vector & c_rdwMembers = pkGroup->GetMemberVector(); + + bool bSpawnedByStone = false; + bool bAggressive = bAggressive_; + + if (m_pkChrSelectedStone) + { + bSpawnedByStone = true; + + if (m_pkChrSelectedStone->GetDungeon()) + bAggressive = true; + } + + LPCHARACTER chLeader = NULL; + + for (DWORD i = 0; i < c_rdwMembers.size(); ++i) + { + LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone); + + if (!tch) + { + if (i == 0) + return NULL; + + continue; + } + + if (i == 0) + chLeader = tch; + + tch->SetDungeon(pDungeon); + + sx = tch->GetX() - number(300, 500); + sy = tch->GetY() - number(300, 500); + ex = tch->GetX() + number(300, 500); + ey = tch->GetY() + number(300, 500); + + if (m_pkChrSelectedStone) + tch->SetStone(m_pkChrSelectedStone); + else if (pkParty) + { + pkParty->Join(tch->GetVID()); + pkParty->Link(tch); + } + else if (!pkChrMaster) + { + pkChrMaster = tch; + pkChrMaster->SetRegen(pkRegen); + + pkParty = CPartyManager::instance().CreateParty(pkChrMaster); + } + + if (bAggressive) + tch->SetAggressive(); + } + + return chLeader; +} + +struct FuncUpdateAndResetChatCounter +{ + void operator () (LPCHARACTER ch) + { + ch->ResetChatCounter(); + ch->CFSM::Update(); + } +}; + +void CHARACTER_MANAGER::Update(int iPulse) +{ + using namespace std; +#if defined(__GNUC__) && !defined(__clang__) && !defined(CXX11_ENABLED) + using namespace __gnu_cxx; +#endif + + BeginPendingDestroy(); + + { + if (!m_map_pkPCChr.empty()) + { + CHARACTER_VECTOR v; + v.reserve(m_map_pkPCChr.size()); +#if (defined(__GNUC__) && !defined(__clang__)) || defined(CXX11_ENABLED) + transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), select2nd()); +#else + transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), boost::bind(&NAME_MAP::value_type::second, _1)); +#endif + + if (0 == (iPulse % PASSES_PER_SEC(5))) + { + FuncUpdateAndResetChatCounter f; + for_each(v.begin(), v.end(), f); + } + else + { + for_each(v.begin(), v.end(), msl::bind2nd(std::mem_fn(&CHARACTER::UpdateCharacter), iPulse)); + } + } + + } + + { + if (!m_set_pkChrState.empty()) + { + CHARACTER_VECTOR v; + v.reserve(m_set_pkChrState.size()); +#if defined(__GNUC__) && !defined(__clang__) && !defined(CXX11_ENABLED) + transform(m_set_pkChrState.begin(), m_set_pkChrState.end(), back_inserter(v), identity()); +#else + v.insert(v.end(), m_set_pkChrState.begin(), m_set_pkChrState.end()); +#endif + for_each(v.begin(), v.end(), msl::bind2nd(std::mem_fn(&CHARACTER::UpdateStateMachine), iPulse)); + } + } + + { + CharacterVectorInteractor i; + + if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(xmas::MOB_SANTA_VNUM, i)) + { + for_each(i.begin(), i.end(), + msl::bind2nd(std::mem_fn(&CHARACTER::UpdateStateMachine), iPulse)); + } + } + + if (0 == (iPulse % PASSES_PER_SEC(3600))) + { + for (itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.begin(); it != m_map_dwMobKillCount.end(); ++it) + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second); + + m_map_dwMobKillCount.clear(); + } + + if (test_server && 0 == (iPulse % PASSES_PER_SEC(60))) + sys_log(0, "CHARACTER COUNT vid %zu pid %zu", m_map_pkChrByVID.size(), m_map_pkChrByPID.size()); + + FlushPendingDestroy(); + + // ShutdownManager Update + CShutdownManager::Instance().Update(); +} + +void CHARACTER_MANAGER::ProcessDelayedSave() +{ + CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.begin(); + + while (it != m_set_pkChrForDelayedSave.end()) + { + LPCHARACTER pkChr = *it++; + pkChr->SaveReal(); + } + + m_set_pkChrForDelayedSave.clear(); +} + +bool CHARACTER_MANAGER::AddToStateList(LPCHARACTER ch) +{ + assert(ch != NULL); + + CHARACTER_SET::iterator it = m_set_pkChrState.find(ch); + + if (it == m_set_pkChrState.end()) + { + m_set_pkChrState.emplace(ch); + return true; + } + + return false; +} + +void CHARACTER_MANAGER::RemoveFromStateList(LPCHARACTER ch) +{ + CHARACTER_SET::iterator it = m_set_pkChrState.find(ch); + + if (it != m_set_pkChrState.end()) + { + //sys_log(0, "RemoveFromStateList %p", ch); + m_set_pkChrState.erase(it); + } +} + +void CHARACTER_MANAGER::DelayedSave(LPCHARACTER ch) +{ + m_set_pkChrForDelayedSave.emplace(ch); +} + +bool CHARACTER_MANAGER::FlushDelayedSave(LPCHARACTER ch) +{ + CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.find(ch); + + if (it == m_set_pkChrForDelayedSave.end()) + return false; + + m_set_pkChrForDelayedSave.erase(it); + ch->SaveReal(); + return true; +} + +void CHARACTER_MANAGER::RegisterForMonsterLog(LPCHARACTER ch) +{ + m_set_pkChrMonsterLog.emplace(ch); +} + +void CHARACTER_MANAGER::UnregisterForMonsterLog(LPCHARACTER ch) +{ + m_set_pkChrMonsterLog.erase(ch); +} + +void CHARACTER_MANAGER::PacketMonsterLog(LPCHARACTER ch, const void* buf, int size) +{ + itertype(m_set_pkChrMonsterLog) it; + + for (it = m_set_pkChrMonsterLog.begin(); it!=m_set_pkChrMonsterLog.end();++it) + { + LPCHARACTER c = *it; + + if (ch && DISTANCE_APPROX(c->GetX()-ch->GetX(), c->GetY()-ch->GetY())>6000) + continue; + + LPDESC d = c->GetDesc(); + + if (d) + d->Packet(buf, size); + } +} + +void CHARACTER_MANAGER::KillLog(DWORD dwVnum) +{ + const DWORD SEND_LIMIT = 10000; + + itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.find(dwVnum); + + if (it == m_map_dwMobKillCount.end()) + m_map_dwMobKillCount.emplace(dwVnum, 1); + else + { + ++it->second; + + if (it->second > SEND_LIMIT) + { + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second); + m_map_dwMobKillCount.erase(it); + } + } +} + +void CHARACTER_MANAGER::RegisterRaceNum(DWORD dwVnum) +{ + m_set_dwRegisteredRaceNum.emplace(dwVnum); +} + +void CHARACTER_MANAGER::RegisterRaceNumMap(LPCHARACTER ch) +{ + DWORD dwVnum = ch->GetRaceNum(); + + if (m_set_dwRegisteredRaceNum.find(dwVnum) != m_set_dwRegisteredRaceNum.end()) + { + sys_log(0, "RegisterRaceNumMap %s %u", ch->GetName(), dwVnum); + m_map_pkChrByRaceNum[dwVnum].emplace(ch); + } +} + +void CHARACTER_MANAGER::UnregisterRaceNumMap(LPCHARACTER ch) +{ + DWORD dwVnum = ch->GetRaceNum(); + + itertype(m_map_pkChrByRaceNum) it = m_map_pkChrByRaceNum.find(dwVnum); + + if (it != m_map_pkChrByRaceNum.end()) + it->second.erase(ch); +} + +bool CHARACTER_MANAGER::GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i) +{ + std::map::iterator it = m_map_pkChrByRaceNum.find(dwRaceNum); + + if (it == m_map_pkChrByRaceNum.end()) + return false; + + i = it->second; + return true; +} + +#define FIND_JOB_WARRIOR_0 (1 << 3) +#define FIND_JOB_WARRIOR_1 (1 << 4) +#define FIND_JOB_WARRIOR_2 (1 << 5) +#define FIND_JOB_WARRIOR (FIND_JOB_WARRIOR_0 | FIND_JOB_WARRIOR_1 | FIND_JOB_WARRIOR_2) +#define FIND_JOB_ASSASSIN_0 (1 << 6) +#define FIND_JOB_ASSASSIN_1 (1 << 7) +#define FIND_JOB_ASSASSIN_2 (1 << 8) +#define FIND_JOB_ASSASSIN (FIND_JOB_ASSASSIN_0 | FIND_JOB_ASSASSIN_1 | FIND_JOB_ASSASSIN_2) +#define FIND_JOB_SURA_0 (1 << 9) +#define FIND_JOB_SURA_1 (1 << 10) +#define FIND_JOB_SURA_2 (1 << 11) +#define FIND_JOB_SURA (FIND_JOB_SURA_0 | FIND_JOB_SURA_1 | FIND_JOB_SURA_2) +#define FIND_JOB_SHAMAN_0 (1 << 12) +#define FIND_JOB_SHAMAN_1 (1 << 13) +#define FIND_JOB_SHAMAN_2 (1 << 14) +#define FIND_JOB_SHAMAN (FIND_JOB_SHAMAN_0 | FIND_JOB_SHAMAN_1 | FIND_JOB_SHAMAN_2) +#ifdef ENABLE_WOLFMAN_CHARACTER +#define FIND_JOB_WOLFMAN_0 (1 << 15) +#define FIND_JOB_WOLFMAN_1 (1 << 16) +#define FIND_JOB_WOLFMAN_2 (1 << 17) +#define FIND_JOB_WOLFMAN (FIND_JOB_WOLFMAN_0 | FIND_JOB_WOLFMAN_1 | FIND_JOB_WOLFMAN_2) +#endif + +// +// (job+1)*3+(skill_group) +// +LPCHARACTER CHARACTER_MANAGER::FindSpecifyPC(unsigned int uiJobFlag, long lMapIndex, LPCHARACTER except, int iMinLevel, int iMaxLevel) +{ + LPCHARACTER chFind = NULL; + itertype(m_map_pkChrByPID) it; + int n = 0; + + for (it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it) + { + LPCHARACTER ch = it->second; + + if (ch == except) + continue; + + if (ch->GetLevel() < iMinLevel) + continue; + + if (ch->GetLevel() > iMaxLevel) + continue; + + if (ch->GetMapIndex() != lMapIndex) + continue; + + if (uiJobFlag) + { + unsigned int uiChrJob = (1 << ((ch->GetJob() + 1) * 3 + ch->GetSkillGroup())); + + if (!IS_SET(uiJobFlag, uiChrJob)) + continue; + } + + if (!chFind || number(1, ++n) == 1) + chFind = ch; + } + + return chFind; +} + +int CHARACTER_MANAGER::GetMobItemRate(LPCHARACTER ch) +{ + //PREVENT_TOXICATION_FOR_CHINA + if (g_bChinaIntoxicationCheck) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0) + return m_iMobItemRatePremium/2; + return m_iMobItemRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0) + return m_iMobItemRatePremium; + return m_iMobItemRate; +} + +int CHARACTER_MANAGER::GetMobDamageRate(LPCHARACTER ch) +{ + return m_iMobDamageRate; +} + +int CHARACTER_MANAGER::GetMobGoldAmountRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobGoldAmountRate; + + //PREVENT_TOXICATION_FOR_CHINA + if (g_bChinaIntoxicationCheck) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldAmountRatePremium/2; + return m_iMobGoldAmountRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldAmountRatePremium; + return m_iMobGoldAmountRate; +} + +int CHARACTER_MANAGER::GetMobGoldDropRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobGoldDropRate; + + //PREVENT_TOXICATION_FOR_CHINA + if (g_bChinaIntoxicationCheck) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldDropRatePremium/2; + return m_iMobGoldDropRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldDropRatePremium; + return m_iMobGoldDropRate; +} + +int CHARACTER_MANAGER::GetMobExpRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobExpRate; + + //PREVENT_TOXICATION_FOR_CHINA + if (g_bChinaIntoxicationCheck) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iMobExpRatePremium/2; + return m_iMobExpRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iMobExpRatePremium; + return m_iMobExpRate; +} + +int CHARACTER_MANAGER::GetUserDamageRate(LPCHARACTER ch) +{ + if (!ch) + return m_iUserDamageRate; + + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iUserDamageRatePremium; + + return m_iUserDamageRate; +} + +void CHARACTER_MANAGER::SendScriptToMap(long lMapIndex, const std::string & s) +{ + LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + + if (NULL == pSecMap) + return; + + struct packet_script p; + + p.header = HEADER_GC_SCRIPT; + p.skin = 1; + p.src_size = s.size(); + + quest::FSendPacket f; + p.size = p.src_size + sizeof(struct packet_script); + f.buf.write(&p, sizeof(struct packet_script)); + f.buf.write(&s[0], s.size()); + + pSecMap->for_each(f); +} + +bool CHARACTER_MANAGER::BeginPendingDestroy() +{ + if (m_bUsePendingDestroy) + return false; + + m_bUsePendingDestroy = true; + return true; +} + +void CHARACTER_MANAGER::FlushPendingDestroy() +{ + using namespace std; + + m_bUsePendingDestroy = false; + + if (!m_set_pkChrPendingDestroy.empty()) + { + sys_log(0, "FlushPendingDestroy size %d", m_set_pkChrPendingDestroy.size()); + + CHARACTER_SET::iterator it = m_set_pkChrPendingDestroy.begin(), + end = m_set_pkChrPendingDestroy.end(); + for ( ; it != end; ++it) { + M2_DESTROY_CHARACTER(*it); + } + + m_set_pkChrPendingDestroy.clear(); + } +} + +CharacterVectorInteractor::CharacterVectorInteractor(const CHARACTER_SET & r) +{ + using namespace std; +#if defined(__GNUC__) && !defined(__clang__) && !defined(CXX11_ENABLED) + using namespace __gnu_cxx; +#endif + + reserve(r.size()); +#if defined(__GNUC__) && !defined(__clang__) && !defined(CXX11_ENABLED) + transform(r.begin(), r.end(), back_inserter(*this), identity()); +#else + insert(end(), r.begin(), r.end()); +#endif + + if (CHARACTER_MANAGER::instance().BeginPendingDestroy()) + m_bMyBegin = true; +} + +CharacterVectorInteractor::~CharacterVectorInteractor() +{ + if (m_bMyBegin) + CHARACTER_MANAGER::instance().FlushPendingDestroy(); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_manager.h b/source-server/Srcs/Server/game/src/char_manager.h new file mode 100644 index 000000000..77e51124e --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_manager.h @@ -0,0 +1,171 @@ +#ifndef __INC_METIN_II_GAME_CHARACTER_MANAGER_H__ +#define __INC_METIN_II_GAME_CHARACTER_MANAGER_H__ + +#ifdef M2_USE_POOL +#include "pool.h" +#endif + +#include "../../common/stl.h" +#include "../../common/length.h" + +#include "vid.h" + +class CDungeon; +class CHARACTER; +class CharacterVectorInteractor; + +class CHARACTER_MANAGER : public singleton +{ + public: + typedef std::unordered_map NAME_MAP; + + CHARACTER_MANAGER(); + virtual ~CHARACTER_MANAGER(); + + void Destroy(); + + void GracefulShutdown(); + + DWORD AllocVID(); + + LPCHARACTER CreateCharacter(const char * name, DWORD dwPID = 0); +#ifndef DEBUG_ALLOC + void DestroyCharacter(LPCHARACTER ch); +#else + void DestroyCharacter(LPCHARACTER ch, const char* file, size_t line); +#endif + + void Update(int iPulse); + + LPCHARACTER SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion = false, int iRot = -1, bool bShow = true); + LPCHARACTER SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException=false, bool bSpawnMotion = false , bool bAggressive = false); + LPCHARACTER SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL); + bool SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL); + bool SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen = NULL, bool bAggressive_ = false); + LPCHARACTER SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex, bool is_aggressive = false); + + void SelectStone(LPCHARACTER pkChrStone); + + NAME_MAP & GetPCMap() { return m_map_pkPCChr; } + + LPCHARACTER Find(DWORD dwVID); + LPCHARACTER Find(const VID & vid); + LPCHARACTER FindPC(const char * name); + LPCHARACTER FindByPID(DWORD dwPID); + + bool AddToStateList(LPCHARACTER ch); + void RemoveFromStateList(LPCHARACTER ch); + + void DelayedSave(LPCHARACTER ch); + bool FlushDelayedSave(LPCHARACTER ch); + void ProcessDelayedSave(); + + template Func for_each_pc(Func f); + + void RegisterForMonsterLog(LPCHARACTER ch); + void UnregisterForMonsterLog(LPCHARACTER ch); + void PacketMonsterLog(LPCHARACTER ch, const void* buf, int size); + + void KillLog(DWORD dwVnum); + + void RegisterRaceNum(DWORD dwVnum); + void RegisterRaceNumMap(LPCHARACTER ch); + void UnregisterRaceNumMap(LPCHARACTER ch); + bool GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i); + + LPCHARACTER FindSpecifyPC(unsigned int uiJobFlag, long lMapIndex, LPCHARACTER except=NULL, int iMinLevel = 1, int iMaxLevel = PLAYER_MAX_LEVEL_CONST); + + void SetMobItemRate(int value) { m_iMobItemRate = value; } + void SetMobDamageRate(int value) { m_iMobDamageRate = value; } + void SetMobGoldAmountRate(int value) { m_iMobGoldAmountRate = value; } + void SetMobGoldDropRate(int value) { m_iMobGoldDropRate = value; } + void SetMobExpRate(int value) { m_iMobExpRate = value; } + + void SetMobItemRatePremium(int value) { m_iMobItemRatePremium = value; } + void SetMobGoldAmountRatePremium(int value) { m_iMobGoldAmountRatePremium = value; } + void SetMobGoldDropRatePremium(int value) { m_iMobGoldDropRatePremium = value; } + void SetMobExpRatePremium(int value) { m_iMobExpRatePremium = value; } + + void SetUserDamageRatePremium(int value) { m_iUserDamageRatePremium = value; } + void SetUserDamageRate(int value ) { m_iUserDamageRate = value; } + int GetMobItemRate(LPCHARACTER ch); + int GetMobDamageRate(LPCHARACTER ch); + int GetMobGoldAmountRate(LPCHARACTER ch); + int GetMobGoldDropRate(LPCHARACTER ch); + int GetMobExpRate(LPCHARACTER ch); + + int GetUserDamageRate(LPCHARACTER ch); + void SendScriptToMap(long lMapIndex, const std::string & s); + + bool BeginPendingDestroy(); + void FlushPendingDestroy(); + + private: + int m_iMobItemRate; + int m_iMobDamageRate; + int m_iMobGoldAmountRate; + int m_iMobGoldDropRate; + int m_iMobExpRate; + + int m_iMobItemRatePremium; + int m_iMobGoldAmountRatePremium; + int m_iMobGoldDropRatePremium; + int m_iMobExpRatePremium; + + int m_iUserDamageRate; + int m_iUserDamageRatePremium; + int m_iVIDCount; + + std::unordered_map m_map_pkChrByVID; + std::unordered_map m_map_pkChrByPID; + NAME_MAP m_map_pkPCChr; + + CHARACTER_SET m_set_pkChrState; + CHARACTER_SET m_set_pkChrForDelayedSave; + CHARACTER_SET m_set_pkChrMonsterLog; + + LPCHARACTER m_pkChrSelectedStone; + + std::map m_map_dwMobKillCount; + + std::set m_set_dwRegisteredRaceNum; + std::map m_map_pkChrByRaceNum; + + bool m_bUsePendingDestroy; + CHARACTER_SET m_set_pkChrPendingDestroy; + +#ifdef M2_USE_POOL + ObjectPool pool_; +#endif +}; + +template +Func CHARACTER_MANAGER::for_each_pc(Func f) +{ + for (auto it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it) + f(it->second); + + return f; +} + +class CharacterVectorInteractor : public CHARACTER_VECTOR +{ + public: + CharacterVectorInteractor() : m_bMyBegin(false) { } + + CharacterVectorInteractor(const CHARACTER_SET & r); + virtual ~CharacterVectorInteractor(); + + private: + bool m_bMyBegin; +}; + +#ifndef DEBUG_ALLOC +#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr) +#else +#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr, __FILE__, __LINE__) +#endif + +#define M2_DESTROY_CHARACTER_EX(ptr) if (ptr) { M2_DESTROY_CHARACTER(ptr); ptr = nullptr; } +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_quickslot.cpp b/source-server/Srcs/Server/game/src/char_quickslot.cpp new file mode 100644 index 000000000..97c33e1ba --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_quickslot.cpp @@ -0,0 +1,171 @@ +#include "stdafx.h" +#include "constants.h" +#include "char.h" +#include "desc.h" +#include "desc_manager.h" +#include "packet.h" +#include "item.h" + +///////////////////////////////////////////////////////////////////////////// +// QUICKSLOT HANDLING +///////////////////////////////////////////////////////////////////////////// +void CHARACTER::SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos) +{ + if (!m_PlayerSlots) + return; + + if (bOldPos == bNewPos) + return; + + for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i) + { + if (m_PlayerSlots->pQuickslot[i].type == bType && m_PlayerSlots->pQuickslot[i].pos == bOldPos) + { + if (bNewPos == 255) + DelQuickslot(i); + else + { + TQuickslot slot; + + slot.type = bType; + slot.pos = bNewPos; + + SetQuickslot(i, slot); + } + } + } +} + +bool CHARACTER::GetQuickslot(BYTE pos, TQuickslot ** ppSlot) +{ + if (!m_PlayerSlots) + return false; + + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + *ppSlot = &m_PlayerSlots->pQuickslot[pos]; + return true; +} + +bool CHARACTER::SetQuickslot(BYTE pos, TQuickslot & rSlot) +{ + if (!m_PlayerSlots) + return false; + + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + if (rSlot.type >= QUICKSLOT_TYPE_MAX_NUM) + return false; + + struct packet_quickslot_add pack_quickslot_add; + for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i) + { + if (rSlot.type == 0) + continue; + else if (m_PlayerSlots->pQuickslot[i].type == rSlot.type && m_PlayerSlots->pQuickslot[i].pos == rSlot.pos) + DelQuickslot(i); + } + + TItemPos srcCell(INVENTORY, rSlot.pos); + + switch (rSlot.type) + { + case QUICKSLOT_TYPE_ITEM: + if (false == srcCell.IsDefaultInventoryPosition() && false == srcCell.IsBeltInventoryPosition()) + return false; + + break; + + case QUICKSLOT_TYPE_SKILL: + if ((int) rSlot.pos >= SKILL_MAX_NUM) + return false; + + break; + + case QUICKSLOT_TYPE_COMMAND: + break; + + default: + return false; + } + + m_PlayerSlots->pQuickslot[pos] = rSlot; + + if (GetDesc()) + { + pack_quickslot_add.header = HEADER_GC_QUICKSLOT_ADD; + pack_quickslot_add.pos = pos; + pack_quickslot_add.slot = m_PlayerSlots->pQuickslot[pos]; + + GetDesc()->Packet(&pack_quickslot_add, sizeof(pack_quickslot_add)); + } + + return true; +} + +bool CHARACTER::DelQuickslot(BYTE pos) +{ + if (!m_PlayerSlots) + return false; + + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + memset(&m_PlayerSlots->pQuickslot[pos], 0, sizeof(TQuickslot)); + + struct packet_quickslot_del pack_quickslot_del; + pack_quickslot_del.header = HEADER_GC_QUICKSLOT_DEL; + pack_quickslot_del.pos = pos; + + GetDesc()->Packet(&pack_quickslot_del, sizeof(pack_quickslot_del)); + return true; +} + +bool CHARACTER::SwapQuickslot(BYTE a, BYTE b) +{ + if (!m_PlayerSlots) + return false; + + if (a >= QUICKSLOT_MAX_NUM || b >= QUICKSLOT_MAX_NUM) + return false; + + TQuickslot quickslot; + quickslot = m_PlayerSlots->pQuickslot[a]; + + m_PlayerSlots->pQuickslot[a] = m_PlayerSlots->pQuickslot[b]; + m_PlayerSlots->pQuickslot[b] = quickslot; + + struct packet_quickslot_swap pack_quickslot_swap; + pack_quickslot_swap.header = HEADER_GC_QUICKSLOT_SWAP; + pack_quickslot_swap.pos = a; + pack_quickslot_swap.pos_to = b; + + GetDesc()->Packet(&pack_quickslot_swap, sizeof(pack_quickslot_swap)); + return true; +} + +void CHARACTER::ChainQuickslotItem(LPITEM pItem, BYTE bType, BYTE bOldPos) +{ + if (!m_PlayerSlots) + return; + + if (pItem->IsDragonSoul()) + return; + + for ( int i=0; i < QUICKSLOT_MAX_NUM; ++i ) + { + if ( m_PlayerSlots->pQuickslot[i].type == bType && m_PlayerSlots->pQuickslot[i].pos == bOldPos ) + { + TQuickslot slot; + slot.type = bType; + slot.pos = pItem->GetCell(); + + SetQuickslot(i, slot); + + break; + } + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_resist.cpp b/source-server/Srcs/Server/game/src/char_resist.cpp new file mode 100644 index 000000000..6ac25f78f --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_resist.cpp @@ -0,0 +1,415 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "affect.h" +#include "locale_service.h" + +const int poison_damage_rate[MOB_RANK_MAX_NUM] = +{ + 80, 50, 40, 30, 25, 1 +}; + +int GetPoisonDamageRate(LPCHARACTER ch) +{ + int iRate; + + if (ch->IsPC()) + iRate = 50; + else + iRate = poison_damage_rate[ch->GetMobRank()]; + + iRate = MAX(0, iRate - ch->GetPoint(POINT_POISON_REDUCE)); + return iRate; +} + +EVENTINFO(TPoisonEventInfo) +{ + DynamicCharacterPtr ch; + int count; + DWORD attacker_pid; + + TPoisonEventInfo() + : ch() + , count(0) + , attacker_pid(0) + { + } +}; + +EVENTFUNC(poison_event) +{ + TPoisonEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "poison_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); + + int dam = ch->GetMaxHP() * GetPoisonDamageRate(ch) / 1000; + if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Poison Damage %d", dam); + + if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_POISON)) + { + ch->m_pkPoisonEvent = NULL; + return 0; + } + + --info->count; + + if (info->count) + return PASSES_PER_SEC(3); + else + { + ch->m_pkPoisonEvent = NULL; + return 0; + } +} + +#ifdef ENABLE_WOLFMAN_CHARACTER +const int bleeding_damage_rate[MOB_RANK_MAX_NUM] = +{ + 80, 50, 40, 30, 25, 1 +}; + +int GetBleedingDamageRate(LPCHARACTER ch) +{ + int iRate; + + if (ch->IsPC()) + iRate = 50; + else + iRate = bleeding_damage_rate[ch->GetMobRank()]; + + iRate = MAX(0, iRate - ch->GetPoint(POINT_BLEEDING_REDUCE)); +#if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_ITEM_BLEEDING_AS_POISON) + iRate = MAX(0, iRate - ch->GetPoint(POINT_POISON_REDUCE)); +#endif + return iRate; +} + +EVENTINFO(TBleedingEventInfo) +{ + DynamicCharacterPtr ch; + int count; + DWORD attacker_pid; + + TBleedingEventInfo() + : ch() + , count(0) + , attacker_pid(0) + { + } +}; + +EVENTFUNC(bleeding_event) +{ + TBleedingEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "bleeding_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); + + int dam = ch->GetMaxHP() * GetBleedingDamageRate(ch) / 1000; + if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Bleeding Damage %d", dam); + + if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_BLEEDING)) + { + ch->m_pkBleedingEvent = NULL; + return 0; + } + + --info->count; + + if (info->count) + return PASSES_PER_SEC(3); + else + { + ch->m_pkBleedingEvent = NULL; + return 0; + } +} +#endif + +EVENTINFO(TFireEventInfo) +{ + DynamicCharacterPtr ch; + int count; + int amount; + DWORD attacker_pid; + + TFireEventInfo() + : ch() + , count(0) + , amount(0) + , attacker_pid(0) + { + } +}; + +EVENTFUNC(fire_event) +{ + TFireEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "fire_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); + + int dam = info->amount; + if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Fire Damage %d", dam); + + if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_FIRE)) + { + ch->m_pkFireEvent = NULL; + return 0; + } + + --info->count; + + if (info->count) + return PASSES_PER_SEC(3); + else + { + ch->m_pkFireEvent = NULL; + return 0; + } +} + +static int poison_level_adjust[9] = +{ + 100, 90, 80, 70, 50, 30, 10, 5, 0 +}; + +#ifdef ENABLE_WOLFMAN_CHARACTER +static int bleeding_level_adjust[9] = +{ + 100, 90, 80, 70, 50, 30, 10, 5, 0 +}; +#endif + +void CHARACTER::AttackedByFire(LPCHARACTER pkAttacker, int amount, int count) +{ + if (m_pkFireEvent) + return; + + AddAffect(AFFECT_FIRE, POINT_NONE, 0, AFF_FIRE, count*3+1, 0, true); + + TFireEventInfo* info = AllocEventInfo(); + + info->ch = this; + info->count = count; + info->amount = amount; + info->attacker_pid = pkAttacker->GetPlayerID(); + + m_pkFireEvent = event_create(fire_event, info, 1); +} + +void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker) +{ + if (m_pkPoisonEvent) + return; + + if (m_bHasPoisoned && !IsPC()) + return; + +#ifdef ENABLE_WOLFMAN_CHARACTER + if (m_pkBleedingEvent) + return; + + if (m_bHasBled && !IsPC()) + return; +#endif + + if (pkAttacker && pkAttacker->GetLevel() < GetLevel()) + { + int delta = GetLevel() - pkAttacker->GetLevel(); + + if (delta > 8) + delta = 8; + + if (number(1, 100) > poison_level_adjust[delta]) + return; + } + + /*if (IsImmune(IMMUNE_POISON)) + return;*/ + + m_bHasPoisoned = true; + + AddAffect(AFFECT_POISON, POINT_NONE, 0, AFF_POISON, POISON_LENGTH + 1, 0, true); + + TPoisonEventInfo* info = AllocEventInfo(); + + info->ch = this; + info->count = 10; + info->attacker_pid = pkAttacker?pkAttacker->GetPlayerID():0; + + m_pkPoisonEvent = event_create(poison_event, info, 1); + + if (test_server && pkAttacker) + { + char buf[256]; + snprintf(buf, sizeof(buf), "POISON %s -> %s", pkAttacker->GetName(), GetName()); + pkAttacker->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + } +} + +#ifdef ENABLE_WOLFMAN_CHARACTER +void CHARACTER::AttackedByBleeding(LPCHARACTER pkAttacker) +{ + if (m_pkBleedingEvent) + return; + + if (m_bHasBled && !IsPC()) + return; + + if (m_pkPoisonEvent) + return; + + if (m_bHasPoisoned && !IsPC()) + return; + + if (pkAttacker && pkAttacker->GetLevel() < GetLevel()) + { + int delta = GetLevel() - pkAttacker->GetLevel(); + + if (delta > 8) + delta = 8; + + if (number(1, 100) > bleeding_level_adjust[delta]) + return; + } + + /*if (IsImmune(IMMUNE_BLEEDING)) + return;*/ + + m_bHasBled = true; + + AddAffect(AFFECT_BLEEDING, POINT_NONE, 0, AFF_BLEEDING, BLEEDING_LENGTH + 1, 0, true); + + TBleedingEventInfo* info = AllocEventInfo(); + + info->ch = this; + info->count = 10; + info->attacker_pid = pkAttacker?pkAttacker->GetPlayerID():0; + + m_pkBleedingEvent = event_create(bleeding_event, info, 1); + + if (test_server && pkAttacker) + { + char buf[256]; + snprintf(buf, sizeof(buf), "BLEEDING %s -> %s", pkAttacker->GetName(), GetName()); + pkAttacker->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + } +} +#endif + +void CHARACTER::RemoveFire() +{ + RemoveAffect(AFFECT_FIRE); + event_cancel(&m_pkFireEvent); +} + +void CHARACTER::RemovePoison() +{ + RemoveAffect(AFFECT_POISON); + event_cancel(&m_pkPoisonEvent); +} + +#ifdef ENABLE_WOLFMAN_CHARACTER +void CHARACTER::RemoveBleeding() +{ + RemoveAffect(AFFECT_BLEEDING); + event_cancel(&m_pkBleedingEvent); +} +#endif + +void CHARACTER::ApplyMobAttribute(const TMobTable* table) +{ + for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i) + { + if (table->cEnchants[i] != 0) + ApplyPoint(aiMobEnchantApplyIdx[i], table->cEnchants[i]); + } +#if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_BLEEDING_AS_POISON) + if (table->cEnchants[MOB_ENCHANT_POISON] != 0) + ApplyPoint(APPLY_BLEEDING_PCT, table->cEnchants[MOB_ENCHANT_POISON]/50); // @warme009 +#endif + + for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i) + { + if (table->cResists[i] != 0) + ApplyPoint(aiMobResistsApplyIdx[i], table->cResists[i]); + } +#if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_CLAW_AS_DAGGER) + if (table->cResists[MOB_RESIST_DAGGER] != 0) + ApplyPoint(APPLY_RESIST_CLAW, table->cResists[MOB_RESIST_DAGGER]); +#endif +#if defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_BLEEDING_AS_POISON) + if (table->cResists[MOB_RESIST_POISON] != 0) + ApplyPoint(APPLY_BLEEDING_REDUCE, table->cResists[MOB_RESIST_POISON]); +#endif +} + +// #define ENABLE_IMMUNE_PERC +bool CHARACTER::IsImmune(DWORD dwImmuneFlag) +{ + // 1 stun, 2 slow, 4 fall = 7 all == X + // ChatPacket(CHAT_TYPE_PARTY, " (%u == %u)", m_pointsInstant.dwImmuneFlag, dwImmuneFlag); + if (IS_SET(m_pointsInstant.dwImmuneFlag, dwImmuneFlag)) + { +#ifdef ENABLE_IMMUNE_PERC + int immune_pct = 90; + int percent = number(1, 100); + + if (percent <= immune_pct) // 90% Immune +#else + if (true) +#endif + { + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s)", GetName()); + + return true; + } + else + { + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s)", GetName()); + + return false; + } + } + + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s) NO_IMMUNE_FLAG", GetName()); + + return false; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_skill.cpp b/source-server/Srcs/Server/game/src/char_skill.cpp new file mode 100644 index 000000000..79821ed1c --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_skill.cpp @@ -0,0 +1,3694 @@ +#include "stdafx.h" +#include + +#include "utils.h" +#include "config.h" +#include "vector.h" +#include "char.h" +#include "char_manager.h" +#include "battle.h" +#include "desc.h" +#include "desc_manager.h" +#include "packet.h" +#include "affect.h" +#include "item.h" +#include "sectree_manager.h" +#include "mob_manager.h" +#include "start_position.h" +#include "party.h" +#include "buffer_manager.h" +#include "guild.h" +#include "log.h" +#include "unique_item.h" +#include "questmanager.h" +#include "../../common/CommonDefines.h" + +#define ENABLE_FORCE2MASTERSKILL +// #define ENABLE_NULLIFYAFFECT_LIMIT + +static const DWORD s_adwSubSkillVnums[] = +{ + SKILL_LEADERSHIP, + SKILL_COMBO, + SKILL_MINING, + SKILL_LANGUAGE1, + SKILL_LANGUAGE2, + SKILL_LANGUAGE3, + SKILL_POLYMORPH, + SKILL_HORSE, + SKILL_HORSE_SUMMON, + SKILL_HORSE_WILDATTACK, + SKILL_HORSE_CHARGE, + SKILL_HORSE_ESCAPE, + SKILL_HORSE_WILDATTACK_RANGE, + SKILL_ADD_HP, + SKILL_RESIST_PENETRATE +}; + +time_t CHARACTER::GetSkillNextReadTime(DWORD dwVnum) const +{ + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (vnum: %u)", dwVnum); + return 0; + } + + return m_pSkillLevels ? m_pSkillLevels[dwVnum].tNextRead : 0; +} + +void CHARACTER::SetSkillNextReadTime(DWORD dwVnum, time_t time) +{ + if (m_pSkillLevels && dwVnum < SKILL_MAX_NUM) + m_pSkillLevels[dwVnum].tNextRead = time; +} + +bool TSkillUseInfo::HitOnce(DWORD dwVnum) +{ + if (!bUsed) + return false; + + sys_log(1, "__HitOnce NextUse %u current %u count %d scount %d", dwNextSkillUsableTime, get_dword_time(), iHitCount, iSplashCount); + + if (dwNextSkillUsableTime && dwNextSkillUsableTimeisGrandMaster = isGrandMaster; + DWORD dwCur = get_dword_time(); + + if (bUsed && dwNextSkillUsableTime > dwCur) + { + sys_log(0, "cooltime is not over delta %u", dwNextSkillUsableTime - dwCur); + iHitCount = 0; + return false; + } + + bUsed = true; + #ifdef ENABLE_SKILL_COOLDOWN_CHECK + this->cooldown = true; + this->skillCount = 0; + #endif + + if (dwCooltime) + dwNextSkillUsableTime = dwCur + dwCooltime; + else + dwNextSkillUsableTime = 0; + + iRange = range; + iMaxHitCount = iHitCount = hitcount; + + if (test_server) + sys_log(0, "UseSkill NextUse %u current %u cooltime %d hitcount %d/%d", dwNextSkillUsableTime, dwCur, dwCooltime, iHitCount, iMaxHitCount); + + dwVID = vid; + iSplashCount = splashcount; + return true; +} + +#ifdef ENABLE_SKILL_COOLDOWN_CHECK +bool TSkillUseInfo::IsOnCooldown(int vnum) +{ + int maxSkillCount = 0; + switch (vnum) + { + case SKILL_KWANKYEOK: + maxSkillCount = 8; + case SKILL_HORSE_WILDATTACK_RANGE: + maxSkillCount = 5; + case SKILL_HORSE_ESCAPE: + maxSkillCount = 10; + } + + this->skillCount++; + if (maxSkillCount) + return this->skillCount > maxSkillCount; + + auto ret = !this->cooldown; + this->cooldown = false; + return ret; +} +#endif + +int CHARACTER::GetChainLightningMaxCount() const +{ + return aiChainLightningCountBySkillLevel[MIN(SKILL_MAX_LEVEL, GetSkillLevel(SKILL_CHAIN))]; +} + +void CHARACTER::SetAffectedEunhyung() +{ + m_dwAffectedEunhyungLevel = GetSkillPower(SKILL_EUNHYUNG); +} + +void CHARACTER::SetSkillGroup(BYTE bSkillGroup) +{ + if (bSkillGroup > 2) + return; + + if (GetLevel() < 5) + return; + + m_points.skill_group = bSkillGroup; + + TPacketGCChangeSkillGroup p; + p.header = HEADER_GC_SKILL_GROUP; + p.skill_group = m_points.skill_group; + + GetDesc()->Packet(&p, sizeof(TPacketGCChangeSkillGroup)); +} + +int CHARACTER::ComputeCooltime(int time) +{ + return CalculateDuration(GetPoint(POINT_CASTING_SPEED), time); +} + +void CHARACTER::SkillLevelPacket() +{ + if (!GetDesc()) + return; + + TPacketGCSkillLevel pack; + + pack.bHeader = HEADER_GC_SKILL_LEVEL; + thecore_memcpy(&pack.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + GetDesc()->Packet(&pack, sizeof(TPacketGCSkillLevel)); +} + +void CHARACTER::SetSkillLevel(DWORD dwVnum, BYTE bLev) +{ + if (NULL == m_pSkillLevels) + return; + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (vnum %u)", dwVnum); + return; + } + + m_pSkillLevels[dwVnum].bLevel = MIN(40, bLev); + + if (bLev >= 40) + m_pSkillLevels[dwVnum].bMasterType = SKILL_PERFECT_MASTER; + else if (bLev >= 30) + m_pSkillLevels[dwVnum].bMasterType = SKILL_GRAND_MASTER; + else if (bLev >= 20) + m_pSkillLevels[dwVnum].bMasterType = SKILL_MASTER; + else + m_pSkillLevels[dwVnum].bMasterType = SKILL_NORMAL; +} + +bool CHARACTER::IsLearnableSkill(DWORD dwSkillVnum) const +{ + const CSkillProto * pkSkill = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSkill) + return false; + + if (GetSkillLevel(dwSkillVnum) >= SKILL_MAX_LEVEL) + return false; + + if (pkSkill->dwType == 0) + { + if (GetSkillLevel(dwSkillVnum) >= pkSkill->bMaxLevel) + return false; + + return true; + } + + if (pkSkill->dwType == 5) + { + if (dwSkillVnum == SKILL_HORSE_WILDATTACK_RANGE && GetJob() != JOB_ASSASSIN) + return false; + + return true; + } + + if (GetSkillGroup() == 0) + return false; + + if (pkSkill->dwType - 1 == GetJob()) + return true; +#ifdef ENABLE_WOLFMAN_CHARACTER + + if (7 == pkSkill->dwType && JOB_WOLFMAN == GetJob()) + return true; +#endif + if (6 == pkSkill->dwType) + { + if (SKILL_7_A_ANTI_TANHWAN <= dwSkillVnum && dwSkillVnum <= SKILL_7_D_ANTI_YONGBI) + { + for (int i=0 ; i < 4 ; i++) + { + if (unsigned(SKILL_7_A_ANTI_TANHWAN + i) != dwSkillVnum) + { + if (0 != GetSkillLevel(SKILL_7_A_ANTI_TANHWAN + i)) + { + return false; + } + } + } + + return true; + } + + if (SKILL_8_A_ANTI_GIGONGCHAM <= dwSkillVnum && dwSkillVnum <= SKILL_8_D_ANTI_BYEURAK) + { + for (int i=0 ; i < 4 ; i++) + { + if (unsigned(SKILL_8_A_ANTI_GIGONGCHAM + i) != dwSkillVnum) + { + if (0 != GetSkillLevel(SKILL_8_A_ANTI_GIGONGCHAM + i)) + return false; + } + } + + return true; + } + } + + return false; +} + +// ADD_GRANDMASTER_SKILL +bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum) +{ + CSkillProto * pkSk = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSk) + return false; + + if (!IsLearnableSkill(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ųԴϴ.")); + return false; + } + + sys_log(0, "learn grand master skill[%d] cur %d, next %d", dwSkillVnum, get_global_time(), GetSkillNextReadTime(dwSkillVnum)); + + if (pkSk->dwType == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ųԴϴ.")); + return false; + } + + if (GetSkillMasterType(dwSkillVnum) != SKILL_GRAND_MASTER) + { + if (GetSkillMasterType(dwSkillVnum) > SKILL_GRAND_MASTER) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ʈ ͵ ųԴϴ. ̻ ϴ.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų ׷ ̸ ʾҽϴ.")); + return false; + } + + std::string strTrainSkill; + { + std::ostringstream os; + os << "training_grandmaster_skill.skill" << dwSkillVnum; + strTrainSkill = os.str(); + } + + BYTE bLastLevel = GetSkillLevel(dwSkillVnum); + + int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 30); + + sys_log(0, "LearnGrandMasterSkill %s table idx %d value %d", GetName(), idx, aiGrandMasterSkillBookCountForLevelUp[idx]); + + int iTotalReadCount = GetQuestFlag(strTrainSkill) + 1; + SetQuestFlag(strTrainSkill, iTotalReadCount); + + int iMinReadCount = aiGrandMasterSkillBookMinCount[idx]; + int iMaxReadCount = aiGrandMasterSkillBookMaxCount[idx]; + + int iBookCount = aiGrandMasterSkillBookCountForLevelUp[idx]; + + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + if (iBookCount&1) + iBookCount = iBookCount / 2 + 1; + else + iBookCount = iBookCount / 2; + + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + + int n = number(1, iBookCount); + sys_log(0, "Number(%d)", n); + + DWORD nextTime = get_global_time() + number(g_dwSkillBookNextReadMin, g_dwSkillBookNextReadMax); + + sys_log(0, "GrandMaster SkillBookCount min %d cur %d max %d (next_time=%d)", iMinReadCount, iTotalReadCount, iMaxReadCount, nextTime); + + bool bSuccess = n == 2; + + if (iTotalReadCount < iMinReadCount) + bSuccess = false; + if (iTotalReadCount > iMaxReadCount) + bSuccess = true; + + if (bSuccess) + { + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_QUEST); + } + + SetSkillNextReadTime(dwSkillVnum, nextTime); + + if (bLastLevel == GetSkillLevel(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ, Ⱑ ϰ ־! ̰ ȭԸΰ!? !")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" з ϴ. ٽ ֽñ ٶϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_FAIL", ""); + return false; + } + + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̾!")); + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("߰ſ ġ ־! ̰, ̰!")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_SUCCESS", ""); + return true; +} +// END_OF_ADD_GRANDMASTER_SKILL + +static bool FN_should_check_exp(LPCHARACTER ch) +{ + // @warme005 + return ch->GetLevel() < gPlayerMaxLevel; +} + +// #define ENABLE_MASTER_SKILLBOOK_NO_STEPS +bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb) +{ + const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSk) + return false; + + if (!IsLearnableSkill(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ųԴϴ.")); + return false; + } + + DWORD need_exp = 0; + + if (FN_should_check_exp(this)) + { + need_exp = 20000; + + if ( GetExp() < need_exp ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ Ͽ å ϴ.")); + return false; + } + } + + if (pkSk->dwType != 0) + { + if (GetSkillMasterType(dwSkillVnum) != SKILL_MASTER) + { + if (GetSkillMasterType(dwSkillVnum) > SKILL_MASTER) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų å ̻ ϴ.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų å ̸ ʾҽϴ.")); + return false; + } + } + + if (get_global_time() < GetSkillNextReadTime(dwSkillVnum)) + { + if (!(test_server && quest::CQuestManager::instance().GetEventFlag("no_read_delay"))) + { + if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY)) + { + RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("־ȼ ȭԸ Խϴ.")); + } + else + { + SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time()); + return false; + } + } + } + + BYTE bLastLevel = GetSkillLevel(dwSkillVnum); + + if (bProb != 0) + { + // SKILL_BOOK_BONUS + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + bProb += bProb / 2; + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + // END_OF_SKILL_BOOK_BONUS + + sys_log(0, "LearnSkillByBook Pct %u prob %d", dwSkillVnum, bProb); + + if (number(1, 100) <= bProb) + { + if (test_server) + sys_log(0, "LearnSkillByBook %u SUCC", dwSkillVnum); + + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK); + } + else + { + if (test_server) + sys_log(0, "LearnSkillByBook %u FAIL", dwSkillVnum); + } + } + else + { + int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 20); + + sys_log(0, "LearnSkillByBook %s table idx %d value %d", GetName(), idx, aiSkillBookCountForLevelUp[idx]); + + { + int need_bookcount = GetSkillLevel(dwSkillVnum) - 20; + + PointChange(POINT_EXP, -need_exp); + + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC) + { + char flag[128+1]; + memset(flag, 0, sizeof(flag)); + snprintf(flag, sizeof(flag), "traning_master_skill.%u.read_count", dwSkillVnum); + + int read_count = pPC->GetFlag(flag); + int percent = 65; + + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + percent = 0; + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + + if (number(1, 100) > percent) + { +#ifdef ENABLE_MASTER_SKILLBOOK_NO_STEPS + if (true) +#else + if (read_count >= need_bookcount) +#endif + { + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK); + pPC->SetFlag(flag, 0); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", ""); + return true; + } + else + { + pPC->SetFlag(flag, read_count + 1); + + switch (number(1, 3)) + { + case 1: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ذ Ǿ ѵ ѵ..")); + break; + + case 2: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̴ ǰ... ϱⰡ ʹ ..")); + break; + + case 3: + default: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ϴ ͸ ִ ̴..")); + break; + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d о Ϸ ֽϴ."), need_bookcount - read_count); + return true; + } + } + } + else + { + } + } + } + + if (bLastLevel != GetSkillLevel(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̾!")); + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("߰ſ ġ ־! ̰, ̰!")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", ""); + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ, Ⱑ ϰ ־! ̰ ȭԸΰ!? !")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" з ϴ. ٽ ֽñ ٶϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_FAIL", ""); + } + + return true; +} + +bool CHARACTER::SkillLevelDown(DWORD dwVnum) +{ + if (NULL == m_pSkillLevels) + return false; + + if (g_bSkillDisable) + return false; + + if (IsPolymorphed()) + return false; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + { + sys_err("There is no such skill by number %u", dwVnum); + return false; + } + + if (!IsLearnableSkill(dwVnum)) + return false; + + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL) + return false; + + if (!GetSkillGroup()) + return false; + + if (pkSk->dwVnum >= SKILL_MAX_NUM) + return false; + + if (m_pSkillLevels[pkSk->dwVnum].bLevel == 0) + return false; + + int idx = POINT_SKILL; + switch (pkSk->dwType) + { + case 0: + idx = POINT_SUB_SKILL; + break; + case 1: + case 2: + case 3: + case 4: + case 6: +#ifdef ENABLE_WOLFMAN_CHARACTER + case 7: +#endif + idx = POINT_SKILL; + break; + case 5: + idx = POINT_HORSE_SKILL; + break; + default: + sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum); + return false; + + } + + PointChange(idx, +1); + SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel - 1); + + sys_log(0, "SkillDown: %s %u %u %u type %u", GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, pkSk->dwType); + Save(); + + ComputePoints(); + SkillLevelPacket(); + return true; +} + +void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod) +{ + if (NULL == m_pSkillLevels) + return; + + if (g_bSkillDisable) + return; + + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (SKILL_7_A_ANTI_TANHWAN <= dwVnum && dwVnum <= SKILL_8_D_ANTI_BYEURAK) + { + if (0 == GetSkillLevel(dwVnum)) + return; + } + + const CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + { + sys_err("There is no such skill by number (vnum %u)", dwVnum); + return; + } + + if (pkSk->dwVnum >= SKILL_MAX_NUM) + { + sys_err("Skill Vnum overflow (vnum %u)", dwVnum); + return; + } + + if (!IsLearnableSkill(dwVnum)) + return; + + if (pkSk->dwType != 0) + { + switch (GetSkillMasterType(pkSk->dwVnum)) + { + case SKILL_GRAND_MASTER: + if (bMethod != SKILL_UP_BY_QUEST) + return; + break; + + case SKILL_PERFECT_MASTER: + return; + } + } + + if (bMethod == SKILL_UP_BY_POINT) + { + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL) + return; + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_DISABLE_BY_POINT_UP)) + return; + } + else if (bMethod == SKILL_UP_BY_BOOK) + { + if (pkSk->dwType != 0) + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_MASTER) + return; + } + + if (GetLevel() < pkSk->bLevelLimit) + return; + + if (pkSk->preSkillVnum) + if (GetSkillMasterType(pkSk->preSkillVnum) == SKILL_NORMAL && + GetSkillLevel(pkSk->preSkillVnum) < pkSk->preSkillLevel) + return; + + if (!GetSkillGroup()) + return; + + if (bMethod == SKILL_UP_BY_POINT) + { + int idx; + + switch (pkSk->dwType) + { + case 0: + idx = POINT_SUB_SKILL; + break; + + case 1: + case 2: + case 3: + case 4: + case 6: +#ifdef ENABLE_WOLFMAN_CHARACTER + case 7: +#endif + idx = POINT_SKILL; + break; + + case 5: + idx = POINT_HORSE_SKILL; + break; + + default: + sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum); + return; + } + + if (GetPoint(idx) < 1) + return; + + PointChange(idx, -1); + } + + int SkillPointBefore = GetSkillLevel(pkSk->dwVnum); + SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel + 1); + + if (pkSk->dwType != 0) + { + switch (GetSkillMasterType(pkSk->dwVnum)) + { + case SKILL_NORMAL: + + if (GetSkillLevel(pkSk->dwVnum) >= 17) + { +#ifdef ENABLE_FORCE2MASTERSKILL + SetSkillLevel(pkSk->dwVnum, 20); +#else + if (GetQuestFlag("reset_scroll.force_to_master_skill") > 0) + { + SetSkillLevel(pkSk->dwVnum, 20); + SetQuestFlag("reset_scroll.force_to_master_skill", 0); + } + else + { + if (number(1, 21 - MIN(20, GetSkillLevel(pkSk->dwVnum))) == 1) + SetSkillLevel(pkSk->dwVnum, 20); + } +#endif + } + break; + + case SKILL_MASTER: + if (GetSkillLevel(pkSk->dwVnum) >= 30) + { + if (number(1, 31 - MIN(30, GetSkillLevel(pkSk->dwVnum))) == 1) + SetSkillLevel(pkSk->dwVnum, 30); + } + break; + + case SKILL_GRAND_MASTER: + if (GetSkillLevel(pkSk->dwVnum) >= 40) + { + SetSkillLevel(pkSk->dwVnum, 40); + } + break; + } + } + + char szSkillUp[1024]; + + snprintf(szSkillUp, sizeof(szSkillUp), "SkillUp: %s %u %d %d[Before:%d] type %u", + GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, SkillPointBefore, pkSk->dwType); + + sys_log(0, "%s", szSkillUp); + + LogManager::instance().CharLog(this, pkSk->dwVnum, "SKILLUP", szSkillUp); + Save(); + + ComputePoints(); + SkillLevelPacket(); +} + +void CHARACTER::ComputeSkillPoints() +{ + if (g_bSkillDisable) + return; +} + +void CHARACTER::ResetSkill() +{ + if (NULL == m_pSkillLevels) + return; + + std::vector > vec; + size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]); + + for (size_t i = 0; i < count; ++i) + { + if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM) + continue; + + vec.emplace_back(s_adwSubSkillVnums[i], m_pSkillLevels[s_adwSubSkillVnums[i]]); + } + + memset(m_pSkillLevels, 0, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + + std::vector >::const_iterator iter = vec.begin(); + + while (iter != vec.end()) + { + const std::pair& pair = *(iter++); + m_pSkillLevels[pair.first] = pair.second; + } + + ComputePoints(); + SkillLevelPacket(); +} + +void CHARACTER::ComputePassiveSkill(DWORD dwVnum) +{ + if (g_bSkillDisable) + return; + + if (GetSkillLevel(dwVnum) == 0) + return; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + pkSk->SetPointVar("k", GetSkillLevel(dwVnum)); + int iAmount = (int) pkSk->kPointPoly.Eval(); + + sys_log(2, "%s passive #%d on %d amount %d", GetName(), dwVnum, pkSk->bPointOn, iAmount); + PointChange(pkSk->bPointOn, iAmount); +} + +struct FFindNearVictim +{ + FFindNearVictim(LPCHARACTER center, LPCHARACTER attacker, const CHARACTER_SET& excepts_set = empty_set_) + : m_pkChrCenter(center), + m_pkChrNextTarget(NULL), + m_pkChrAttacker(attacker), + m_count(0), + m_excepts_set(excepts_set) + { + } + + void operator ()(LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!m_excepts_set.empty()) { + if (m_excepts_set.find(pkChr) != m_excepts_set.end()) + return; + } + + if (m_pkChrCenter == pkChr) + return; + + if (!battle_is_attackable(m_pkChrAttacker, pkChr)) + { + return; + } + + if (abs(m_pkChrCenter->GetX() - pkChr->GetX()) > 1000 || abs(m_pkChrCenter->GetY() - pkChr->GetY()) > 1000) + return; + + float fDist = DISTANCE_APPROX(m_pkChrCenter->GetX() - pkChr->GetX(), m_pkChrCenter->GetY() - pkChr->GetY()); + + if (fDist < 1000) + { + ++m_count; + + if ((m_count == 1) || number(1, m_count) == 1) + m_pkChrNextTarget = pkChr; + } + } + + LPCHARACTER GetVictim() + { + return m_pkChrNextTarget; + } + + LPCHARACTER m_pkChrCenter; + LPCHARACTER m_pkChrNextTarget; + LPCHARACTER m_pkChrAttacker; + int m_count; + const CHARACTER_SET & m_excepts_set; +private: + static CHARACTER_SET empty_set_; +}; + +CHARACTER_SET FFindNearVictim::empty_set_; + +EVENTINFO(chain_lightning_event_info) +{ + DWORD dwVictim; + DWORD dwChr; + + chain_lightning_event_info() + : dwVictim(0) + , dwChr(0) + { + } +}; + +EVENTFUNC(ChainLightningEvent) +{ + chain_lightning_event_info * info = dynamic_cast( event->info ); + + LPCHARACTER pkChrVictim = CHARACTER_MANAGER::instance().Find(info->dwVictim); + LPCHARACTER pkChr = CHARACTER_MANAGER::instance().Find(info->dwChr); + LPCHARACTER pkTarget = NULL; + + if (!pkChr || !pkChrVictim) + { + sys_log(1, "use chainlighting, but no character"); + return 0; + } + + sys_log(1, "chainlighting event %s", pkChr->GetName()); + + if (pkChrVictim->GetParty()) + { + pkTarget = pkChrVictim->GetParty()->GetNextOwnership(NULL, pkChrVictim->GetX(), pkChrVictim->GetY()); + if (pkTarget == pkChrVictim || !number(0, 2) || pkChr->GetChainLightingExcept().find(pkTarget) != pkChr->GetChainLightingExcept().end()) + pkTarget = NULL; + } + + if (!pkTarget) + { + // 1. Find Next victim + FFindNearVictim f(pkChrVictim, pkChr, pkChr->GetChainLightingExcept()); + + if (pkChrVictim->GetSectree()) + { + pkChrVictim->GetSectree()->ForEachAround(f); + // 2. If exist, compute it again + pkTarget = f.GetVictim(); + } + } + + if (pkTarget) + { + pkChrVictim->CreateFly(FLY_CHAIN_LIGHTNING, pkTarget); + pkChr->ComputeSkill(SKILL_CHAIN, pkTarget); + pkChr->AddChainLightningExcept(pkTarget); + } + else + { + sys_log(1, "%s use chainlighting, but find victim failed near %s", pkChr->GetName(), pkChrVictim->GetName()); + } + + return 0; +} + +void SetPolyVarForAttack(LPCHARACTER ch, CSkillProto * pkSk, LPITEM pkWeapon) +{ + if (ch->IsPC()) + { + if (pkWeapon && pkWeapon->GetType() == ITEM_WEAPON) + { + int iWep = number(pkWeapon->GetValue(3), pkWeapon->GetValue(4)); + iWep += pkWeapon->GetValue(5); + + int iMtk = number(pkWeapon->GetValue(1), pkWeapon->GetValue(2)); + iMtk += pkWeapon->GetValue(5); + + pkSk->SetPointVar("wep", iWep); + pkSk->SetPointVar("mtk", iMtk); + pkSk->SetPointVar("mwep", iMtk); + } + else + { + pkSk->SetPointVar("wep", 0); + pkSk->SetPointVar("mtk", 0); + pkSk->SetPointVar("mwep", 0); + } + } + else + { + int iWep = number(ch->GetMobDamageMin(), ch->GetMobDamageMax()); + pkSk->SetPointVar("wep", iWep); + pkSk->SetPointVar("mwep", iWep); + pkSk->SetPointVar("mtk", iWep); + } +} + +struct FuncSplashDamage +{ + FuncSplashDamage(int x, int y, CSkillProto * pkSk, LPCHARACTER pkChr, int iAmount, int iAG, int iMaxHit, LPITEM pkWeapon, bool bDisableCooltime, TSkillUseInfo* pInfo, BYTE bUseSkillPower) + : + m_x(x), m_y(y), m_pkSk(pkSk), m_pkChr(pkChr), m_iAmount(iAmount), m_iAG(iAG), m_iCount(0), m_iMaxHit(iMaxHit), m_pkWeapon(pkWeapon), m_bDisableCooltime(bDisableCooltime), m_pInfo(pInfo), m_bUseSkillPower(bUseSkillPower) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + { + //if (m_pkSk->dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN target not character %s", m_pkChr->GetName()); + return; + } + + LPCHARACTER pkChrVictim = (LPCHARACTER) ent; + + if (DISTANCE_APPROX(m_x - pkChrVictim->GetX(), m_y - pkChrVictim->GetY()) > m_pkSk->iSplashRange) + { + if(test_server) + sys_log(0, "XXX target too far %s", m_pkChr->GetName()); + return; + } + + if (!battle_is_attackable(m_pkChr, pkChrVictim)) + { + if(test_server) + sys_log(0, "XXX target not attackable %s", m_pkChr->GetName()); + return; + } + + if (m_pkChr->IsPC()) + + if (!(m_pkSk->dwVnum >= GUILD_SKILL_START && m_pkSk->dwVnum <= GUILD_SKILL_END)) + if (!m_bDisableCooltime && m_pInfo && !m_pInfo->HitOnce(m_pkSk->dwVnum) && m_pkSk->dwVnum != SKILL_MUYEONG) + { + if(test_server) + sys_log(0, "check guild skill %s", m_pkChr->GetName()); + return; + } + + ++m_iCount; + + int iDam; + + //////////////////////////////////////////////////////////////////////////////// + //float k = 1.0f * m_pkChr->GetSkillPower(m_pkSk->dwVnum) * m_pkSk->bMaxLevel / 100; + //m_pkSk->kPointPoly2.SetVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetPointVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetPointVar("lv", m_pkChr->GetLevel()); + m_pkSk->SetPointVar("iq", m_pkChr->GetPoint(POINT_IQ)); + m_pkSk->SetPointVar("str", m_pkChr->GetPoint(POINT_ST)); + m_pkSk->SetPointVar("dex", m_pkChr->GetPoint(POINT_DX)); + m_pkSk->SetPointVar("con", m_pkChr->GetPoint(POINT_HT)); + m_pkSk->SetPointVar("def", m_pkChr->GetPoint(POINT_DEF_GRADE)); + m_pkSk->SetPointVar("odef", m_pkChr->GetPoint(POINT_DEF_GRADE) - m_pkChr->GetPoint(POINT_DEF_GRADE_BONUS)); + m_pkSk->SetPointVar("horse_level", m_pkChr->GetHorseLevel()); + + //int iPenetratePct = (int)(1 + k*4); + bool bIgnoreDefense = false; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_PENETRATE)) + { + int iPenetratePct = (int) m_pkSk->kPointPoly2.Eval(); + + if (number(1, 100) <= iPenetratePct) + bIgnoreDefense = true; + } + + bool bIgnoreTargetRating = false; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_IGNORE_TARGET_RATING)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + + if (number(1, 100) <= iPct) + bIgnoreTargetRating = true; + } + + m_pkSk->SetPointVar("ar", CalcAttackRating(m_pkChr, pkChrVictim, bIgnoreTargetRating)); + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + m_pkSk->SetPointVar("atk", CalcMeleeDamage(m_pkChr, pkChrVictim, true, bIgnoreTargetRating)); + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + + if (1 == m_pkChr->GetArrowAndBow(&pkBow, &pkArrow, 1)) + m_pkSk->SetPointVar("atk", CalcArrowDamage(m_pkChr, pkChrVictim, pkBow, pkArrow, true)); + else + m_pkSk->SetPointVar("atk", 0); + } + + if (m_pkSk->bPointOn == POINT_MOV_SPEED) + m_pkSk->kPointPoly.SetVar("maxv", pkChrVictim->GetLimitPoint(POINT_MOV_SPEED)); + + m_pkSk->SetPointVar("maxhp", pkChrVictim->GetMaxHP()); + m_pkSk->SetPointVar("maxsp", pkChrVictim->GetMaxSP()); + + m_pkSk->SetPointVar("chain", m_pkChr->GetChainLightningIndex()); + m_pkChr->IncChainLightningIndex(); + + bool bUnderEunhyung = m_pkChr->GetAffectedEunhyung() > 0; + + m_pkSk->SetPointVar("ek", m_pkChr->GetAffectedEunhyung()*1./100); + //m_pkChr->ClearAffectedEunhyung(); + SetPolyVarForAttack(m_pkChr, m_pkSk, m_pkWeapon); + + int iAmount = 0; + + if (m_pkChr->GetUsedSkillMasterType(m_pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + iAmount = (int) m_pkSk->kMasterBonusPoly.Eval(); + } + else + { + iAmount = (int) m_pkSk->kPointPoly.Eval(); + } + + if (test_server && iAmount == 0 && m_pkSk->bPointOn != POINT_NONE) + { + m_pkChr->ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + //////////////////////////////////////////////////////////////////////////////// + iAmount = -iAmount; + + if (m_pkSk->dwVnum == SKILL_AMSEOP) + { + float fDelta = GetDegreeDelta(m_pkChr->GetRotation(), pkChrVictim->GetRotation()); + float adjust; + + if (fDelta < 35.0f) + { + adjust = 1.5f; + + if (bUnderEunhyung) + adjust += 0.5f; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + { + adjust += 0.5f; + } + } + else + { + adjust = 1.0f; + + if (bUnderEunhyung) + adjust += 0.5f; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + adjust += 0.5f; + } + + iAmount = (int) (iAmount * adjust); + } + else if (m_pkSk->dwVnum == SKILL_GUNGSIN) + { + float adjust = 1.0; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + { + adjust = 1.35f; + } + + iAmount = (int) (iAmount * adjust); + } +#ifdef ENABLE_WOLFMAN_CHARACTER + else if (m_pkSk->dwVnum == SKILL_GONGDAB) + { + float adjust = 1.0; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_CLAW) + { + adjust = 1.35f; + } + + iAmount = (int)(iAmount * adjust); + } +#endif + //////////////////////////////////////////////////////////////////////////////// + //sys_log(0, "name: %s skill: %s amount %d to %s", m_pkChr->GetName(), m_pkSk->szName, iAmount, pkChrVictim->GetName()); + + iDam = CalcBattleDamage(iAmount, m_pkChr->GetLevel(), pkChrVictim->GetLevel()); + + if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() != (DWORD) pkChrVictim->GetVID()) + { + iDam = (int) (iDam * m_pkSk->kSplashAroundDamageAdjustPoly.Eval()); + } + + EDamageType dt = DAMAGE_TYPE_NONE; + + switch (m_pkSk->bSkillAttrType) + { + case SKILL_ATTR_TYPE_NORMAL: + break; + + case SKILL_ATTR_TYPE_MELEE: + { + dt = DAMAGE_TYPE_MELEE; + + iDam = CalcDamBonus(m_pkChr, pkChrVictim, iDam); + + if (!bIgnoreDefense) + iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE); + } + break; + + case SKILL_ATTR_TYPE_RANGE: + dt = DAMAGE_TYPE_RANGE; + + //iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE); + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_BOW)) / 100; + break; + + case SKILL_ATTR_TYPE_MAGIC: + dt = DAMAGE_TYPE_MAGIC; + iDam = CalcAttBonus(m_pkChr, pkChrVictim, iDam); + + //iDam -= pkChrVictim->GetPoint(POINT_MAGIC_DEF_GRADE); +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + { + const int resist_magic = MINMAX(0, pkChrVictim->GetPoint(POINT_RESIST_MAGIC), 100); + const int resist_magic_reduction = MINMAX(0, (m_pkChr->GetJob()==JOB_SURA) ? m_pkChr->GetPoint(POINT_RESIST_MAGIC_REDUCTION)/2 : m_pkChr->GetPoint(POINT_RESIST_MAGIC_REDUCTION), 50); + const int total_res_magic = MINMAX(0, resist_magic - resist_magic_reduction, 100); + iDam = iDam * (100 - total_res_magic) / 100; + } +#else + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_MAGIC)) / 100; +#endif + break; + + default: + sys_err("Unknown skill attr type %u vnum %u", m_pkSk->bSkillAttrType, m_pkSk->dwVnum); + break; + } + + if (pkChrVictim->IsNPC()) + { + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_WIND)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_WIND)) / 100; + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_ELEC)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_ELEC)) / 100; + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_FIRE)) / 100; + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_COMPUTE_MAGIC_DAMAGE)) + dt = DAMAGE_TYPE_MAGIC; + + if (pkChrVictim->CanBeginFight()) + pkChrVictim->BeginFight(m_pkChr); + + if (m_pkSk->dwVnum == SKILL_CHAIN) + sys_log(0, "%s CHAIN INDEX %d DAM %d DT %d", m_pkChr->GetName(), m_pkChr->GetChainLightningIndex() - 1, iDam, dt); + + { + BYTE AntiSkillID = 0; + + switch (m_pkSk->dwVnum) + { + case SKILL_TANHWAN: AntiSkillID = SKILL_7_A_ANTI_TANHWAN; break; + case SKILL_AMSEOP: AntiSkillID = SKILL_7_B_ANTI_AMSEOP; break; + case SKILL_SWAERYUNG: AntiSkillID = SKILL_7_C_ANTI_SWAERYUNG; break; + case SKILL_YONGBI: AntiSkillID = SKILL_7_D_ANTI_YONGBI; break; + case SKILL_GIGONGCHAM: AntiSkillID = SKILL_8_A_ANTI_GIGONGCHAM; break; + case SKILL_YEONSA: AntiSkillID = SKILL_8_B_ANTI_YEONSA; break; + case SKILL_MAHWAN: AntiSkillID = SKILL_8_C_ANTI_MAHWAN; break; + case SKILL_BYEURAK: AntiSkillID = SKILL_8_D_ANTI_BYEURAK; break; + } + + if (0 != AntiSkillID) + { + BYTE AntiSkillLevel = pkChrVictim->GetSkillLevel(AntiSkillID); + + if (0 != AntiSkillLevel) + { + CSkillProto* pkSk = CSkillManager::instance().Get(AntiSkillID); + if (!pkSk) + { + sys_err ("There is no anti skill(%d) in skill proto", AntiSkillID); + } + else + { + pkSk->SetPointVar("k", 1.0f * pkChrVictim->GetSkillPower(AntiSkillID) * pkSk->bMaxLevel / 100); + + double ResistAmount = pkSk->kPointPoly.Eval(); + + sys_log(0, "ANTI_SKILL: Resist(%lf) Orig(%d) Reduce(%d)", ResistAmount, iDam, int(iDam * (ResistAmount/100.0))); + + iDam -= iDam * (ResistAmount/100.0); + } + } + } + } + + if (!pkChrVictim->Damage(m_pkChr, iDam, dt) && !pkChrVictim->IsStun() && !pkChrVictim->IsDead()) // @fixme312 IsDead + { + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_REMOVE_GOOD_AFFECT)) + { +#ifdef ENABLE_NULLIFYAFFECT_LIMIT + int iLevel = m_pkChr->GetLevel(); + int yLevel = pkChrVictim->GetLevel(); + // const float k = 1.0 * m_pkChr->GetSkillPower(m_pkSk->dwVnum, bSkillLevel) * m_pkSk->bMaxLevel / 100; + int iDifLev = 9; + if ((iLevel-iDifLev <= yLevel) && (iLevel+iDifLev >= yLevel)) +#endif + { + int iAmount2 = (int) m_pkSk->kPointPoly2.Eval(); + int iDur2 = (int) m_pkSk->kDurationPoly2.Eval(); + iDur2 += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (number(1, 100) <= iAmount2) + { + pkChrVictim->RemoveGoodAffect(); + pkChrVictim->AddAffect(m_pkSk->dwVnum, POINT_NONE, 0, AFF_PABEOP, iDur2, 0, true); + } + } + } +#ifdef ENABLE_WOLFMAN_CHARACTER + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW | SKILL_FLAG_STUN | SKILL_FLAG_FIRE_CONT | SKILL_FLAG_POISON | SKILL_FLAG_BLEEDING)) +#else + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW | SKILL_FLAG_STUN | SKILL_FLAG_FIRE_CONT | SKILL_FLAG_POISON)) +#endif + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + int iDur = (int) m_pkSk->kDurationPoly2.Eval(); + + iDur += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_STUN)) + { + SkillAttackAffect(pkChrVictim, iPct, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iDur, m_pkSk->szName); + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW)) + { + SkillAttackAffect(pkChrVictim, iPct, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, iDur, m_pkSk->szName); + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE_CONT)) + { + m_pkSk->SetDurationVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetDurationVar("iq", m_pkChr->GetPoint(POINT_IQ)); + + iDur = (int)m_pkSk->kDurationPoly2.Eval(); + int bonus = m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (bonus != 0) + { + iDur += bonus / 2; + } + + if (number(1, 100) <= iDur) + { + pkChrVictim->AttackedByFire(m_pkChr, iPct, 5); + } + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_POISON)) + { + if (number(1, 100) <= iPct) + pkChrVictim->AttackedByPoison(m_pkChr); + } +#ifdef ENABLE_WOLFMAN_CHARACTER + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_BLEEDING)) + { + if (number(1, 100) <= iPct) + pkChrVictim->AttackedByBleeding(m_pkChr); + } +#endif + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && + !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) + { + float fCrushSlidingLength = 200; + + if (m_pkChr->IsNPC()) + fCrushSlidingLength = 400; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH_LONG)) + fCrushSlidingLength *= 2; + + float fx, fy; + float degree = GetDegreeFromPositionXY(m_pkChr->GetX(), m_pkChr->GetY(), pkChrVictim->GetX(), pkChrVictim->GetY()); + + if (m_pkSk->dwVnum == SKILL_HORSE_WILDATTACK) + { + degree -= m_pkChr->GetRotation(); + degree = fmod(degree, 360.0f) - 180.0f; + + if (degree > 0) + degree = m_pkChr->GetRotation() + 90.0f; + else + degree = m_pkChr->GetRotation() - 90.0f; + } + + GetDeltaByDegree(degree, fCrushSlidingLength, &fx, &fy); + sys_log(0, "CRUSH! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); + long tx = (long)(pkChrVictim->GetX()+fx); + long ty = (long)(pkChrVictim->GetY()+fy); + + pkChrVictim->Sync(tx, ty); + pkChrVictim->Goto(tx, ty); + pkChrVictim->CalculateMoveDuration(); + + if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) + { + SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); + } + else + { + pkChrVictim->SyncPacket(); + } + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_HP_ABSORB)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + m_pkChr->PointChange(POINT_HP, iDam * iPct / 100); + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SP_ABSORB)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + m_pkChr->PointChange(POINT_SP, iDam * iPct / 100); + } + + if (m_pkSk->dwVnum == SKILL_CHAIN && m_pkChr->GetChainLightningIndex() < m_pkChr->GetChainLightningMaxCount()) + { + chain_lightning_event_info* info = AllocEventInfo(); + + info->dwVictim = pkChrVictim->GetVID(); + info->dwChr = m_pkChr->GetVID(); + + event_create(ChainLightningEvent, info, passes_per_sec / 5); + } + if(test_server) + sys_log(0, "FuncSplashDamage End :%s ", m_pkChr->GetName()); + } + + int m_x; + int m_y; + CSkillProto * m_pkSk; + LPCHARACTER m_pkChr; + int m_iAmount; + int m_iAG; + int m_iCount; + int m_iMaxHit; + LPITEM m_pkWeapon; + bool m_bDisableCooltime; + TSkillUseInfo* m_pInfo; + BYTE m_bUseSkillPower; +}; + +struct FuncSplashAffect +{ + FuncSplashAffect(LPCHARACTER ch, int x, int y, int iDist, DWORD dwVnum, BYTE bPointOn, int iAmount, DWORD dwAffectFlag, int iDuration, int iSPCost, bool bOverride, int iMaxHit) + { + m_x = x; + m_y = y; + m_iDist = iDist; + m_dwVnum = dwVnum; + m_bPointOn = bPointOn; + m_iAmount = iAmount; + m_dwAffectFlag = dwAffectFlag; + m_iDuration = iDuration; + m_iSPCost = iSPCost; + m_bOverride = bOverride; + m_pkChrAttacker = ch; + m_iMaxHit = iMaxHit; + m_iCount = 0; + } + + void operator () (LPENTITY ent) + { + if (m_iMaxHit && m_iMaxHit <= m_iCount) + return; + + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (test_server) + sys_log(0, "FuncSplashAffect step 1 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration); + if (DISTANCE_APPROX(m_x - pkChr->GetX(), m_y - pkChr->GetY()) < m_iDist) + { + if (test_server) + sys_log(0, "FuncSplashAffect step 2 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration); + if (m_dwVnum == SKILL_TUSOK) + if (pkChr->CanBeginFight()) + pkChr->BeginFight(m_pkChrAttacker); + + if (pkChr->IsPC() && m_dwVnum == SKILL_TUSOK) + pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration/3, m_iSPCost, m_bOverride); + else + pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration, m_iSPCost, m_bOverride); + + m_iCount ++; + } + } + } + + LPCHARACTER m_pkChrAttacker; + int m_x; + int m_y; + int m_iDist; + DWORD m_dwVnum; + BYTE m_bPointOn; + int m_iAmount; + DWORD m_dwAffectFlag; + int m_iDuration; + int m_iSPCost; + bool m_bOverride; + int m_iMaxHit; + int m_iCount; +}; + +EVENTINFO(skill_gwihwan_info) +{ + DWORD pid; + BYTE bsklv; + + skill_gwihwan_info() + : pid( 0 ) + , bsklv( 0 ) + { + } +}; + +EVENTFUNC(skill_gwihwan_event) +{ + skill_gwihwan_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "skill_gwihwan_event> Null pointer" ); + return 0; + } + + DWORD pid = info->pid; + BYTE sklv= info->bsklv; + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pid); + + if (!ch) + return 0; + + int percent = 20 * sklv - 1; + + if (number(1, 100) <= percent) + { + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + { + sys_log(1, "Recall: %s %d %d -> %d %d", ch->GetName(), ch->GetX(), ch->GetY(), pos.x, pos.y); + ch->WarpSet(pos.x, pos.y); + } + else + { + sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", ch->GetName(), ch->GetX(), ch->GetY()); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȯ Ͽϴ.")); + } + return 0; +} + +int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTarget, BYTE bSkillLevel) +{ + if (GetMountVnum()) + return BATTLE_NONE; + + if (IsPolymorphed()) + return BATTLE_NONE; + + if (g_bSkillDisable) + return BATTLE_NONE; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return BATTLE_NONE; + + if (test_server) + { + sys_log(0, "ComputeSkillAtPosition %s vnum %d x %d y %d level %d", + GetName(), dwVnum, posTarget.x, posTarget.y, bSkillLevel); + } + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + return BATTLE_NONE; + + if (0 == bSkillLevel) + { + if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0) + { + return BATTLE_NONE; + } + } + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, this, true, false)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMagicDamage(this, this)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, this, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", 0); + } + } + + if (pkSk->bPointOn == POINT_MOV_SPEED) + { + pkSk->SetPointVar("maxv", this->GetLimitPoint(POINT_MOV_SPEED)); + } + + pkSk->SetPointVar("lv", GetLevel()); + pkSk->SetPointVar("iq", GetPoint(POINT_IQ)); + pkSk->SetPointVar("str", GetPoint(POINT_ST)); + pkSk->SetPointVar("dex", GetPoint(POINT_DX)); + pkSk->SetPointVar("con", GetPoint(POINT_HT)); + pkSk->SetPointVar("maxhp", this->GetMaxHP()); + pkSk->SetPointVar("maxsp", this->GetMaxSP()); + pkSk->SetPointVar("chain", 0); + pkSk->SetPointVar("ar", CalcAttackRating(this, this)); + pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE)); + pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS)); + pkSk->SetPointVar("horse_level", GetHorseLevel()); + + if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL) + OnMove(true); + + LPITEM pkWeapon = GetWear(WEAR_WEAPON); + + SetPolyVarForAttack(this, pkSk, pkWeapon); + + pkSk->SetDurationVar("k", k/*bSkillLevel*/); + + int iAmount = (int) pkSk->kPointPoly.Eval(); + int iAmount2 = (int) pkSk->kPointPoly2.Eval(); + + // ADD_GRANDMASTER_SKILL + int iAmount3 = (int) pkSk->kPointPoly3.Eval(); + + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + /* + if (iAmount >= 0) + iAmount += (int) m_pkSk->kMasterBonusPoly.Eval(); + else + iAmount -= (int) m_pkSk->kMasterBonusPoly.Eval(); + */ + iAmount = (int) pkSk->kMasterBonusPoly.Eval(); + } + + if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE) + { + ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT)) + { + if (number(1, 100) <= iAmount2) + { + RemoveBadAffect(); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + bool bAdded = false; + + if (pkSk->bPointOn == POINT_HP && iAmount < 0) + { + int iAG = 0; + + FuncSplashDamage f(posTarget.x, posTarget.y, pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel)); + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + { + if (GetSectree()) + GetSectree()->ForEachAround(f); + } + else + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill call FuncSplashDamage %s", GetName()); + f(this); + } + } + else + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill no damage %d %s", iAmount, GetName()); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (IsPC()) + if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) + if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG) + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill cannot hit %s", GetName()); + return BATTLE_NONE; + } + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + } + + if (pkSk->bPointOn2 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + sys_log(1, "try second %u %d %d", pkSk->dwVnum, pkSk->bPointOn2, iDur); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + } + else + { + PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_DAMAGE; + } + else + { + bool bAdded = false; + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + pkSk->dwAffectFlag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + !bAdded); + + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn, iAmount); + } + + if (pkSk->bPointOn2 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded); + } + else + { + PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_NONE; + } +} + +#ifdef ENABLE_SKILL_FLAG_PARTY +struct FComputeSkillParty +{ + FComputeSkillParty(DWORD dwVnum, LPCHARACTER pkAttacker, BYTE bSkillLevel = 0) + : m_dwVnum(dwVnum), m_pkAttacker(pkAttacker), m_bSkillLevel(bSkillLevel) + { + } + + void operator () (LPCHARACTER ch) + { + m_pkAttacker->ComputeSkill(m_dwVnum, ch, m_bSkillLevel); + } + + DWORD m_dwVnum; + LPCHARACTER m_pkAttacker; + BYTE m_bSkillLevel; +}; + +int CHARACTER::ComputeSkillParty(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) +{ + FComputeSkillParty f(dwVnum, pkVictim, bSkillLevel); + if (GetParty() && GetParty()->GetNearMemberCount()) + GetParty()->ForEachNearMember(f); + else + f(this); + + return BATTLE_NONE; +} +#endif + +int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) +{ + const bool bCanUseHorseSkill = CanUseHorseSkill(); + + if (false == bCanUseHorseSkill && true == IsRiding()) + return BATTLE_NONE; + + if (IsPolymorphed()) + return BATTLE_NONE; + + if (g_bSkillDisable) + return BATTLE_NONE; + + CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return BATTLE_NONE; + + if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + pkVictim = this; + + if (!pkVictim) + { + if (test_server) + sys_log(0, "ComputeSkill: %s Victim == null, skill %d", GetName(), dwVnum); + + return BATTLE_NONE; + } + + if (pkSk->dwTargetRange && DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()) >= pkSk->dwTargetRange + 50) + { + if (test_server) + sys_log(0, "ComputeSkill: Victim too far, skill %d : %s to %s (distance %u limit %u)", + dwVnum, + GetName(), + pkVictim->GetName(), + (long)DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()), + pkSk->dwTargetRange); + + return BATTLE_NONE; + } + + if (0 == bSkillLevel) + { + if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0) + { + if (test_server) + sys_log(0, "ComputeSkill : name:%s vnum:%d skillLevelBySkill : %d ", GetName(), pkSk->dwVnum, bSkillLevel); + return BATTLE_NONE; + } + } + + if (pkVictim->IsAffectFlag(AFF_PABEOP) && pkVictim->IsGoodAffect(dwVnum)) + { + return BATTLE_NONE; + } + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + if (pkSk->dwType == SKILL_TYPE_HORSE) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false)); + } + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMagicDamage(this, pkVictim)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", 0); + } + } + + if (pkSk->bPointOn == POINT_MOV_SPEED) + { + pkSk->SetPointVar("maxv", pkVictim->GetLimitPoint(POINT_MOV_SPEED)); + } + + pkSk->SetPointVar("lv", GetLevel()); + pkSk->SetPointVar("iq", GetPoint(POINT_IQ)); + pkSk->SetPointVar("str", GetPoint(POINT_ST)); + pkSk->SetPointVar("dex", GetPoint(POINT_DX)); + pkSk->SetPointVar("con", GetPoint(POINT_HT)); + pkSk->SetPointVar("maxhp", pkVictim->GetMaxHP()); + pkSk->SetPointVar("maxsp", pkVictim->GetMaxSP()); + pkSk->SetPointVar("chain", 0); + pkSk->SetPointVar("ar", CalcAttackRating(this, pkVictim)); + pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE)); + pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS)); + pkSk->SetPointVar("horse_level", GetHorseLevel()); + + if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL) + OnMove(true); + + LPITEM pkWeapon = GetWear(WEAR_WEAPON); + + SetPolyVarForAttack(this, pkSk, pkWeapon); + + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/); + + int iAmount = (int) pkSk->kPointPoly.Eval(); + int iAmount2 = (int) pkSk->kPointPoly2.Eval(); + int iAmount3 = (int) pkSk->kPointPoly3.Eval(); + + if (test_server && IsPC()) + sys_log(0, "iAmount: %d %d %d , atk:%f skLevel:%f k:%f GetSkillPower(%d) MaxLevel:%d Per:%f", + iAmount, iAmount2, iAmount3, + pkSk->kPointPoly.GetVar("atk"), + pkSk->kPointPoly.GetVar("k"), + k, + GetSkillPower(pkSk->dwVnum, bSkillLevel), + pkSk->bMaxLevel, + pkSk->bMaxLevel/100 + ); + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + iAmount = (int) pkSk->kMasterBonusPoly.Eval(); + } + + if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE) + { + ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + // END_OF_ADD_GRANDMASTER_SKILL + + //sys_log(0, "XXX SKILL Calc %d Amount %d", dwVnum, iAmount); + + // REMOVE_BAD_AFFECT_BUG_FIX + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT)) + { + if (number(1, 100) <= iAmount2) + { + pkVictim->RemoveBadAffect(); + } + } + // END_OF_REMOVE_BAD_AFFECT_BUG_FIX + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE) && + !(pkSk->dwVnum == SKILL_MUYEONG && pkVictim == this) && !(pkSk->IsChargeSkill() && pkVictim == this)) + { + bool bAdded = false; + + if (pkSk->bPointOn == POINT_HP && iAmount < 0) + { + int iAG = 0; + + FuncSplashDamage f(pkVictim->GetX(), pkVictim->GetY(), pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel)); + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + { + if (pkVictim->GetSectree()) + pkVictim->GetSectree()->ForEachAround(f); + } + else + { + f(pkVictim); + } + } + else + { + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (IsPC()) + if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) + if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG) + { + return BATTLE_NONE; + } + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + } + + if (pkSk->bPointOn2 != POINT_NONE && !pkSk->IsChargeSkill()) + { + pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_DAMAGE; + } + else + { + if (dwVnum == SKILL_MUYEONG) + { + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + int iDur = (long) pkSk->kDurationPoly.Eval(); + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (pkVictim == this) + AddAffect(dwVnum, + POINT_NONE, 0, + AFF_MUYEONG, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + true); + + return BATTLE_NONE; + } + + bool bAdded = false; + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + if (pkSk->bPointOn2 != POINT_NONE) + { + pkVictim->RemoveAffect(pkSk->dwVnum); + + int iDur2 = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur2 > 0) + { + if (test_server) + sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d", + GetName(), + pkSk->szName, + iDur2, + pkSk->bPointOn2, + iAmount2); + + iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS); + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false); + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + + DWORD affact_flag = pkSk->dwAffectFlag; + + // ADD_GRANDMASTER_SKILL + if ((pkSk->dwVnum == SKILL_CHUNKEON && GetUsedSkillMasterType(pkSk->dwVnum) < SKILL_GRAND_MASTER)) + affact_flag = AFF_CHEONGEUN_WITH_FALL; + // END_OF_ADD_GRANDMASTER_SKILL + + pkVictim->AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + affact_flag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + false); + } + else + { + if (test_server) + sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d", + GetName(), + pkSk->szName, + iDur, + pkSk->bPointOn, + iAmount); + + pkVictim->AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + pkSk->dwAffectFlag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + // ADD_GRANDMASTER_SKILL + !bAdded); + // END_OF_ADD_GRANDMASTER_SKILL + } + + bAdded = true; + } + else + { + if (!pkSk->IsChargeSkill()) + pkVictim->PointChange(pkSk->bPointOn, iAmount); + + if (pkSk->bPointOn2 != POINT_NONE) + { + pkVictim->RemoveAffect(pkSk->dwVnum); + + int iDur2 = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur2 > 0) + { + iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (pkSk->IsChargeSkill()) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, AFF_TANHWAN_DASH, iDur2, 0, false); + else + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false); + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + + } + } + + // ADD_GRANDMASTER_SKILL + if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + sys_log(0, "try third %u %d %d %d 1894", pkSk->dwVnum, pkSk->bPointOn3, iDur, iAmount3); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_NONE; + } +} + +bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster) +{ + if (false == CanUseSkill(dwVnum)) + return false; + + // NO_GRANDMASTER + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("no_grand_master")) + { + bUseGrandMaster = false; + } + } + // END_OF_NO_GRANDMASTER + + if (g_bSkillDisable) + return false; + + if (IsObserverMode()) + return false; + + if (!CanMove()) + return false; + + if (IsPolymorphed()) + return false; + + const bool bCanUseHorseSkill = CanUseHorseSkill(); + + if (dwVnum == SKILL_HORSE_SUMMON) + { + if (GetSkillLevel(dwVnum) == 0) + return false; + + if (GetHorseLevel() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. ° ãư.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯ ϼ.")); + + return true; + } + + if (false == bCanUseHorseSkill && true == IsRiding()) + return false; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + sys_log(0, "%s: USE_SKILL: %d pkVictim %p", GetName(), dwVnum, get_pointer(pkVictim)); + + if (!pkSk) + return false; + + if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (GetSkillLevel(dwVnum) == 0) + return false; + + // NO_GRANDMASTER + if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER) + bUseGrandMaster = false; + // END_OF_NO_GRANDMASTER + + // MINING + if (GetWear(WEAR_WEAPON) && (GetWear(WEAR_WEAPON)->GetType() == ITEM_ROD || GetWear(WEAR_WEAPON)->GetType() == ITEM_PICK)) + return false; + // END_OF_MINING + + m_SkillUseInfo[dwVnum].TargetVIDMap.clear(); + + if (pkSk->IsChargeSkill()) + { + if ((IsAffectFlag(AFF_TANHWAN_DASH)) || (pkVictim && (pkVictim != this))) + { + if (!pkVictim) + return false; + + if (!IsAffectFlag(AFF_TANHWAN_DASH)) + { + if (!UseSkill(dwVnum, this)) + return false; + } + + m_SkillUseInfo[dwVnum].SetMainTargetVID(pkVictim->GetVID()); + + ComputeSkill(dwVnum, pkVictim); + RemoveAffect(dwVnum); + return true; + } + } + + if (dwVnum == SKILL_COMBO) + { + if (m_bComboIndex) + m_bComboIndex = 0; + else + m_bComboIndex = GetSkillLevel(SKILL_COMBO); + + ChatPacket(CHAT_TYPE_COMMAND, "combo %d", m_bComboIndex); + return true; + } + + if ((0 != pkSk->dwAffectFlag || pkSk->dwVnum == SKILL_MUYEONG) && (pkSk->dwFlag & SKILL_FLAG_TOGGLE) && RemoveAffect(pkSk->dwVnum)) + { + return true; + } + + if (IsAffectFlag(AFF_REVIVE_INVISIBLE)) + RemoveAffect(AFFECT_REVIVE_INVISIBLE); + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + pkSk->kCooldownPoly.SetVar("k", k); + int iCooltime = (int) pkSk->kCooldownPoly.Eval(); + int lMaxHit = pkSk->lMaxHit ? pkSk->lMaxHit : -1; + + pkSk->SetSPCostVar("k", k); + + DWORD dwCur = get_dword_time(); + + if (dwVnum == SKILL_TERROR && m_SkillUseInfo[dwVnum].bUsed && m_SkillUseInfo[dwVnum].dwNextSkillUsableTime > dwCur ) + { + sys_log(0, " SKILL_TERROR's Cooltime is not delta over %u", m_SkillUseInfo[dwVnum].dwNextSkillUsableTime - dwCur ); + return false; + } + + int iNeededSP = 0; + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_HP_AS_COST)) + { + pkSk->SetSPCostVar("maxhp", GetMaxHP()); + pkSk->SetSPCostVar("v", GetHP()); + iNeededSP = (int) pkSk->kSPCostPoly.Eval(); + + // ADD_GRANDMASTER_SKILL + if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster) + { + iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval(); + } + // END_OF_ADD_GRANDMASTER_SKILL + + if (GetHP() < iNeededSP) + return false; + + PointChange(POINT_HP, -iNeededSP); + } + else + { + // SKILL_FOMULA_REFACTORING + pkSk->SetSPCostVar("maxhp", GetMaxHP()); + pkSk->SetSPCostVar("maxv", GetMaxSP()); + pkSk->SetSPCostVar("v", GetSP()); + + iNeededSP = (int) pkSk->kSPCostPoly.Eval(); + + if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster) + { + iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval(); + } + // END_OF_SKILL_FOMULA_REFACTORING + + if (GetSP() < iNeededSP) + return false; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s SPҸ: %d"), pkSk->szName, iNeededSP); + + PointChange(POINT_SP, -iNeededSP); + } + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + pkVictim = this; +#ifdef ENABLE_SKILL_FLAG_PARTY + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_PARTY)) + pkVictim = this; +#endif + + if ((pkSk->dwVnum == SKILL_MUYEONG) || (pkSk->IsChargeSkill() && !IsAffectFlag(AFF_TANHWAN_DASH) && !pkVictim)) + { + pkVictim = this; + } + + int iSplashCount = 1; + + if (false == m_bDisableCooltime) + { + if (false == + m_SkillUseInfo[dwVnum].UseSkill( + bUseGrandMaster, + (NULL != pkVictim && SKILL_HORSE_WILDATTACK != dwVnum) ? pkVictim->GetVID() : VID(), + ComputeCooltime(iCooltime * 1000), + iSplashCount, + lMaxHit)) + { + if (test_server) + ChatPacket(CHAT_TYPE_NOTICE, "cooltime not finished %s %d", pkSk->szName, iCooltime); + + return false; + } + } + + if (dwVnum == SKILL_CHAIN) + { + ResetChainLightningIndex(); + AddChainLightningExcept(pkVictim); + } + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + ComputeSkill(dwVnum, this); +#ifdef ENABLE_SKILL_FLAG_PARTY + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_PARTY)) + ComputeSkillParty(dwVnum, this); +#endif + else if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK)) + ComputeSkill(dwVnum, pkVictim); + else if (dwVnum == SKILL_BYEURAK) + ComputeSkill(dwVnum, pkVictim); + else if (dwVnum == SKILL_MUYEONG || pkSk->IsChargeSkill()) + ComputeSkill(dwVnum, pkVictim); + + m_dwLastSkillTime = get_dword_time(); + + return true; +} + +int CHARACTER::GetUsedSkillMasterType(DWORD dwVnum) +{ + const TSkillUseInfo& rInfo = m_SkillUseInfo[dwVnum]; + + if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER) + return GetSkillMasterType(dwVnum); + + if (rInfo.isGrandMaster) + return GetSkillMasterType(dwVnum); + + return MIN(GetSkillMasterType(dwVnum), SKILL_MASTER); +} + +int CHARACTER::GetSkillMasterType(DWORD dwVnum) const +{ + if (!IsPC()) + return 0; + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + return m_pSkillLevels ? m_pSkillLevels[dwVnum].bMasterType:SKILL_NORMAL; +} + +int CHARACTER::GetSkillPower(DWORD dwVnum, BYTE bLevel) const +{ + if (dwVnum >= SKILL_LANGUAGE1 && dwVnum <= SKILL_LANGUAGE3 && IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE)) + { + return 100; + } + + if (dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END) + { + if (GetGuild()) + return 100 * GetGuild()->GetSkillLevel(dwVnum) / 7 / 7; + else + return 0; + } + + if (bLevel) + { + //SKILL_POWER_BY_LEVEL + return GetSkillPowerByLevel(bLevel, true); + //END_SKILL_POWER_BY_LEVEL; + } + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + //SKILL_POWER_BY_LEVEL + return GetSkillPowerByLevel(GetSkillLevel(dwVnum)); + //SKILL_POWER_BY_LEVEL +} + +int CHARACTER::GetSkillLevel(DWORD dwVnum) const +{ + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + sys_log(0, "%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + return MIN(SKILL_MAX_LEVEL, m_pSkillLevels ? m_pSkillLevels[dwVnum].bLevel : 0); +} + +EVENTFUNC(skill_muyoung_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "skill_muyoung_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->IsAffectFlag(AFF_MUYEONG)) + { + ch->StopMuyeongEvent(); + return 0; + } + + // 1. Find Victim + FFindNearVictim f(ch, ch); + if (ch->GetSectree()) + { + ch->GetSectree()->ForEachAround(f); + // 2. Shoot! + if (f.GetVictim()) + { + ch->CreateFly(FLY_SKILL_MUYEONG, f.GetVictim()); + ch->ComputeSkill(SKILL_MUYEONG, f.GetVictim()); + } + } + + return PASSES_PER_SEC(3); +} + +void CHARACTER::StartMuyeongEvent() +{ + if (m_pkMuyeongEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + m_pkMuyeongEvent = event_create(skill_muyoung_event, info, PASSES_PER_SEC(1)); +} + +void CHARACTER::StopMuyeongEvent() +{ + event_cancel(&m_pkMuyeongEvent); +} + +void CHARACTER::SkillLearnWaitMoreTimeMessage(DWORD ms) +{ + //const char* str = ""; + + if (ms < 3 * 60) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" ̱߰. . ̴ ⸦ Ű.")); + else if (ms < 5 * 60) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("׷, õõ. õõ, ׷ !")); + else if (ms < 10 * 60) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("׷, ̾. ü Ⱑ 游.")); + else if (ms < 30 * 60) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" о! ޿ ִ ſ ⸦ ⸸ ϸ,")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("װ ž!")); + } + else if (ms < 1 * 3600) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" å ̾! ̰ ־!")); + else if (ms < 2 * 3600) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" Ҿ! ݸ !")); + else if (ms < 3 * 3600) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Ҿ! ݸ ̴!")); + else if (ms < 6 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("å嵵 ʾұ.")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" ȿ .")); + } + else if (ms < 12 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(", ⼼ !")); + } + else if (ms < 18 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ƴ  о Ӹ .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ϱ Ⱦ.")); + } + else //if (ms < 2 * 86400) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ŭ бⰡ ʱ. ص ư 뵵 .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("̷ ΰ ȵȴٱ.")); + } + /* + str = "30%"; + else if (ms < 3 * 86400) + str = "10%"; + else if (ms < 4 * 86400) + str = "5%"; + else + str = "0%";*/ + + //ChatPacket(CHAT_TYPE_TALKING, "%s", str); +} + +void CHARACTER::DisableCooltime() +{ + m_bDisableCooltime = true; +} + +bool CHARACTER::HasMobSkill() const +{ + return CountMobSkill() > 0; +} + +size_t CHARACTER::CountMobSkill() const +{ + if (!m_pkMobData) + return 0; + + size_t c = 0; + + for (size_t i = 0; i < MOB_SKILL_MAX_NUM; ++i) + if (m_pkMobData->m_table.Skills[i].dwVnum) + ++c; + + return c; +} + +const TMobSkillInfo* CHARACTER::GetMobSkill(unsigned int idx) const +{ + if (idx >= MOB_SKILL_MAX_NUM) + return NULL; + + if (!m_pkMobData) + return NULL; + + if (0 == m_pkMobData->m_table.Skills[idx].dwVnum) + return NULL; + + return &m_pkMobData->m_mobSkillInfo[idx]; +} + +bool CHARACTER::CanUseMobSkill(unsigned int idx) const +{ + const TMobSkillInfo* pInfo = GetMobSkill(idx); + + if (!pInfo) + return false; + + if (m_adwMobSkillCooltime[idx] > get_dword_time()) + return false; + + if (number(0, 1)) + return false; + + return true; +} + +EVENTINFO(mob_skill_event_info) +{ + DynamicCharacterPtr ch; + PIXEL_POSITION pos; + DWORD vnum; + int index; + BYTE level; + + mob_skill_event_info() + : ch() + , pos() + , vnum(0) + , index(0) + , level(0) + { + } +}; + +EVENTFUNC(mob_skill_hit_event) +{ + mob_skill_event_info * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "mob_skill_event_info> Null pointer" ); + return 0; + } + + // + LPCHARACTER ch = info->ch; + if (ch == NULL) { + return 0; + } + + ch->ComputeSkillAtPosition(info->vnum, info->pos, info->level); + ch->m_mapMobSkillEvent.erase(info->index); + + return 0; +} + +bool CHARACTER::UseMobSkill(unsigned int idx) +{ + if (IsPC()) + return false; + + const TMobSkillInfo* pInfo = GetMobSkill(idx); + + if (!pInfo) + return false; + + DWORD dwVnum = pInfo->dwSkillVnum; + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return false; + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, pInfo->bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->kCooldownPoly.SetVar("k", k); + int iCooltime = (int) (pkSk->kCooldownPoly.Eval() * 1000); + + m_adwMobSkillCooltime[idx] = get_dword_time() + iCooltime; + + sys_log(0, "USE_MOB_SKILL: %s idx %d vnum %u cooltime %d", GetName(), idx, dwVnum, iCooltime); + + if (m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.empty()) + { + sys_err("No skill hit data for mob %s index %d", GetName(), idx); + return false; + } + + for (size_t i = 0; i < m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.size(); i++) + { + PIXEL_POSITION pos = GetXYZ(); + const TMobSplashAttackInfo& rInfo = m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack[i]; + + if (rInfo.dwHitDistance) + { + float fx, fy; + GetDeltaByDegree(GetRotation(), rInfo.dwHitDistance, &fx, &fy); + pos.x += (long) fx; + pos.y += (long) fy; + } + + if (rInfo.dwTiming) + { + if (test_server) + sys_log(0, " timing %ums", rInfo.dwTiming); + + mob_skill_event_info* info = AllocEventInfo(); + + info->ch = this; + info->pos = pos; + info->level = pInfo->bSkillLevel; + info->vnum = dwVnum; + info->index = i; + + // Cancel existing event first + itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.find(i); + if (it != m_mapMobSkillEvent.end()) { + LPEVENT existing = it->second; + event_cancel(&existing); + m_mapMobSkillEvent.erase(it); + } + + m_mapMobSkillEvent.emplace(i, event_create(mob_skill_hit_event, info, PASSES_PER_SEC(rInfo.dwTiming) / 1000)); + } + else + { + ComputeSkillAtPosition(dwVnum, pos, pInfo->bSkillLevel); + } + } + + return true; +} + +void CHARACTER::ResetMobSkillCooltime() +{ + memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime)); +} + +bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const +{ + DWORD selfJobGroup = (GetJob()+1) * 10 + GetSkillGroup(); +#ifdef ENABLE_WOLFMAN_CHARACTER + const DWORD SKILL_NUM = 176; +#else + const DWORD SKILL_NUM = 158; +#endif + static DWORD s_anSkill2JobGroup[SKILL_NUM] = { + 0, // common_skill 0 + 11, // job_skill 1 + 11, // job_skill 2 + 11, // job_skill 3 + 11, // job_skill 4 + 11, // job_skill 5 + 11, // job_skill 6 + 0, // common_skill 7 + 0, // common_skill 8 + 0, // common_skill 9 + 0, // common_skill 10 + 0, // common_skill 11 + 0, // common_skill 12 + 0, // common_skill 13 + 0, // common_skill 14 + 0, // common_skill 15 + 12, // job_skill 16 + 12, // job_skill 17 + 12, // job_skill 18 + 12, // job_skill 19 + 12, // job_skill 20 + 12, // job_skill 21 + 0, // common_skill 22 + 0, // common_skill 23 + 0, // common_skill 24 + 0, // common_skill 25 + 0, // common_skill 26 + 0, // common_skill 27 + 0, // common_skill 28 + 0, // common_skill 29 + 0, // common_skill 30 + 21, // job_skill 31 + 21, // job_skill 32 + 21, // job_skill 33 + 21, // job_skill 34 + 21, // job_skill 35 + 21, // job_skill 36 + 0, // common_skill 37 + 0, // common_skill 38 + 0, // common_skill 39 + 0, // common_skill 40 + 0, // common_skill 41 + 0, // common_skill 42 + 0, // common_skill 43 + 0, // common_skill 44 + 0, // common_skill 45 + 22, // job_skill 46 + 22, // job_skill 47 + 22, // job_skill 48 + 22, // job_skill 49 + 22, // job_skill 50 + 22, // job_skill 51 + 0, // common_skill 52 + 0, // common_skill 53 + 0, // common_skill 54 + 0, // common_skill 55 + 0, // common_skill 56 + 0, // common_skill 57 + 0, // common_skill 58 + 0, // common_skill 59 + 0, // common_skill 60 + 31, // job_skill 61 + 31, // job_skill 62 + 31, // job_skill 63 + 31, // job_skill 64 + 31, // job_skill 65 + 31, // job_skill 66 + 0, // common_skill 67 + 0, // common_skill 68 + 0, // common_skill 69 + 0, // common_skill 70 + 0, // common_skill 71 + 0, // common_skill 72 + 0, // common_skill 73 + 0, // common_skill 74 + 0, // common_skill 75 + 32, // job_skill 76 + 32, // job_skill 77 + 32, // job_skill 78 + 32, // job_skill 79 + 32, // job_skill 80 + 32, // job_skill 81 + 0, // common_skill 82 + 0, // common_skill 83 + 0, // common_skill 84 + 0, // common_skill 85 + 0, // common_skill 86 + 0, // common_skill 87 + 0, // common_skill 88 + 0, // common_skill 89 + 0, // common_skill 90 + 41, // job_skill 91 + 41, // job_skill 92 + 41, // job_skill 93 + 41, // job_skill 94 + 41, // job_skill 95 + 41, // job_skill 96 + 0, // common_skill 97 + 0, // common_skill 98 + 0, // common_skill 99 + 0, // common_skill 100 + 0, // common_skill 101 + 0, // common_skill 102 + 0, // common_skill 103 + 0, // common_skill 104 + 0, // common_skill 105 + 42, // job_skill 106 + 42, // job_skill 107 + 42, // job_skill 108 + 42, // job_skill 109 + 42, // job_skill 110 + 42, // job_skill 111 + 0, // common_skill 112 + 0, // common_skill 113 + 0, // common_skill 114 + 0, // common_skill 115 + 0, // common_skill 116 + 0, // common_skill 117 + 0, // common_skill 118 + 0, // common_skill 119 + 0, // common_skill 120 + 0, // common_skill 121 + 0, // common_skill 122 + 0, // common_skill 123 + 0, // common_skill 124 + 0, // common_skill 125 + 0, // common_skill 126 + 0, // common_skill 127 + 0, // common_skill 128 + 0, // common_skill 129 + 0, // common_skill 130 + 0, // common_skill 131 + 0, // common_skill 132 + 0, // common_skill 133 + 0, // common_skill 134 + 0, // common_skill 135 + 0, // common_skill 136 + 0, // job_skill 137 + 0, // job_skill 138 + 0, // job_skill 139 + 0, // job_skill 140 + 0, // common_skill 141 + 0, // common_skill 142 + 0, // common_skill 143 + 0, // common_skill 144 + 0, // common_skill 145 + 0, // common_skill 146 + 0, // common_skill 147 + 0, // common_skill 148 + 0, // common_skill 149 + 0, // common_skill 150 + 0, // common_skill 151 + 0, // job_skill 152 + 0, // job_skill 153 + 0, // job_skill 154 + 0, // job_skill 155 + 0, // job_skill 156 + 0, // job_skill 157 +#ifdef ENABLE_WOLFMAN_CHARACTER + 0, // empty(reserved) 158 + 0, // empty(reserved) 159 + 0, // empty(reserved) 160 + 0, // empty(reserved) 161 + 0, // empty(reserved) 162 + 0, // empty(reserved) 163 + 0, // empty(reserved) 164 + 0, // empty(reserved) 165 + 0, // empty(reserved) 166 + 0, // empty(reserved) 167 + 0, // empty(reserved) 168 + 0, // empty(reserved) 169 + 51, // job_skill(WOLFMAN SKILL) 170 + 51, // job_skill(WOLFMAN SKILL) 171 + 51, // job_skill(WOLFMAN SKILL) 172 + 51, // job_skill(WOLFMAN SKILL) 173 + 51, // job_skill(WOLFMAN SKILL) 174 + 51, // job_skill(WOLFMAN SKILL) 175 +#endif + }; // s_anSkill2JobGroup + + const DWORD MOTION_MAX_NUM = 124; +#ifdef ENABLE_WOLFMAN_CHARACTER + const DWORD SKILL_LIST_MAX_COUNT = 6; +#else + const DWORD SKILL_LIST_MAX_COUNT = 5; +#endif + static DWORD s_anMotion2SkillVnumList[MOTION_MAX_NUM][SKILL_LIST_MAX_COUNT] = + { + { 0, 0, 0, 0, 0 }, // 0 + +#ifdef ENABLE_WOLFMAN_CHARACTER + { 5, 1, 31, 61, 91, 170 }, // 1 + { 5, 2, 32, 62, 92, 171 }, // 2 + { 5, 3, 33, 63, 93, 172 }, // 3 + { 5, 4, 34, 64, 94, 173 }, // 4 + { 5, 5, 35, 65, 95, 174 }, // 5 + { 5, 6, 36, 66, 96, 175 }, // 6 +#else + { 4, 1, 31, 61, 91 }, // 1 + { 4, 2, 32, 62, 92 }, // 2 + { 4, 3, 33, 63, 93 }, // 3 + { 4, 4, 34, 64, 94 }, // 4 + { 4, 5, 35, 65, 95 }, // 5 + { 4, 6, 36, 66, 96 }, // 6 +#endif + { 0, 0, 0, 0, 0 }, // 7 + { 0, 0, 0, 0, 0 }, // 8 + + { 0, 0, 0, 0, 0 }, // 9 + { 0, 0, 0, 0, 0 }, // 10 + { 0, 0, 0, 0, 0 }, // 11 + { 0, 0, 0, 0, 0 }, // 12 + { 0, 0, 0, 0, 0 }, // 13 + { 0, 0, 0, 0, 0 }, // 14 + { 0, 0, 0, 0, 0 }, // 15 + + { 4, 16, 46, 76, 106 }, // 16 + { 4, 17, 47, 77, 107 }, // 17 + { 4, 18, 48, 78, 108 }, // 18 + { 4, 19, 49, 79, 109 }, // 19 + { 4, 20, 50, 80, 110 }, // 20 + { 4, 21, 51, 81, 111 }, // 21 + { 0, 0, 0, 0, 0 }, // 22 + { 0, 0, 0, 0, 0 }, // 23 + + { 0, 0, 0, 0, 0 }, // 24 + { 0, 0, 0, 0, 0 }, // 25 + +#ifdef ENABLE_WOLFMAN_CHARACTER + { 5, 1, 31, 61, 91, 170 }, // 26 + { 5, 2, 32, 62, 92, 171 }, // 27 + { 5, 3, 33, 63, 93, 172 }, // 28 + { 5, 4, 34, 64, 94, 173 }, // 29 + { 5, 5, 35, 65, 95, 174 }, // 30 + { 5, 6, 36, 66, 96, 175 }, // 31 +#else + { 4, 1, 31, 61, 91 }, // 26 + { 4, 2, 32, 62, 92 }, // 27 + { 4, 3, 33, 63, 93 }, // 28 + { 4, 4, 34, 64, 94 }, // 29 + { 4, 5, 35, 65, 95 }, // 30 + { 4, 6, 36, 66, 96 }, // 31 +#endif + { 0, 0, 0, 0, 0 }, // 32 + { 0, 0, 0, 0, 0 }, // 33 + + { 0, 0, 0, 0, 0 }, // 34 + { 0, 0, 0, 0, 0 }, // 35 + { 0, 0, 0, 0, 0 }, // 36 + { 0, 0, 0, 0, 0 }, // 37 + { 0, 0, 0, 0, 0 }, // 38 + { 0, 0, 0, 0, 0 }, // 39 + { 0, 0, 0, 0, 0 }, // 40 + + { 4, 16, 46, 76, 106 }, // 41 + { 4, 17, 47, 77, 107 }, // 42 + { 4, 18, 48, 78, 108 }, // 43 + { 4, 19, 49, 79, 109 }, // 44 + { 4, 20, 50, 80, 110 }, // 45 + { 4, 21, 51, 81, 111 }, // 46 + { 0, 0, 0, 0, 0 }, // 47 + { 0, 0, 0, 0, 0 }, // 48 + + { 0, 0, 0, 0, 0 }, // 49 + { 0, 0, 0, 0, 0 }, // 50 + +#ifdef ENABLE_WOLFMAN_CHARACTER + { 5, 1, 31, 61, 91, 170 }, // 51 + { 5, 2, 32, 62, 92, 171 }, // 52 + { 5, 3, 33, 63, 93, 172 }, // 53 + { 5, 4, 34, 64, 94, 173 }, // 54 + { 5, 5, 35, 65, 95, 174 }, // 55 + { 5, 6, 36, 66, 96, 175 }, // 56 +#else + { 4, 1, 31, 61, 91 }, // 51 + { 4, 2, 32, 62, 92 }, // 52 + { 4, 3, 33, 63, 93 }, // 53 + { 4, 4, 34, 64, 94 }, // 54 + { 4, 5, 35, 65, 95 }, // 55 + { 4, 6, 36, 66, 96 }, // 56 +#endif + { 0, 0, 0, 0, 0 }, // 57 + { 0, 0, 0, 0, 0 }, // 58 + + { 0, 0, 0, 0, 0 }, // 59 + { 0, 0, 0, 0, 0 }, // 60 + { 0, 0, 0, 0, 0 }, // 61 + { 0, 0, 0, 0, 0 }, // 62 + { 0, 0, 0, 0, 0 }, // 63 + { 0, 0, 0, 0, 0 }, // 64 + { 0, 0, 0, 0, 0 }, // 65 + + { 4, 16, 46, 76, 106 }, // 66 + { 4, 17, 47, 77, 107 }, // 67 + { 4, 18, 48, 78, 108 }, // 68 + { 4, 19, 49, 79, 109 }, // 69 + { 4, 20, 50, 80, 110 }, // 70 + { 4, 21, 51, 81, 111 }, // 71 + { 0, 0, 0, 0, 0 }, // 72 + { 0, 0, 0, 0, 0 }, // 73 + + { 0, 0, 0, 0, 0 }, // 74 + { 0, 0, 0, 0, 0 }, // 75 + +#ifdef ENABLE_WOLFMAN_CHARACTER + { 5, 1, 31, 61, 91, 170 }, // 76 + { 5, 2, 32, 62, 92, 171 }, // 77 + { 5, 3, 33, 63, 93, 172 }, // 78 + { 5, 4, 34, 64, 94, 173 }, // 79 + { 5, 5, 35, 65, 95, 174 }, // 80 + { 5, 6, 36, 66, 96, 175 }, // 81 +#else + { 4, 1, 31, 61, 91 }, // 76 + { 4, 2, 32, 62, 92 }, // 77 + { 4, 3, 33, 63, 93 }, // 78 + { 4, 4, 34, 64, 94 }, // 79 + { 4, 5, 35, 65, 95 }, // 80 + { 4, 6, 36, 66, 96 }, // 81 +#endif + { 0, 0, 0, 0, 0 }, // 82 + { 0, 0, 0, 0, 0 }, // 83 + + { 0, 0, 0, 0, 0 }, // 84 + { 0, 0, 0, 0, 0 }, // 85 + { 0, 0, 0, 0, 0 }, // 86 + { 0, 0, 0, 0, 0 }, // 87 + { 0, 0, 0, 0, 0 }, // 88 + { 0, 0, 0, 0, 0 }, // 89 + { 0, 0, 0, 0, 0 }, // 90 + + { 4, 16, 46, 76, 106 }, // 91 + { 4, 17, 47, 77, 107 }, // 92 + { 4, 18, 48, 78, 108 }, // 93 + { 4, 19, 49, 79, 109 }, // 94 + { 4, 20, 50, 80, 110 }, // 95 + { 4, 21, 51, 81, 111 }, // 96 + { 0, 0, 0, 0, 0 }, // 97 + { 0, 0, 0, 0, 0 }, // 98 + + { 0, 0, 0, 0, 0 }, // 99 + { 0, 0, 0, 0, 0 }, // 100 + + { 1, 152, 0, 0, 0}, // 101 + { 1, 153, 0, 0, 0}, // 102 + { 1, 154, 0, 0, 0}, // 103 + { 1, 155, 0, 0, 0}, // 104 + { 1, 156, 0, 0, 0}, // 105 + { 1, 157, 0, 0, 0}, // 106 + + { 0, 0, 0, 0, 0}, // 107 + { 0, 0, 0, 0, 0}, // 108 + { 0, 0, 0, 0, 0}, // 109 + { 0, 0, 0, 0, 0}, // 110 + { 0, 0, 0, 0, 0}, // 111 + { 0, 0, 0, 0, 0}, // 112 + { 0, 0, 0, 0, 0}, // 113 + { 0, 0, 0, 0, 0}, // 114 + { 0, 0, 0, 0, 0}, // 115 + { 0, 0, 0, 0, 0}, // 116 + { 0, 0, 0, 0, 0}, // 117 + { 0, 0, 0, 0, 0}, // 118 + { 0, 0, 0, 0, 0}, // 119 + { 0, 0, 0, 0, 0}, // 120 + + { 2, 137, 140, 0, 0}, // 121 + { 1, 138, 0, 0, 0}, // 122 + { 1, 139, 0, 0, 0}, // 123 + + }; + + if (dwMotionIndex >= MOTION_MAX_NUM) + { + sys_err("OUT_OF_MOTION_VNUM: name=%s, motion=%d/%d", GetName(), dwMotionIndex, MOTION_MAX_NUM); + return false; + } + + DWORD* skillVNums = s_anMotion2SkillVnumList[dwMotionIndex]; + + DWORD skillCount = *skillVNums++; + if (skillCount >= SKILL_LIST_MAX_COUNT) + { + sys_err("OUT_OF_SKILL_LIST: name=%s, count=%d/%d", GetName(), skillCount, SKILL_LIST_MAX_COUNT); + return false; + } + + for (DWORD skillIndex = 0; skillIndex != skillCount; ++skillIndex) + { + if (skillIndex >= SKILL_MAX_NUM) + { + sys_err("OUT_OF_SKILL_VNUM: name=%s, skill=%d/%d", GetName(), skillIndex, SKILL_MAX_NUM); + return false; + } + + DWORD eachSkillVNum = skillVNums[skillIndex]; + if ( eachSkillVNum != 0 ) + { + DWORD eachJobGroup = s_anSkill2JobGroup[eachSkillVNum]; + + if (0 == eachJobGroup || eachJobGroup == selfJobGroup) + { + // GUILDSKILL_BUG_FIX + DWORD eachSkillLevel = 0; + + if (eachSkillVNum >= GUILD_SKILL_START && eachSkillVNum <= GUILD_SKILL_END) + { + if (GetGuild()) + eachSkillLevel = GetGuild()->GetSkillLevel(eachSkillVNum); + else + eachSkillLevel = 0; + } + else + { + eachSkillLevel = GetSkillLevel(eachSkillVNum); + } + + if (eachSkillLevel > 0) + { + return true; + } + // END_OF_GUILDSKILL_BUG_FIX + } + } + } + + return false; +} + +void CHARACTER::ClearSkill() +{ + PointChange(POINT_SKILL, 4 + (GetLevel() - 5) - GetPoint(POINT_SKILL)); + RemoveSkillAffect(); // @fixme305 + ResetSkill(); +} + +void CHARACTER::ClearSubSkill() +{ + PointChange(POINT_SUB_SKILL, GetLevel() < 10 ? 0 : (GetLevel() - 9) - GetPoint(POINT_SUB_SKILL)); + + if (m_pSkillLevels == NULL) + { + sys_err("m_pSkillLevels nil (name: %s)", GetName()); + return; + } + + TPlayerSkill CleanSkill; + memset(&CleanSkill, 0, sizeof(TPlayerSkill)); + + size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]); + + for (size_t i = 0; i < count; ++i) + { + if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM) + continue; + + m_pSkillLevels[s_adwSubSkillVnums[i]] = CleanSkill; + } + + ComputePoints(); + SkillLevelPacket(); +} + +bool CHARACTER::ResetOneSkill(DWORD dwVnum) +{ + if (NULL == m_pSkillLevels) + { + sys_err("m_pSkillLevels nil (name %s, vnum %u)", GetName(), dwVnum); + return false; + } + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (name %s, vnum %u)", GetName(), dwVnum); + return false; + } + + BYTE level = m_pSkillLevels[dwVnum].bLevel; + + m_pSkillLevels[dwVnum].bLevel = 0; + m_pSkillLevels[dwVnum].bMasterType = 0; + m_pSkillLevels[dwVnum].tNextRead = 0; + + if (level > 17) + level = 17; + + PointChange(POINT_SKILL, level); + + LogManager::instance().CharLog(this, dwVnum, "ONE_SKILL_RESET_BY_SCROLL", ""); + + ComputePoints(); + SkillLevelPacket(); + + return true; +} + +eMountType GetMountLevelByVnum(DWORD dwMountVnum, bool IsNew) // updated to 2014/12/10 +{ + if (!dwMountVnum) + return MOUNT_TYPE_NONE; + + switch (dwMountVnum) + { + // ### YES SKILL + // @fixme116 begin + case 20107: // normal military horse (no guild) + case 20108: // normal military horse (guild member) + case 20109: // normal military horse (guild master) + if (IsNew) + return MOUNT_TYPE_NONE; + // @fixme116 end + // Classic + case 20110: // Classic Boar + case 20111: // Classic Wolf + case 20112: // Classic Tiger + case 20113: // Classic Lion + case 20114: // White Lion + // Special Lv2 + case 20115: // Wild Battle Boar + case 20116: // Fight Wolf + case 20117: // Storm Tiger + case 20118: // Battle Lion (bugged) + case 20205: // Wild Battle Boar (alternative) + case 20206: // Fight Wolf (alternative) + case 20207: // Storm Tiger (alternative) + case 20208: // Battle Lion (bugged) (alternative) + // Royal Tigers + case 20120: // blue + case 20121: // dark red + case 20122: // gold + case 20123: // green + case 20124: // pied + case 20125: // white + // Royal mounts (Special Lv3) + case 20209: // Royal Boar + case 20210: // Royal Wolf + case 20211: // Royal Tiger + case 20212: // Royal Lion + + case 20215: // Rudolph m Lv3 (yes skill, yes atk) + case 20218: // Rudolph f Lv3 (yes skill, yes atk) + case 20225: // Dyno Lv3 (yes skill, yes atk) + case 20230: // Turkey Lv3 (yes skill, yes atk) + return MOUNT_TYPE_MILITARY; + break; + // ### NO SKILL YES ATK + // @fixme116 begin + case 20104: // normal combat horse (no guild) + case 20105: // normal combat horse (guild member) + case 20106: // normal combat horse (guild master) + if (IsNew) + return MOUNT_TYPE_NONE; + // @fixme116 end + case 20119: // Black Horse (no skill, yes atk) + case 20214: // Rudolph m Lv2 (no skill, yes atk) + case 20217: // Rudolph f Lv2 (no skill, yes atk) + case 20219: // Equus Porphyreus (no skill, yes atk) + case 20220: // Comet (no skill, yes atk) + case 20221: // Polar Predator (no skill, yes atk) + case 20222: // Armoured Panda (no skill, yes atk) + case 20224: // Dyno Lv2 (no skill, yes atk) + case 20226: // Nightmare (no skill, yes atk) + case 20227: // Unicorn (no skill, yes atk) + case 20229: // Turkey Lv2 (no skill, yes atk) + case 20231: // Leopard (no skill, yes atk) + case 20232: // Black Panther (no skill, yes atk) + return MOUNT_TYPE_COMBAT; + break; + // ### NO SKILL NO ATK + // @fixme116 begin + case 20101: // normal beginner horse (no guild) + case 20102: // normal beginner horse (guild member) + case 20103: // normal beginner horse (guild master) + if (IsNew) + return MOUNT_TYPE_NONE; + // @fixme116 end + case 20213: // Rudolph m Lv1 (no skill, no atk) + case 20216: // Rudolph f Lv1 (no skill, no atk) + // Special Lv1 + case 20201: // Boar Lv1 (no skill, no atk) + case 20202: // Wolf Lv1 (no skill, no atk) + case 20203: // Tiger Lv1 (no skill, no atk) + case 20204: // Lion Lv1 (no skill, no atk) + + case 20223: // Dyno Lv1 (no skill, no atk) + case 20228: // Turkey Lv1 (no skill, no atk) + return MOUNT_TYPE_NORMAL; + break; + default: + return MOUNT_TYPE_NONE; + break; + } +} + +const int SKILL_COUNT = 6; +static const DWORD SkillList[JOB_MAX_NUM][SKILL_GROUP_MAX_NUM][SKILL_COUNT] = +{ + { { 1, 2, 3, 4, 5, 6 }, { 16, 17, 18, 19, 20, 21 } }, + { { 31, 32, 33, 34, 35, 36 }, { 46, 47, 48, 49, 50, 51 } }, + { { 61, 62, 63, 64, 65, 66 }, { 76, 77, 78, 79, 80, 81 } }, + { { 91, 92, 93, 94, 95, 96 }, { 106,107,108,109,110,111 } }, +#ifdef ENABLE_WOLFMAN_CHARACTER + { { 170,171,172,173,174,175 }, { 0, 0, 0, 0, 0, 0 } }, +#endif +}; + +const DWORD GetRandomSkillVnum(BYTE bJob) +{ + // the chosen skill + DWORD dwSkillVnum = 0; + do + { + // tmp stuff + DWORD tmpJob = (bJob != JOB_MAX_NUM)?MINMAX(0, bJob, JOB_MAX_NUM-1):number(0, JOB_MAX_NUM-1); + DWORD tmpSkillGroup = number(0, SKILL_GROUP_MAX_NUM-1); + DWORD tmpSkillCount = number(0, SKILL_COUNT-1); + // set skill + dwSkillVnum = SkillList[tmpJob][tmpSkillGroup][tmpSkillCount]; + +#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_WOLFMAN_BOOKS) + if (tmpJob==JOB_WOLFMAN) + continue; +#endif + + if (dwSkillVnum != 0 && NULL != CSkillManager::instance().Get(dwSkillVnum)) + break; + } while (true); + return dwSkillVnum; +} + +bool CHARACTER::CanUseSkill(DWORD dwSkillVnum) const +{ + if (0 == dwSkillVnum) return false; + + if (0 < GetSkillGroup()) + { + const DWORD* pSkill = SkillList[ GetJob() ][ GetSkillGroup()-1 ]; + + for (int i=0 ; i < SKILL_COUNT ; ++i) + { + if (pSkill[i] == dwSkillVnum) return true; + } + } + + //if (true == IsHorseRiding()) + + if (true == IsRiding()) + { +#ifndef ENABLE_NO_MOUNT_CHECK + eMountType eIsMount = GetMountLevelByVnum(GetMountVnum(), false); + if (eIsMount != MOUNT_TYPE_MILITARY) + { + if (test_server) + sys_log(0, "CanUseSkill: Mount can't skill. vnum(%u) type(%d)", GetMountVnum(), static_cast(eIsMount)); + return false; + } +#endif + switch(dwSkillVnum) + { + case SKILL_HORSE_WILDATTACK: + case SKILL_HORSE_CHARGE: + case SKILL_HORSE_ESCAPE: + case SKILL_HORSE_WILDATTACK_RANGE: + return true; + } + } + + switch( dwSkillVnum ) + { + case 121: case 122: case 124: case 126: case 127: case 128: case 129: case 130: + case 131: + case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: + return true; + } + + return false; +} + +bool CHARACTER::CheckSkillHitCount(const BYTE SkillID, const VID TargetVID) +{ + std::map::iterator iter = m_SkillUseInfo.find(SkillID); + + if (iter == m_SkillUseInfo.end()) + { + sys_log(0, "SkillHack: Skill(%u) is not in container", SkillID); + return false; + } + + TSkillUseInfo& rSkillUseInfo = iter->second; + + if (false == rSkillUseInfo.bUsed) + { + sys_log(0, "SkillHack: not used skill(%u)", SkillID); + return false; + } + + switch (SkillID) + { + case SKILL_YONGKWON: + case SKILL_HWAYEOMPOK: + case SKILL_DAEJINGAK: + case SKILL_PAERYONG: + sys_log(0, "SkillHack: cannot use attack packet for skill(%u)", SkillID); + return false; + } + + target_map::iterator iterTargetMap = rSkillUseInfo.TargetVIDMap.find(TargetVID); + + if (rSkillUseInfo.TargetVIDMap.end() != iterTargetMap) + { + size_t MaxAttackCountPerTarget = 1; + + switch (SkillID) + { + case SKILL_SAMYEON: + case SKILL_CHARYUN: +#ifdef ENABLE_WOLFMAN_CHARACTER + case SKILL_CHAYEOL: +#endif + MaxAttackCountPerTarget = 3; + break; + + case SKILL_HORSE_WILDATTACK_RANGE: + MaxAttackCountPerTarget = 5; + break; + + case SKILL_YEONSA: + MaxAttackCountPerTarget = 7; + break; + + case SKILL_HORSE_ESCAPE: + MaxAttackCountPerTarget = 10; + break; + } + + if (iterTargetMap->second >= MaxAttackCountPerTarget) + { + sys_log(0, "SkillHack: Too Many Hit count from SkillID(%u) count(%u)", SkillID, iterTargetMap->second); + return false; + } + + iterTargetMap->second++; + } + else + { + rSkillUseInfo.TargetVIDMap.emplace(TargetVID, 1); + } + + return true; +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/char_state.cpp b/source-server/Srcs/Server/game/src/char_state.cpp new file mode 100644 index 000000000..adc138271 --- /dev/null +++ b/source-server/Srcs/Server/game/src/char_state.cpp @@ -0,0 +1,1161 @@ +#include "stdafx.h" +#include "config.h" +#include "utils.h" +#include "vector.h" +#include "char.h" +#include "battle.h" +#include "char_manager.h" +#include "packet.h" +#include "motion.h" +#include "party.h" +#include "affect.h" +#include "buffer_manager.h" +#include "questmanager.h" +#include "p2p.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "exchange.h" +#include "sectree_manager.h" +#include "xmas_event.h" +#include "guild_manager.h" +#include "war_map.h" +#include "locale_service.h" +#include "BlueDragon.h" + +#include "../../common/VnumHelper.h" + +extern LPCHARACTER FindVictim(LPCHARACTER pkChr, int iMaxDistance); + +namespace +{ + class FuncFindChrForFlag + { + public: + FuncFindChrForFlag(LPCHARACTER pkChr) : + m_pkChr(pkChr), m_pkChrFind(NULL), m_iMinDistance(INT_MAX) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + if (ent->IsObserverMode()) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!pkChr->IsPC()) + return; + + if (!pkChr->GetGuild()) + return; + + if (pkChr->IsDead()) + return; + + int iDist = DISTANCE_APPROX(pkChr->GetX()-m_pkChr->GetX(), pkChr->GetY()-m_pkChr->GetY()); + + if (iDist <= 500 && m_iMinDistance > iDist && + !pkChr->IsAffectFlag(AFF_WAR_FLAG1) && + !pkChr->IsAffectFlag(AFF_WAR_FLAG2) && + !pkChr->IsAffectFlag(AFF_WAR_FLAG3)) + { + if ((DWORD) m_pkChr->GetPoint(POINT_STAT) == pkChr->GetGuild()->GetID()) + { + CWarMap * pMap = pkChr->GetWarMap(); + BYTE idx; + + if (!pMap || !pMap->GetTeamIndex(pkChr->GetGuild()->GetID(), idx)) + return; + + if (!pMap->IsFlagOnBase(idx)) + { + m_pkChrFind = pkChr; + m_iMinDistance = iDist; + } + } + else + { + m_pkChrFind = pkChr; + m_iMinDistance = iDist; + } + } + } + + LPCHARACTER m_pkChr; + LPCHARACTER m_pkChrFind; + int m_iMinDistance; + }; + + class FuncFindChrForFlagBase + { + public: + FuncFindChrForFlagBase(LPCHARACTER pkChr) : m_pkChr(pkChr) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + if (ent->IsObserverMode()) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!pkChr->IsPC()) + return; + + CGuild * pkGuild = pkChr->GetGuild(); + + if (!pkGuild) + return; + + int iDist = DISTANCE_APPROX(pkChr->GetX()-m_pkChr->GetX(), pkChr->GetY()-m_pkChr->GetY()); + + if (iDist <= 500 && + (pkChr->IsAffectFlag(AFF_WAR_FLAG1) || + pkChr->IsAffectFlag(AFF_WAR_FLAG2) || + pkChr->IsAffectFlag(AFF_WAR_FLAG3))) + { + CAffect * pkAff = pkChr->FindAffect(AFFECT_WAR_FLAG); + + sys_log(0, "FlagBase %s dist %d aff %p flag gid %d chr gid %u", + pkChr->GetName(), iDist, pkAff, m_pkChr->GetPoint(POINT_STAT), + pkChr->GetGuild()->GetID()); + + if (pkAff) + { + if ((DWORD) m_pkChr->GetPoint(POINT_STAT) == pkGuild->GetID() && + m_pkChr->GetPoint(POINT_STAT) != pkAff->lApplyValue) + { + CWarMap * pMap = pkChr->GetWarMap(); + BYTE idx; + + if (!pMap || !pMap->GetTeamIndex(pkGuild->GetID(), idx)) + return; + + //if (pMap->IsFlagOnBase(idx)) + { + BYTE idx_opp = idx == 0 ? 1 : 0; + + SendGuildWarScore(m_pkChr->GetPoint(POINT_STAT), pkAff->lApplyValue, 1); + //SendGuildWarScore(pkAff->lApplyValue, m_pkChr->GetPoint(POINT_STAT), -1); + + pMap->ResetFlag(); + //pMap->AddFlag(idx_opp); + //pkChr->RemoveAffect(AFFECT_WAR_FLAG); + + char buf[256]; + snprintf(buf, sizeof(buf), LC_TEXT("%s 尡 %s Ѿҽϴ!"), pMap->GetGuild(idx)->GetName(), pMap->GetGuild(idx_opp)->GetName()); + pMap->Notice(buf); + } + } + } + } + } + + LPCHARACTER m_pkChr; + }; + + class FuncFindGuardVictim + { + public: + FuncFindGuardVictim(LPCHARACTER pkChr, int iMaxDistance) : + m_iMinDistance(INT_MAX), + m_iMaxDistance(iMaxDistance), + m_lx(pkChr->GetX()), + m_ly(pkChr->GetY()), + m_pkChrVictim(NULL) + { + }; + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (pkChr->IsPC()) + return; + + if (pkChr->IsNPC() && !pkChr->IsMonster()) + return; + + if (pkChr->IsDead()) + return; + + if (pkChr->IsAffectFlag(AFF_EUNHYUNG) || + pkChr->IsAffectFlag(AFF_INVISIBILITY) || + pkChr->IsAffectFlag(AFF_REVIVE_INVISIBLE)) + return; + + if (pkChr->GetRaceNum() == 5001) + return; + + int iDistance = DISTANCE_APPROX(m_lx - pkChr->GetX(), m_ly - pkChr->GetY()); + + if (iDistance < m_iMinDistance && iDistance <= m_iMaxDistance) + { + m_pkChrVictim = pkChr; + m_iMinDistance = iDistance; + } + } + + LPCHARACTER GetVictim() + { + return (m_pkChrVictim); + } + + private: + int m_iMinDistance; + int m_iMaxDistance; + long m_lx; + long m_ly; + + LPCHARACTER m_pkChrVictim; + }; +} + +bool CHARACTER::IsAggressive() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_AGGRESSIVE); +} + +void CHARACTER::SetAggressive() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_AGGRESSIVE); +} + +bool CHARACTER::IsCoward() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_COWARD); +} + +void CHARACTER::SetCoward() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_COWARD); +} + +bool CHARACTER::IsBerserker() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_BERSERK); +} + +bool CHARACTER::IsStoneSkinner() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_STONESKIN); +} + +bool CHARACTER::IsGodSpeeder() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_GODSPEED); +} + +bool CHARACTER::IsDeathBlower() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_DEATHBLOW); +} + +bool CHARACTER::IsReviver() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_REVIVE); +} + +void CHARACTER::CowardEscape() +{ + int iDist[4] = {500, 1000, 3000, 5000}; + + for (int iDistIdx = 2; iDistIdx >= 0; --iDistIdx) + for (int iTryCount = 0; iTryCount < 8; ++iTryCount) + { + SetRotation(number(0, 359)); + + float fx, fy; + float fDist = number(iDist[iDistIdx], iDist[iDistIdx+1]); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + bool bIsWayBlocked = false; + for (int j = 1; j <= 100; ++j) + { + if (!SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx*j/100, GetY() + (int) fy*j/100)) + { + bIsWayBlocked = true; + break; + } + } + + if (bIsWayBlocked) + continue; + + m_dwStateDuration = PASSES_PER_SEC(1); + + int iDestX = GetX() + (int) fx; + int iDestY = GetY() + (int) fy; + + if (Goto(iDestX, iDestY)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + sys_log(0, "WAEGU move to %d %d (far)", iDestX, iDestY); + return; + } +} + +void CHARACTER::SetNoAttackShinsu() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKSHINSU); +} +bool CHARACTER::IsNoAttackShinsu() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKSHINSU); +} + +void CHARACTER::SetNoAttackChunjo() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKCHUNJO); +} + +bool CHARACTER::IsNoAttackChunjo() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKCHUNJO); +} + +void CHARACTER::SetNoAttackJinno() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKJINNO); +} + +bool CHARACTER::IsNoAttackJinno() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKJINNO); +} + +void CHARACTER::SetAttackMob() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_ATTACKMOB); +} + +bool CHARACTER::IsAttackMob() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_ATTACKMOB); +} + +// STATE_IDLE_REFACTORING +void CHARACTER::StateIdle() +{ + if (IsStone()) + { + __StateIdle_Stone(); + return; + } + else if (IsWarp() || IsGoto()) + { + m_dwStateDuration = 60 * passes_per_sec; + return; + } + + if (IsPC()) + return; + + if (!IsMonster()) + { + __StateIdle_NPC(); + return; + } + + __StateIdle_Monster(); +} + +void CHARACTER::__StateIdle_Stone() +{ + m_dwStateDuration = PASSES_PER_SEC(1); + + int iPercent = 0; // @fixme136 + if (GetMaxHP() >= 0) + iPercent = (GetHP() * 100) / GetMaxHP(); + DWORD dwVnum = number(MIN(GetMobTable().sAttackSpeed, GetMobTable().sMovingSpeed ), MAX(GetMobTable().sAttackSpeed, GetMobTable().sMovingSpeed)); + + if (iPercent <= 10 && GetMaxSP() < 10) + { + SetMaxSP(10); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1500, GetY() - 1500, GetX() + 1500, GetY() + 1500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 20 && GetMaxSP() < 9) + { + SetMaxSP(9); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1500, GetY() - 1500, GetX() + 1500, GetY() + 1500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 30 && GetMaxSP() < 8) + { + SetMaxSP(8); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 40 && GetMaxSP() < 7) + { + SetMaxSP(7); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 50 && GetMaxSP() < 6) + { + SetMaxSP(6); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 60 && GetMaxSP() < 5) + { + SetMaxSP(5); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 70 && GetMaxSP() < 4) + { + SetMaxSP(4); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 80 && GetMaxSP() < 3) + { + SetMaxSP(3); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 90 && GetMaxSP() < 2) + { + SetMaxSP(2); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 99 && GetMaxSP() < 1) + { + SetMaxSP(1); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else + return; + + UpdatePacket(); + return; +} + +void CHARACTER::__StateIdle_NPC() +{ + MonsterChat(MONSTER_CHAT_WAIT); + m_dwStateDuration = PASSES_PER_SEC(5); + + if (IsPet()) + return; + else if (IsGuardNPC()) + { + if (!quest::CQuestManager::instance().GetEventFlag("noguard")) + { + FuncFindGuardVictim f(this, 50000); + + if (GetSectree()) + GetSectree()->ForEachAround(f); + + LPCHARACTER victim = f.GetVictim(); + + if (victim) + { + m_dwStateDuration = passes_per_sec/2; + + if (CanBeginFight()) + BeginFight(victim); + } + } + } + else + { + if (GetRaceNum() == xmas::MOB_SANTA_VNUM) + { + if (get_dword_time() > m_dwPlayStartTime) + { + int next_warp_time = 2 * 1000; + + m_dwPlayStartTime = get_dword_time() + next_warp_time; + + const int WARP_MAP_INDEX_NUM = 7; + static const long c_lWarpMapIndexs[WARP_MAP_INDEX_NUM] = { 61, 62, 63, 64, 3, 23, 43 }; + long lNextMapIndex; + lNextMapIndex = c_lWarpMapIndexs[number(1, WARP_MAP_INDEX_NUM) - 1]; + + if (map_allow_find(lNextMapIndex)) + { + M2_DESTROY_CHARACTER(this); + int iNextSpawnDelay = 50 * 60; + + xmas::SpawnSanta(lNextMapIndex, iNextSpawnDelay); + } + else + { + TPacketGGXmasWarpSanta p; + p.bHeader = HEADER_GG_XMAS_WARP_SANTA; + p.bChannel = g_bChannel; + p.lMapIndex = lNextMapIndex; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGXmasWarpSanta)); + } + return; + } + } + + if (!IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + { + LPCHARACTER pkChrProtege = GetProtege(); + + if (pkChrProtege) + { + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 500) + { + if (Follow(pkChrProtege, number(100, 300))) + return; + } + } + + if (!number(0, 6)) + { + SetRotation(number(0, 359)); + + float fx, fy; + float fDist = number(200, 400); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + if (!(SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx / 2, GetY() + (int) fy / 2))) + return; + + SetNowWalking(true); + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + return; + } + } + } +} + +void CHARACTER::__StateIdle_Monster() +{ + if (IsStun()) + return; + + if (!CanMove()) + return; + + if (IsCoward()) + { + if (!IsDead()) + CowardEscape(); + + return; + } + + if (IsBerserker()) + if (IsBerserk()) + SetBerserk(false); + + if (IsGodSpeeder()) + if (IsGodSpeed()) + SetGodSpeed(false); + + LPCHARACTER victim = GetVictim(); + + if (!victim || victim->IsDead()) + { + SetVictim(NULL); + victim = NULL; + m_dwStateDuration = PASSES_PER_SEC(1); + } + + if (!victim || victim->IsBuilding()) + { + if (m_pkChrStone) + { + victim = m_pkChrStone->GetNearestVictim(m_pkChrStone); + } + + else if (!no_wander && IsAggressive()) + { + if (GetMapIndex() == 61 && quest::CQuestManager::instance().GetEventFlag("xmas_tree")); + + else + victim = FindVictim(this, m_pkMobData->m_table.wAggressiveSight); + } + } + + if (victim && !victim->IsDead()) + { + if (CanBeginFight()) + BeginFight(victim); + + return; + } + + if (IsAggressive() && !victim) + m_dwStateDuration = PASSES_PER_SEC(number(1, 3)); + else + m_dwStateDuration = PASSES_PER_SEC(number(3, 5)); + + LPCHARACTER pkChrProtege = GetProtege(); + + if (pkChrProtege) + { + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000) + { + if (Follow(pkChrProtege, number(150, 400))) + { + MonsterLog("[IDLE] κ ʹ ָ ! Ѵ."); + return; + } + } + } + + if (!no_wander && !IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + { + if (!number(0, 6)) + { + SetRotation(number(0, 359)); + + float fx, fy; + float fDist = number(300, 700); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + if (!(SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx/2, GetY() + (int) fy/2))) + return; + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + return; + } + } + + MonsterChat(MONSTER_CHAT_WAIT); +} +// END_OF_STATE_IDLE_REFACTORING + +bool __CHARACTER_GotoNearTarget(LPCHARACTER self, LPCHARACTER victim) +{ + if (IS_SET(self->GetAIFlag(), AIFLAG_NOMOVE)) + return false; + + switch (self->GetMobBattleType()) + { + case BATTLE_TYPE_RANGE: + case BATTLE_TYPE_MAGIC: + + if (self->Follow(victim, self->GetMobAttackRange() * 8 / 10)) + return true; + break; + + default: + + if (self->Follow(victim, self->GetMobAttackRange() * 9 / 10)) + return true; + } + + return false; +} + +void CHARACTER::StateMove() +{ + DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime; + float fRate = (float) dwElapsedTime / (float) m_dwMoveDuration; + + if (fRate > 1.0f) + fRate = 1.0f; + + int x = (int) ((float) (m_posDest.x - m_posStart.x) * fRate + m_posStart.x); + int y = (int) ((float) (m_posDest.y - m_posStart.y) * fRate + m_posStart.y); + + Move(x, y); + + if (IsPC() && (thecore_pulse() & 15) == 0) + { + UpdateSectree(); + + if (GetExchange()) + { + LPCHARACTER victim = GetExchange()->GetCompany()->GetOwner(); + int iDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + if (iDist >= EXCHANGE_MAX_DISTANCE) + { + GetExchange()->Cancel(); + } + } + } + + if (IsPC()) + { + if (IsWalking() && GetStamina() < GetMaxStamina()) + { + if (get_dword_time() - GetWalkStartTime() > 5000) + PointChange(POINT_STAMINA, GetMaxStamina() / 1); + } + + if (!IsWalking() && !IsRiding()){ + if ((get_dword_time() - GetLastAttackTime()) < 20000) + { + StartAffectEvent(); + + if (IsStaminaHalfConsume()) + { + if (thecore_pulse()&1) + PointChange(POINT_STAMINA, -STAMINA_PER_STEP); + } + else + PointChange(POINT_STAMINA, -STAMINA_PER_STEP); + + StartStaminaConsume(); + + if (GetStamina() <= 0) + { + SetStamina(0); + SetNowWalking(true); + StopStaminaConsume(); + } + } + else if (IsStaminaConsume()) + { + StopStaminaConsume(); + } + } + } + else + { + // XXX AGGRO + if (IsMonster() && GetVictim()) + { + LPCHARACTER victim = GetVictim(); + UpdateAggrPoint(victim, DAMAGE_TYPE_NORMAL, -(victim->GetLevel() / 3 + 1)); + } + + if (IsMonster() && GetMobRank() >= MOB_RANK_BOSS && GetVictim()) + { + LPCHARACTER victim = GetVictim(); + + if (GetRaceNum() == 2191 && number(1, 20) == 1 && get_dword_time() - m_pkMobInst->m_dwLastWarpTime > 1000) + { + float fx, fy; + GetDeltaByDegree(victim->GetRotation(), 400, &fx, &fy); + long new_x = victim->GetX() + (long)fx; + long new_y = victim->GetY() + (long)fy; + SetRotation(GetDegreeFromPositionXY(new_x, new_y, victim->GetX(), victim->GetY())); + Show(victim->GetMapIndex(), new_x, new_y, 0, true); + GotoState(m_stateBattle); + m_dwStateDuration = 1; + ResetMobSkillCooltime(); + m_pkMobInst->m_dwLastWarpTime = get_dword_time(); + return; + } + + if (number(0, 3) == 0) + { + if (__CHARACTER_GotoNearTarget(this, victim)) + return; + } + } + } + + if (1.0f == fRate) + { + if (IsPC()) + { + sys_log(1, " %s %d %d", GetName(), x, y); + GotoState(m_stateIdle); + StopStaminaConsume(); + } + else + { + if (GetVictim() && !IsCoward()) + { + if (!IsState(m_stateBattle)) + MonsterLog("[BATTLE] ó ݽ %s", GetVictim()->GetName()); + + GotoState(m_stateBattle); + m_dwStateDuration = 1; + } + else + { + if (!IsState(m_stateIdle)) + MonsterLog("[IDLE] "); + + GotoState(m_stateIdle); + + //LPCHARACTER rider = GetRider(); + + m_dwStateDuration = PASSES_PER_SEC(number(1, 3)); + } + } + } +} + +void CHARACTER::StateBattle() +{ + if (IsStone()) + { + sys_err("Stone must not use battle state (name %s)", GetName()); + return; + } + + if (IsPC()) + return; + + if (!CanMove()) + return; + + if (IsStun()) + return; + + LPCHARACTER victim = GetVictim(); + + if (IsCoward()) + { + if (IsDead()) + return; + + SetVictim(NULL); + + if (number(1, 50) != 1) + { + GotoState(m_stateIdle); + m_dwStateDuration = 1; + } + else + CowardEscape(); + + return; + } + + if (!victim || (victim->IsStun() && IsGuardNPC()) || victim->IsDead()) + { + if (victim && victim->IsDead() && + !no_wander && IsAggressive() && (!GetParty() || GetParty()->GetLeader() == this)) + { + LPCHARACTER new_victim = FindVictim(this, m_pkMobData->m_table.wAggressiveSight); + + SetVictim(new_victim); + m_dwStateDuration = PASSES_PER_SEC(1); + + if (!new_victim) + { + switch (GetMobBattleType()) + { + case BATTLE_TYPE_MELEE: + case BATTLE_TYPE_SUPER_POWER: + case BATTLE_TYPE_SUPER_TANKER: + case BATTLE_TYPE_POWER: + case BATTLE_TYPE_TANKER: + { + float fx, fy; + float fDist = number(400, 1500); + + GetDeltaByDegree(number(0, 359), fDist, &fx, &fy); + + if (SECTREE_MANAGER::instance().IsAttackablePosition(victim->GetMapIndex(), + victim->GetX() + (int) fx, + victim->GetY() + (int) fy) && + SECTREE_MANAGER::instance().IsAttackablePosition(victim->GetMapIndex(), + victim->GetX() + (int) fx/2, + victim->GetY() + (int) fy/2)) + { + float dx = victim->GetX() + fx; + float dy = victim->GetY() + fy; + + SetRotation(GetDegreeFromPosition(dx, dy)); + + if (Goto((long) dx, (long) dy)) + { + sys_log(0, "KILL_AND_GO: %s distance %.1f", GetName(), fDist); + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + } + } + } + } + return; + } + + SetVictim(NULL); + + if (IsGuardNPC()) + Return(); + + m_dwStateDuration = PASSES_PER_SEC(1); + return; + } + + if (IsSummonMonster() && !IsDead() && !IsStun()) + { + if (!GetParty()) + { + CPartyManager::instance().CreateParty(this); + } + + LPPARTY pParty = GetParty(); + bool bPct = !number(0, 3); + + if (bPct && pParty->CountMemberByVnum(GetSummonVnum()) < SUMMON_MONSTER_COUNT) + { + MonsterLog(" ȯ!"); + + int sx = GetX() - 300; + int sy = GetY() - 300; + int ex = GetX() + 300; + int ey = GetY() + 300; + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(GetSummonVnum(), GetMapIndex(), sx, sy, ex, ey, true, true); + + if (tch) + { + pParty->Join(tch->GetVID()); + pParty->Link(tch); + } + } + } + + LPCHARACTER pkChrProtege = GetProtege(); + + float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + if (fDist >= 4000.0f) + { + MonsterLog("Ÿ ־ "); + SetVictim(NULL); + + if (pkChrProtege) + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000) + Follow(pkChrProtege, number(150, 400)); + + return; + } + + if (fDist >= GetMobAttackRange() * 1.15) + { + __CHARACTER_GotoNearTarget(this, victim); + return; + } + + if (m_pkParty) + m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0); + + if (2493 == m_pkMobData->m_table.dwVnum) + { + m_dwStateDuration = BlueDragon_StateBattle(this); + return; + } + + DWORD dwCurTime = get_dword_time(); + DWORD dwDuration = CalculateDuration(GetLimitPoint(POINT_ATT_SPEED), 2000); + + if ((dwCurTime - m_dwLastAttackTime) < dwDuration) + { + m_dwStateDuration = MAX(1, (passes_per_sec * (dwDuration - (dwCurTime - m_dwLastAttackTime)) / 1000)); + return; + } + + if (IsBerserker() == true) + if (GetHPPct() < m_pkMobData->m_table.bBerserkPoint) + if (IsBerserk() != true) + SetBerserk(true); + + if (IsGodSpeeder() == true) + if (GetHPPct() < m_pkMobData->m_table.bGodSpeedPoint) + if (IsGodSpeed() != true) + SetGodSpeed(true); + + if (HasMobSkill()) + { + for (unsigned int iSkillIdx = 0; iSkillIdx < MOB_SKILL_MAX_NUM; ++iSkillIdx) + { + if (CanUseMobSkill(iSkillIdx)) + { + SetRotationToXY(victim->GetX(), victim->GetY()); + + if (UseMobSkill(iSkillIdx)) + { + SendMovePacket(FUNC_MOB_SKILL, iSkillIdx, GetX(), GetY(), 0, dwCurTime); + + float fDuration = CMotionManager::instance().GetMotionDuration(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + iSkillIdx)); + m_dwStateDuration = (DWORD) (fDuration == 0.0f ? PASSES_PER_SEC(2) : PASSES_PER_SEC(fDuration)); + + if (test_server) + sys_log(0, "USE_MOB_SKILL: %s idx %u motion %u duration %.0f", GetName(), iSkillIdx, MOTION_SPECIAL_1 + iSkillIdx, fDuration); + + return; + } + } + } + } + + if (!Attack(victim)) + m_dwStateDuration = passes_per_sec / 2; + else + { + SetRotationToXY(victim->GetX(), victim->GetY()); + + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0, dwCurTime); + + float fDuration = CMotionManager::instance().GetMotionDuration(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_NORMAL_ATTACK)); + m_dwStateDuration = (DWORD) (fDuration == 0.0f ? PASSES_PER_SEC(2) : PASSES_PER_SEC(fDuration)); + } +} + +void CHARACTER::StateFlag() +{ + m_dwStateDuration = (DWORD) PASSES_PER_SEC(0.5); + + CWarMap * pMap = GetWarMap(); + + if (!pMap) + return; + + FuncFindChrForFlag f(this); + GetSectree()->ForEachAround(f); + + if (!f.m_pkChrFind) + return; + + if (NULL == f.m_pkChrFind->GetGuild()) + return; + + char buf[256]; + BYTE idx; + + if (!pMap->GetTeamIndex(GetPoint(POINT_STAT), idx)) + return; + + f.m_pkChrFind->AddAffect(AFFECT_WAR_FLAG, POINT_NONE, GetPoint(POINT_STAT), idx == 0 ? AFF_WAR_FLAG1 : AFF_WAR_FLAG2, INFINITE_AFFECT_DURATION, 0, false); + f.m_pkChrFind->AddAffect(AFFECT_WAR_FLAG, POINT_MOV_SPEED, 50 - f.m_pkChrFind->GetPoint(POINT_MOV_SPEED), 0, INFINITE_AFFECT_DURATION, 0, false); + + pMap->RemoveFlag(idx); + + snprintf(buf, sizeof(buf), LC_TEXT("%s %s ȹϿϴ."), pMap->GetGuild(idx)->GetName(), f.m_pkChrFind->GetName()); + pMap->Notice(buf); +} + +void CHARACTER::StateFlagBase() +{ + m_dwStateDuration = (DWORD) PASSES_PER_SEC(0.5); + + FuncFindChrForFlagBase f(this); + GetSectree()->ForEachAround(f); +} + +void CHARACTER::StateHorse() +{ + float START_FOLLOW_DISTANCE = 400.0f; + float START_RUN_DISTANCE = 700.0f; + int MIN_APPROACH = 150; + int MAX_APPROACH = 300; + + DWORD STATE_DURATION = (DWORD)PASSES_PER_SEC(0.5); + + bool bDoMoveAlone = true; + bool bRun = true; + + if (IsDead()) + return; + + m_dwStateDuration = STATE_DURATION; + + LPCHARACTER victim = GetRider(); + + if (!victim) + { + M2_DESTROY_CHARACTER(this); + return; + } + + m_pkMobInst->m_posLastAttacked = GetXYZ(); + + float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + if (fDist >= START_FOLLOW_DISTANCE) + { + if (fDist > START_RUN_DISTANCE) + SetNowWalking(!bRun); + + Follow(victim, number(MIN_APPROACH, MAX_APPROACH)); + + m_dwStateDuration = STATE_DURATION; + } + else if (bDoMoveAlone && (get_dword_time() > m_dwLastAttackTime)) + { + // wondering-.- + m_dwLastAttackTime = get_dword_time() + number(5000, 12000); + + SetRotation(number(0, 359)); + + float fx, fy; + float fDist = number(200, 400); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + if (!(SECTREE_MANAGER::instance().IsAttackablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsAttackablePosition(GetMapIndex(), GetX() + (int) fx/2, GetY() + (int) fy/2))) + return; + + SetNowWalking(true); + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cipher.cpp b/source-server/Srcs/Server/game/src/cipher.cpp new file mode 100644 index 000000000..e133dabcf --- /dev/null +++ b/source-server/Srcs/Server/game/src/cipher.cpp @@ -0,0 +1,403 @@ +#include "stdafx.h" + +#include "cipher.h" + +#ifdef _IMPROVED_PACKET_ENCRYPTION_ + +#include +#include +#include + +// Diffie-Hellman key agreement +#include +#include + +// AES winner and candidates +//#include +#include +#include +#include +#include +#include +// Other block ciphers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace CryptoPP; + +// Block cipher algorithm selector abstract base class. +struct BlockCipherAlgorithm { + enum { + kDefault, // to give more chances to default algorithm + // AES winner and candidates +// kAES, // Rijndael + kRC6, + kMARS, + kTwofish, + kSerpent, + kCAST256, + // Other block ciphers + kIDEA, + k3DES, // DES-EDE2 + kCamellia, + kSEED, + kRC5, + kBlowfish, + kTEA, +// kSKIPJACK, + kSHACAL2, + // End sentinel + kMaxAlgorithms + }; + + BlockCipherAlgorithm() {} + virtual ~BlockCipherAlgorithm() {} + + static BlockCipherAlgorithm* Pick(int hint); + + virtual int GetBlockSize() const = 0; + virtual int GetDefaultKeyLength() const = 0; + virtual int GetIVLength() const = 0; + + virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen, + const byte* iv) const = 0; + virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen, + const byte* iv) const = 0; +}; + +// Block cipher (with CTR mode) algorithm selector template class. +template +struct BlockCipherDetail : public BlockCipherAlgorithm { + BlockCipherDetail() {} + virtual ~BlockCipherDetail() {} + + virtual int GetBlockSize() const { return T::BLOCKSIZE; } + virtual int GetDefaultKeyLength() const { return T::DEFAULT_KEYLENGTH; } + virtual int GetIVLength() const { return T::IV_LENGTH; } + + virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen, + const byte* iv) const { + return new typename CTR_Mode::Encryption(key, keylen, iv); + } + virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen, + const byte* iv) const { + return new typename CTR_Mode::Decryption(key, keylen, iv); + } +}; + +// Key agreement scheme abstract class. +class KeyAgreement { + public: + KeyAgreement() {} + virtual ~KeyAgreement() {} + + virtual size_t Prepare(void* buffer, size_t* length) = 0; + virtual bool Agree(size_t agreed_length, const void* buffer, size_t length) = 0; + + const SecByteBlock& shared() const { return shared_; } + + protected: + SecByteBlock shared_; +}; + +// Crypto++ Unified Diffie-Hellman key agreement scheme implementation. +class DH2KeyAgreement : public KeyAgreement { + public: + DH2KeyAgreement(); + virtual ~DH2KeyAgreement(); + + virtual size_t Prepare(void* buffer, size_t* length); + virtual bool Agree(size_t agreed_length, const void* buffer, size_t length); + + private: + DH dh_; + DH2 dh2_; + SecByteBlock spriv_key_; + SecByteBlock epriv_key_; +}; + +Cipher::Cipher() + : activated_(false), encoder_(NULL), decoder_(NULL), key_agreement_(NULL) { +} + +Cipher::~Cipher() { + if (activated_) { + CleanUp(); + } +} + +void Cipher::CleanUp() { + if (encoder_ != NULL) { + delete encoder_; + encoder_ = NULL; + } + if (decoder_ != NULL) { + delete decoder_; + decoder_ = NULL; + } + if (key_agreement_ != NULL) { + delete key_agreement_; + key_agreement_ = NULL; + } + activated_ = false; +} + +size_t Cipher::Prepare(void* buffer, size_t* length) { + assert(key_agreement_ == NULL); + key_agreement_ = new DH2KeyAgreement(); + assert(key_agreement_ != NULL); + size_t agreed_length = key_agreement_->Prepare(buffer, length); + if (agreed_length == 0) { + delete key_agreement_; + key_agreement_ = NULL; + } + return agreed_length; +} + +bool Cipher::Activate(bool polarity, size_t agreed_length, + const void* buffer, size_t length) { + assert(activated_ == false); + assert(key_agreement_ != NULL); + if (activated_ != false) + return false; + + if (key_agreement_->Agree(agreed_length, buffer, length)) { + activated_ = SetUp(polarity); + } + delete key_agreement_; + key_agreement_ = NULL; + return activated_; +} + +bool Cipher::SetUp(bool polarity) { + assert(key_agreement_ != NULL); + const SecByteBlock& shared = key_agreement_->shared(); + + // Pick a block cipher algorithm + + if (shared.size() < 2) { + return false; + } + int hint_0 = shared.BytePtr()[*(shared.BytePtr()) % shared.size()]; + int hint_1 = shared.BytePtr()[*(shared.BytePtr() + 1) % shared.size()]; + BlockCipherAlgorithm* detail_0 = BlockCipherAlgorithm::Pick(hint_0); + BlockCipherAlgorithm* detail_1 = BlockCipherAlgorithm::Pick(hint_1); + assert(detail_0 != NULL); + assert(detail_1 != NULL); + std::unique_ptr algorithm_0(detail_0); + std::unique_ptr algorithm_1(detail_1); + + const size_t key_length_0 = algorithm_0->GetDefaultKeyLength(); + const size_t iv_length_0 = algorithm_0->GetBlockSize(); + if (shared.size() < key_length_0 || shared.size() < iv_length_0) { + return false; + } + const size_t key_length_1 = algorithm_1->GetDefaultKeyLength(); + const size_t iv_length_1 = algorithm_1->GetBlockSize(); + if (shared.size() < key_length_1 || shared.size() < iv_length_1) { + return false; + } + + // Pick encryption keys and initial vectors + + SecByteBlock key_0(key_length_0), iv_0(iv_length_0); + SecByteBlock key_1(key_length_1), iv_1(iv_length_1); + + size_t offset; + + key_0.Assign(shared, key_length_0); + offset = key_length_0; +#ifdef __GNUC__ + offset = std::min(key_length_0, shared.size() - key_length_1); +#else + offset = std::min(key_length_0, shared.size() - key_length_1); +#endif + key_1.Assign(shared.BytePtr() + offset, key_length_1); + + offset = shared.size() - iv_length_0; + iv_0.Assign(shared.BytePtr() + offset, iv_length_0); + offset = (offset < iv_length_1 ? 0 : offset - iv_length_1); + iv_1.Assign(shared.BytePtr() + offset, iv_length_1); + + // Create encryption/decryption objects + + if (polarity) { + encoder_ = algorithm_1->CreateEncoder(key_1, key_1.size(), iv_1); + decoder_ = algorithm_0->CreateDecoder(key_0, key_0.size(), iv_0); + } else { + encoder_ = algorithm_0->CreateEncoder(key_0, key_0.size(), iv_0); + decoder_ = algorithm_1->CreateDecoder(key_1, key_1.size(), iv_1); + } + assert(encoder_ != NULL); + assert(decoder_ != NULL); + return true; +} + +BlockCipherAlgorithm* BlockCipherAlgorithm::Pick(int hint) { + BlockCipherAlgorithm* detail; + int selector = hint % kMaxAlgorithms; + switch (selector) { +// case kAES: +// detail = new BlockCipherDetail(); + break; + case kRC6: + detail = new BlockCipherDetail(); + break; + case kMARS: + detail = new BlockCipherDetail(); + break; + case kTwofish: + detail = new BlockCipherDetail(); + break; + case kSerpent: + detail = new BlockCipherDetail(); + break; + case kCAST256: + detail = new BlockCipherDetail(); + break; + case kIDEA: + detail = new BlockCipherDetail(); + break; + case k3DES: + detail = new BlockCipherDetail(); + break; + case kCamellia: + detail = new BlockCipherDetail(); + break; + case kSEED: + detail = new BlockCipherDetail(); + break; + case kRC5: + detail = new BlockCipherDetail(); + break; + case kBlowfish: + detail = new BlockCipherDetail(); + break; + case kTEA: + detail = new BlockCipherDetail(); + break; +// case kSKIPJACK: +// detail = new BlockCipherDetail(); +// break; + case kSHACAL2: + detail = new BlockCipherDetail(); + break; + case kDefault: + default: + detail = new BlockCipherDetail(); // default algorithm + break; + } + return detail; +} + +DH2KeyAgreement::DH2KeyAgreement() : dh_(), dh2_(dh_) { +} + +DH2KeyAgreement::~DH2KeyAgreement() { +} + +size_t DH2KeyAgreement::Prepare(void* buffer, size_t* length) { + // RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup + // http://tools.ietf.org/html/rfc5114#section-2.1 + Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" + "DF1FB2BC2E4A4371"); + + Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + "855E6EEB22B3B2E5"); + + Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353"); + + // Schnorr Group primes are of the form p = rq + 1, p and q prime. They + // provide a subgroup order. In the case of 1024-bit MODP Group, the + // security level is 80 bits (based on the 160-bit prime order subgroup). + + // For a compare/contrast of using the maximum security level, see + // dh-unified.zip. Also see http://www.cryptopp.com/wiki/Diffie-Hellman + // and http://www.cryptopp.com/wiki/Security_level . + + AutoSeededRandomPool rnd; + + dh_.AccessGroupParameters().Initialize(p, q, g); + + if(!dh_.GetGroupParameters().ValidateGroup(rnd, 3)) { + // Failed to validate prime and generator + return 0; + } + + p = dh_.GetGroupParameters().GetModulus(); + q = dh_.GetGroupParameters().GetSubgroupOrder(); + g = dh_.GetGroupParameters().GetGenerator(); + + // http://groups.google.com/group/sci.crypt/browse_thread/thread/7dc7eeb04a09f0ce + Integer v = ModularExponentiation(g, q, p); + if(v != Integer::One()) { + // Failed to verify order of the subgroup + return 0; + } + + ////////////////////////////////////////////////////////////// + + spriv_key_.New(dh2_.StaticPrivateKeyLength()); + epriv_key_.New(dh2_.EphemeralPrivateKeyLength()); + SecByteBlock spub_key(dh2_.StaticPublicKeyLength()); + SecByteBlock epub_key(dh2_.EphemeralPublicKeyLength()); + + dh2_.GenerateStaticKeyPair(rnd, spriv_key_, spub_key); + dh2_.GenerateEphemeralKeyPair(rnd, epriv_key_, epub_key); + + // Prepare key agreement data + const size_t spub_key_length = spub_key.size(); + const size_t epub_key_length = epub_key.size(); + const size_t data_length = spub_key_length + epub_key_length; + if (*length < data_length) { + // Not enough data buffer length + return 0; + } + *length = data_length; + byte* buf = (byte*)buffer; + memcpy(buf, spub_key.BytePtr(), spub_key_length); + memcpy(buf + spub_key_length, epub_key.BytePtr(), epub_key_length); + + return dh2_.AgreedValueLength(); +} + +bool DH2KeyAgreement::Agree(size_t agreed_length, const void* buffer, size_t length) { + if (agreed_length != dh2_.AgreedValueLength()) { + // Shared secret size mismatch + return false; + } + const size_t spub_key_length = dh2_.StaticPublicKeyLength(); + const size_t epub_key_length = dh2_.EphemeralPublicKeyLength(); + if (length != (spub_key_length + epub_key_length)) { + // Wrong data length + return false; + } + shared_.New(dh2_.AgreedValueLength()); + const byte* buf = (const byte*)buffer; + if (!dh2_.Agree(shared_, spriv_key_, epriv_key_, buf, buf + spub_key_length)) { + // Failed to reach shared secret + return false; + } + return true; +} + +#endif // _IMPROVED_PACKET_ENCRYPTION_ + +// EOF cipher.cpp +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cipher.h b/source-server/Srcs/Server/game/src/cipher.h new file mode 100644 index 000000000..9c24a657b --- /dev/null +++ b/source-server/Srcs/Server/game/src/cipher.h @@ -0,0 +1,68 @@ +#ifndef __CIPHER_H__ +#define __CIPHER_H__ + +#ifdef _IMPROVED_PACKET_ENCRYPTION_ + +#include + +#ifdef __clang__ +namespace CryptoPP +{ + typedef BYTE byte; +} +#endif + +// Forward declaration +class KeyAgreement; + +// Communication channel encryption handler. +class Cipher { + public: + Cipher(); + ~Cipher(); + + void CleanUp(); + + // Returns agreed value length in bytes, or zero on failure. + size_t Prepare(void* buffer, size_t* length); + // Try to activate cipher algorithm with agreement data received from peer. + bool Activate(bool polarity, size_t agreed_length, + const void* buffer, size_t length); + + // Encrypts the given block of data. (no padding required) + void Encrypt(void* buffer, size_t length) { + assert(activated_); + if (!activated_) { + return; + } + encoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); + } + // Decrypts the given block of data. (no padding required) + void Decrypt(void* buffer, size_t length) { + assert(activated_); + if (!activated_) { + return; + } + decoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); + } + + bool activated() const { return activated_; } + void set_activated(bool value) { activated_ = value; } + + bool IsKeyPrepared() { return key_agreement_ != NULL; } + + private: + bool SetUp(bool polarity); + + bool activated_; + + CryptoPP::SymmetricCipher* encoder_; + CryptoPP::SymmetricCipher* decoder_; + + KeyAgreement* key_agreement_; +}; + +#endif // _IMPROVED_PACKET_ENCRYPTION_ + +#endif // __CIPHER_H__ +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cmd.cpp b/source-server/Srcs/Server/game/src/cmd.cpp new file mode 100644 index 000000000..065e9838f --- /dev/null +++ b/source-server/Srcs/Server/game/src/cmd.cpp @@ -0,0 +1,734 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "char.h" +#include "locale_service.h" +#include "log.h" +#include "desc.h" +#include "../../common/PulseManager.h" +// #define ENABLE_BLOCK_CMD_SHORTCUT + +ACMD(do_user_horse_ride); +ACMD(do_user_horse_back); +ACMD(do_user_horse_feed); + +// ADD_COMMAND_SLOW_STUN +ACMD(do_slow); +ACMD(do_stun); +// END_OF_ADD_COMMAND_SLOW_STUN + +ACMD(do_warp); +ACMD(do_goto); +ACMD(do_item); +ACMD(do_mob); +ACMD(do_mob_ld); +ACMD(do_mob_aggresive); +ACMD(do_mob_coward); +ACMD(do_mob_map); +ACMD(do_purge); +ACMD(do_weaken); +ACMD(do_item_purge); +ACMD(do_state); +ACMD(do_notice); +ACMD(do_map_notice); +ACMD(do_big_notice); +#ifdef ENABLE_FULL_NOTICE +ACMD(do_notice_test); +ACMD(do_big_notice_test); +ACMD(do_map_big_notice); +#endif +ACMD(do_who); +ACMD(do_user); +ACMD(do_disconnect); +ACMD(do_kill); +ACMD(do_emotion_allow); +ACMD(do_emotion); +ACMD(do_transfer); +ACMD(do_set); +ACMD(do_cmd); +ACMD(do_reset); +ACMD(do_greset); +ACMD(do_mount); +ACMD(do_fishing); +ACMD(do_refine_rod); + +// REFINE_PICK +ACMD(do_max_pick); +ACMD(do_refine_pick); +// END_OF_REFINE_PICK + +ACMD(do_fishing_simul); +ACMD(do_console); +ACMD(do_restart); +ACMD(do_advance); +ACMD(do_stat); +ACMD(do_respawn); +ACMD(do_skillup); +ACMD(do_guildskillup); +ACMD(do_pvp); +ACMD(do_point_reset); +ACMD(do_safebox_size); +ACMD(do_safebox_close); +ACMD(do_safebox_password); +ACMD(do_safebox_change_password); +ACMD(do_mall_password); +ACMD(do_mall_close); +ACMD(do_ungroup); +ACMD(do_makeguild); +ACMD(do_deleteguild); +ACMD(do_shutdown); +ACMD(do_group); +ACMD(do_group_random); +ACMD(do_invisibility); +ACMD(do_event_flag); +ACMD(do_get_event_flag); +ACMD(do_private); +ACMD(do_qf); +ACMD(do_clear_quest); +ACMD(do_book); +ACMD(do_reload); +ACMD(do_war); +ACMD(do_nowar); +ACMD(do_setskill); +ACMD(do_setskillother); +ACMD(do_level); +ACMD(do_polymorph); +ACMD(do_polymorph_item); +/* + ACMD(do_b1); + ACMD(do_b2); + ACMD(do_b3); + ACMD(do_b4); + ACMD(do_b5); + ACMD(do_b6); + ACMD(do_b7); + */ +ACMD(do_close_shop); +ACMD(do_set_walk_mode); +ACMD(do_set_run_mode); +ACMD(do_set_skill_group); +ACMD(do_set_skill_point); +ACMD(do_cooltime); +ACMD(do_detaillog); +ACMD(do_monsterlog); + +ACMD(do_gwlist); +ACMD(do_stop_guild_war); +ACMD(do_cancel_guild_war); +ACMD(do_guild_state); + +ACMD(do_pkmode); +ACMD(do_messenger_auth); + +ACMD(do_getqf); +ACMD(do_setqf); +ACMD(do_delqf); +ACMD(do_set_state); + +ACMD(do_forgetme); +ACMD(do_aggregate); +ACMD(do_attract_ranger); +ACMD(do_pull_monster); +ACMD(do_setblockmode); +ACMD(do_priv_empire); +ACMD(do_priv_guild); +ACMD(do_mount_test); +ACMD(do_unmount); +ACMD(do_observer); +ACMD(do_observer_exit); +ACMD(do_socket_item); +ACMD(do_xmas); +ACMD(do_stat_minus); +ACMD(do_stat_reset); +ACMD(do_view_equip); +ACMD(do_block_chat); +ACMD(do_vote_block_chat); + +// BLOCK_CHAT +ACMD(do_block_chat_list); +// END_OF_BLOCK_CHAT + +ACMD(do_party_request); +ACMD(do_party_request_deny); +ACMD(do_party_request_accept); +ACMD(do_build); +ACMD(do_clear_land); + +ACMD(do_horse_state); +ACMD(do_horse_level); +ACMD(do_horse_ride); +ACMD(do_horse_summon); +ACMD(do_horse_unsummon); +ACMD(do_horse_set_stat); + +ACMD(do_save_attribute_to_image); +ACMD(do_affect_remove); + +ACMD(do_change_attr); +ACMD(do_add_attr); +ACMD(do_add_socket); + +ACMD(do_inputall) +{ + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ɾ Էϼ.")); +} + +ACMD(do_show_arena_list); +ACMD(do_end_all_duel); +ACMD(do_end_duel); +ACMD(do_duel); + +ACMD(do_stat_plus_amount); + +ACMD(do_break_marriage); + +ACMD(do_oxevent_show_quiz); +ACMD(do_oxevent_log); +ACMD(do_oxevent_get_attender); + +ACMD(do_effect); +ACMD(do_threeway_war_info ); +ACMD(do_threeway_war_myinfo ); +// + +ACMD(do_monarch_warpto); +ACMD(do_monarch_transfer); +ACMD(do_monarch_info); +ACMD(do_elect); +ACMD(do_monarch_tax); +ACMD(do_monarch_mob); +ACMD(do_monarch_notice); + +ACMD(do_rmcandidacy); +ACMD(do_setmonarch); +ACMD(do_rmmonarch); + +ACMD(do_hair); +//gift notify quest command +ACMD(do_gift); + +ACMD(do_inventory); +ACMD(do_cube); + +ACMD(do_siege); +ACMD(do_temp); +ACMD(do_frog); + +ACMD(do_check_monarch_money); + +ACMD(do_reset_subskill ); +ACMD(do_flush); + +ACMD(do_eclipse); + +ACMD(do_event_helper); + +ACMD(do_in_game_mall); + +ACMD(do_get_mob_count); + +ACMD(do_dice); +ACMD(do_special_item); + +ACMD(do_click_mall); + +ACMD(do_ride); +ACMD(do_get_item_id_list); +ACMD(do_set_socket); + +ACMD(do_costume); +ACMD(do_set_stat); + +ACMD (do_can_dead); + +ACMD (do_full_set); + +ACMD (do_item_full_set); + +ACMD (do_attr_full_set); + +ACMD (do_all_skill_master); + +ACMD (do_use_item); +ACMD (do_dragon_soul); +ACMD (do_ds_list); +ACMD (do_clear_affect); + +#ifdef ENABLE_NEWSTUFF +ACMD(do_change_rare_attr); +ACMD(do_add_rare_attr); + +ACMD(do_click_safebox); +ACMD(do_force_logout); + +ACMD(do_poison); +ACMD(do_rewarp); +#endif +#ifdef ENABLE_WOLFMAN_CHARACTER +ACMD(do_bleeding); +#endif +#ifdef ENABLE_MOVE_CHANNEL +ACMD(DoChangeChannel); +#endif +#if defined(ENABLE_CHEQUE_SYSTEM) && defined(ENABLE_WON_EXCHANGE_WINDOW) +ACMD(do_won_exchange); +#endif + +struct command_info cmd_info[] = +{ + { "!RESERVED!", NULL, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "who", do_who, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "war", do_war, 0, POS_DEAD, GM_PLAYER }, + { "warp", do_warp, 0, POS_DEAD, GM_LOW_WIZARD }, + { "user", do_user, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "notice", do_notice, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "notice_map", do_map_notice, 0, POS_DEAD, GM_LOW_WIZARD }, + { "big_notice", do_big_notice, 0, POS_DEAD, GM_HIGH_WIZARD }, +#ifdef ENABLE_FULL_NOTICE + { "big_notice_map", do_map_big_notice, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "notice_test", do_notice_test, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "big_notice_test",do_big_notice_test, 0, POS_DEAD, GM_HIGH_WIZARD }, +#endif + { "nowar", do_nowar, 0, POS_DEAD, GM_PLAYER }, + { "purge", do_purge, 0, POS_DEAD, GM_WIZARD }, + { "weaken", do_weaken, 0, POS_DEAD, GM_GOD }, + { "dc", do_disconnect, 0, POS_DEAD, GM_LOW_WIZARD }, + { "transfer", do_transfer, 0, POS_DEAD, GM_LOW_WIZARD }, + { "goto", do_goto, 0, POS_DEAD, GM_LOW_WIZARD }, + { "level", do_level, 0, POS_DEAD, GM_LOW_WIZARD }, + { "eventflag", do_event_flag, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "geteventflag", do_get_event_flag, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "item", do_item, 0, POS_DEAD, GM_GOD }, + + { "mob", do_mob, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mob_ld", do_mob_ld, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "ma", do_mob_aggresive, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mc", do_mob_coward, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mm", do_mob_map, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "kill", do_kill, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "ipurge", do_item_purge, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "group", do_group, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "grrandom", do_group_random, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "set", do_set, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reset", do_reset, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "greset", do_greset, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "advance", do_advance, 0, POS_DEAD, GM_GOD }, + { "book", do_book, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "console", do_console, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "shutdow", do_inputall, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "shutdown", do_shutdown, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "stat", do_stat, 0, POS_DEAD, GM_PLAYER }, + { "stat-", do_stat_minus, 0, POS_DEAD, GM_PLAYER }, + { "stat_reset", do_stat_reset, 0, POS_DEAD, GM_LOW_WIZARD }, + { "state", do_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + // ADD_COMMAND_SLOW_STUN + { "stun", do_stun, 0, POS_DEAD, GM_LOW_WIZARD }, + { "slow", do_slow, 0, POS_DEAD, GM_LOW_WIZARD }, + // END_OF_ADD_COMMAND_SLOW_STUN + + { "respawn", do_respawn, 0, POS_DEAD, GM_WIZARD }, + + { "makeguild", do_makeguild, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "deleteguild", do_deleteguild, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "mount", do_mount, 0, POS_MOUNTING, GM_PLAYER }, + { "restart_here", do_restart, SCMD_RESTART_HERE, POS_DEAD, GM_PLAYER }, + { "restart_town", do_restart, SCMD_RESTART_TOWN, POS_DEAD, GM_PLAYER }, + { "phase_selec", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "phase_select", do_cmd, SCMD_PHASE_SELECT, POS_DEAD, GM_PLAYER }, + { "qui", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "quit", do_cmd, SCMD_QUIT, POS_DEAD, GM_PLAYER }, + { "logou", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "logout", do_cmd, SCMD_LOGOUT, POS_DEAD, GM_PLAYER }, + { "skillup", do_skillup, 0, POS_DEAD, GM_PLAYER }, + { "gskillup", do_guildskillup, 0, POS_DEAD, GM_PLAYER }, + { "pvp", do_pvp, 0, POS_DEAD, GM_PLAYER }, + { "safebox", do_safebox_size, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "safebox_close", do_safebox_close, 0, POS_DEAD, GM_PLAYER }, + { "safebox_passwor",do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "safebox_password",do_safebox_password, 0, POS_DEAD, GM_PLAYER }, + { "safebox_change_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "safebox_change_password", do_safebox_change_password, 0, POS_DEAD, GM_PLAYER }, + { "mall_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "mall_password", do_mall_password, 0, POS_DEAD, GM_PLAYER }, + { "mall_close", do_mall_close, 0, POS_DEAD, GM_PLAYER }, + + // Group Command + { "ungroup", do_ungroup, 0, POS_DEAD, GM_PLAYER }, + + // REFINE_ROD_HACK_BUG_FIX + { "refine_rod", do_refine_rod, 0, POS_DEAD, GM_IMPLEMENTOR }, + // END_OF_REFINE_ROD_HACK_BUG_FIX + + // REFINE_PICK + { "refine_pick", do_refine_pick, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "max_pick", do_max_pick, 0, POS_DEAD, GM_IMPLEMENTOR }, + // END_OF_REFINE_PICK + + { "fish_simul", do_fishing_simul, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "invisible", do_invisibility, 0, POS_DEAD, GM_LOW_WIZARD }, + { "qf", do_qf, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "clear_quest", do_clear_quest, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "close_shop", do_close_shop, 0, POS_DEAD, GM_PLAYER }, + + { "set_walk_mode", do_set_walk_mode, 0, POS_DEAD, GM_PLAYER }, + { "set_run_mode", do_set_run_mode, 0, POS_DEAD, GM_PLAYER }, + { "setjob",do_set_skill_group, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "setskill", do_setskill, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setskillother", do_setskillother, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "setskillpoint", do_set_skill_point, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reload", do_reload, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "cooltime", do_cooltime, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "gwlist", do_gwlist, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gwstop", do_stop_guild_war, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gwcancel", do_cancel_guild_war, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gstate", do_guild_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "pkmode", do_pkmode, 0, POS_DEAD, GM_PLAYER }, + { "messenger_auth", do_messenger_auth, 0, POS_DEAD, GM_PLAYER }, + + { "getqf", do_getqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setqf", do_setqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "delqf", do_delqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "set_state", do_set_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "detaillog", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD }, + { "monsterlog", do_monsterlog, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "forgetme", do_forgetme, 0, POS_DEAD, GM_LOW_WIZARD }, + { "aggregate", do_aggregate, 0, POS_DEAD, GM_LOW_WIZARD }, + { "attract_ranger", do_attract_ranger, 0, POS_DEAD, GM_LOW_WIZARD }, + { "pull_monster", do_pull_monster, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setblockmode", do_setblockmode, 0, POS_DEAD, GM_PLAYER }, + { "polymorph", do_polymorph, 0, POS_DEAD, GM_LOW_WIZARD }, + { "polyitem", do_polymorph_item, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "priv_empire", do_priv_empire, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "priv_guild", do_priv_guild, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mount_test", do_mount_test, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "unmount", do_unmount, 0, POS_DEAD, GM_PLAYER }, + { "private", do_private, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "party_request", do_party_request, 0, POS_DEAD, GM_PLAYER }, + { "party_request_accept", do_party_request_accept,0, POS_DEAD, GM_PLAYER }, + { "party_request_deny", do_party_request_deny,0, POS_DEAD, GM_PLAYER }, + { "observer", do_observer, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "observer_exit", do_observer_exit, 0, POS_DEAD, GM_PLAYER }, + { "socketitem", do_socket_item, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "saveati", do_save_attribute_to_image, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "xmas_boom", do_xmas, SCMD_XMAS_BOOM, POS_DEAD, GM_HIGH_WIZARD }, + { "xmas_snow", do_xmas, SCMD_XMAS_SNOW, POS_DEAD, GM_HIGH_WIZARD }, + { "xmas_santa", do_xmas, SCMD_XMAS_SANTA, POS_DEAD, GM_HIGH_WIZARD }, + { "view_equip", do_view_equip, 0, POS_DEAD, GM_PLAYER }, + { "jy", do_block_chat, 0, POS_DEAD, GM_HIGH_WIZARD }, + + // BLOCK_CHAT + { "vote_block_chat", do_vote_block_chat, 0, POS_DEAD, GM_PLAYER }, + { "block_chat", do_block_chat, 0, POS_DEAD, GM_PLAYER }, + { "block_chat_list",do_block_chat_list, 0, POS_DEAD, GM_PLAYER }, + // END_OF_BLOCK_CHAT + + { "build", do_build, 0, POS_DEAD, GM_PLAYER }, + { "clear_land", do_clear_land, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "affect_remove", do_affect_remove, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "horse_state", do_horse_state, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_level", do_horse_level, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_ride", do_horse_ride, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_summon", do_horse_summon, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_unsummon", do_horse_unsummon, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_set_stat", do_horse_set_stat, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "emotion_allow", do_emotion_allow, 0, POS_FIGHTING, GM_PLAYER }, + { "kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "slap", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "french_kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "clap", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheer1", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheer2", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + + // DANCE + { "dance1", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance2", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance3", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance4", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance5", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance6", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + // END_OF_DANCE + + { "congratulation", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "forgive", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "angry", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "attractive", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "sad", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "shy", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheerup", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "banter", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "joy", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + + { "change_attr", do_change_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "add_attr", do_add_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "add_socket", do_add_socket, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "user_horse_ride", do_user_horse_ride, 0, POS_FISHING, GM_PLAYER }, + { "user_horse_back", do_user_horse_back, 0, POS_FISHING, GM_PLAYER }, + { "user_horse_feed", do_user_horse_feed, 0, POS_FISHING, GM_PLAYER }, + + { "show_arena_list", do_show_arena_list, 0, POS_DEAD, GM_LOW_WIZARD }, + { "end_all_duel", do_end_all_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + { "end_duel", do_end_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + { "duel", do_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "con+", do_stat_plus_amount, POINT_HT, POS_DEAD, GM_LOW_WIZARD }, + { "int+", do_stat_plus_amount, POINT_IQ, POS_DEAD, GM_LOW_WIZARD }, + { "str+", do_stat_plus_amount, POINT_ST, POS_DEAD, GM_LOW_WIZARD }, + { "dex+", do_stat_plus_amount, POINT_DX, POS_DEAD, GM_LOW_WIZARD }, + + { "break_marriage", do_break_marriage, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "show_quiz", do_oxevent_show_quiz, 0, POS_DEAD, GM_LOW_WIZARD }, + { "log_oxevent", do_oxevent_log, 0, POS_DEAD, GM_LOW_WIZARD }, + { "get_oxevent_att", do_oxevent_get_attender,0, POS_DEAD, GM_LOW_WIZARD }, + + { "effect", do_effect, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "threeway_info", do_threeway_war_info, 0, POS_DEAD, GM_LOW_WIZARD}, + { "threeway_myinfo", do_threeway_war_myinfo, 0, POS_DEAD, GM_LOW_WIZARD}, + { "mto", do_monarch_warpto, 0, POS_DEAD, GM_PLAYER}, + { "mtr", do_monarch_transfer, 0, POS_DEAD, GM_PLAYER}, + { "minfo", do_monarch_info, 0, POS_DEAD, GM_PLAYER}, + { "mtax", do_monarch_tax, 0, POS_DEAD, GM_PLAYER}, + { "mmob", do_monarch_mob, 0, POS_DEAD, GM_PLAYER}, + { "elect", do_elect, 0, POS_DEAD, GM_HIGH_WIZARD}, + { "rmcandidacy", do_rmcandidacy, 0, POS_DEAD, GM_LOW_WIZARD}, + { "setmonarch", do_setmonarch, 0, POS_DEAD, GM_LOW_WIZARD}, + { "rmmonarch", do_rmmonarch, 0, POS_DEAD, GM_LOW_WIZARD}, + { "hair", do_hair, 0, POS_DEAD, GM_PLAYER }, + { "inventory", do_inventory, 0, POS_DEAD, GM_LOW_WIZARD }, + { "cube", do_cube, 0, POS_DEAD, GM_PLAYER }, + { "siege", do_siege, 0, POS_DEAD, GM_LOW_WIZARD }, + { "temp", do_temp, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "frog", do_frog, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "check_mmoney", do_check_monarch_money, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reset_subskill", do_reset_subskill, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "flush", do_flush, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "gift", do_gift, 0, POS_DEAD, GM_PLAYER }, //gift + + { "mnotice", do_monarch_notice, 0, POS_DEAD, GM_PLAYER }, + + { "eclipse", do_eclipse, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "eventhelper", do_event_helper, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "in_game_mall", do_in_game_mall, 0, POS_DEAD, GM_PLAYER }, + + { "get_mob_count", do_get_mob_count, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "dice", do_dice, 0, POS_DEAD, GM_PLAYER }, + { "special_item", do_special_item, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "click_mall", do_click_mall, 0, POS_DEAD, GM_PLAYER }, + + { "ride", do_ride, 0, POS_DEAD, GM_PLAYER }, + + { "item_id_list", do_get_item_id_list, 0, POS_DEAD, GM_LOW_WIZARD }, + { "set_socket", do_set_socket, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "costume", do_costume, 0, POS_DEAD, GM_PLAYER }, + + { "tcon", do_set_stat, POINT_HT, POS_DEAD, GM_LOW_WIZARD }, + { "tint", do_set_stat, POINT_IQ, POS_DEAD, GM_LOW_WIZARD }, + { "tstr", do_set_stat, POINT_ST, POS_DEAD, GM_LOW_WIZARD }, + { "tdex", do_set_stat, POINT_DX, POS_DEAD, GM_LOW_WIZARD }, + + { "cannot_dead", do_can_dead, 1, POS_DEAD, GM_LOW_WIZARD}, + { "can_dead", do_can_dead, 0, POS_DEAD, GM_LOW_WIZARD}, + + { "full_set", do_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "item_full_set", do_item_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "attr_full_set", do_attr_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "all_skill_master", do_all_skill_master, 0, POS_DEAD, GM_LOW_WIZARD}, + { "use_item", do_use_item, 0, POS_DEAD, GM_LOW_WIZARD}, + + { "dragon_soul", do_dragon_soul, 0, POS_DEAD, GM_PLAYER }, + { "ds_list", do_ds_list, 0, POS_DEAD, GM_PLAYER }, + { "do_clear_affect", do_clear_affect, 0, POS_DEAD, GM_LOW_WIZARD}, +#ifdef ENABLE_NEWSTUFF + //item + { "add_rare_attr", do_add_rare_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "change_rare_attr", do_change_rare_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + //player + { "click_safebox", do_click_safebox, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "force_logout", do_force_logout, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "poison", do_poison, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "rewarp", do_rewarp, 0, POS_DEAD, GM_LOW_WIZARD }, +#endif +#ifdef ENABLE_WOLFMAN_CHARACTER + { "bleeding", do_bleeding, 0, POS_DEAD, GM_IMPLEMENTOR }, +#endif +#ifdef ENABLE_MOVE_CHANNEL + { "change_channel", DoChangeChannel, 0, POS_DEAD, GM_PLAYER }, +#endif +#if defined(ENABLE_CHEQUE_SYSTEM) && defined(ENABLE_WON_EXCHANGE_WINDOW) + { "won_exchange", do_won_exchange, 0, POS_DEAD, GM_PLAYER }, +#endif + { "\n", NULL, 0, POS_DEAD, GM_IMPLEMENTOR } +}; + +void interpreter_set_privilege(const char *cmd, int lvl) +{ + int i; + + for (i = 0; *cmd_info[i].command != '\n'; ++i) + { + if (!str_cmp(cmd, cmd_info[i].command)) + { + cmd_info[i].gm_level = lvl; + sys_log(0, "Setting command privilege: %s -> %d", cmd, lvl); + break; + } + } +} + +void double_dollar(const char *src, size_t src_len, char *dest, size_t dest_len) +{ + const char * tmp = src; + size_t cur_len = 0; + + dest_len -= 1; + + while (src_len-- && *tmp) + { + if (*tmp == '$') + { + if (cur_len + 1 >= dest_len) + break; + + *(dest++) = '$'; + *(dest++) = *(tmp++); + cur_len += 2; + } + else + { + if (cur_len >= dest_len) + break; + + *(dest++) = *(tmp++); + cur_len += 1; + } + } + + *dest = '\0'; +} + +void interpret_command(LPCHARACTER ch, const char * argument, size_t len) +{ + if (!ch) + { + sys_err("NULL CHRACTER"); + return; + } + +#ifdef ENABLE_ANTI_CMD_FLOOD + if (ch && !PulseManager::Instance().IncreaseCount(ch->GetPlayerID(), ePulse::CommandRequest, std::chrono::milliseconds(500), 5)) + { + if (test_server) + ch->ChatPacket(CHAT_TYPE_INFO, "Blocked Command %s", argument); + sys_log(0, "BLOCKED COMMAND: %s: %s", ch->GetName(), argument); + return; + } +#endif + + char cmd[128 + 1]; + char new_line[256 + 1]; + const char * line; + int icmd; + + if (len == 0 || !*argument) + return; + + double_dollar(argument, len, new_line, sizeof(new_line)); + + size_t cmdlen; + line = first_cmd(new_line, cmd, sizeof(cmd), &cmdlen); + + for (icmd = 1; *cmd_info[icmd].command != '\n'; ++icmd) + { + if (cmd_info[icmd].command_pointer == do_cmd) + { + if (!strcmp(cmd_info[icmd].command, cmd)) + break; + } +#ifdef ENABLE_BLOCK_CMD_SHORTCUT + else if (!strcmp(cmd_info[icmd].command, cmd)) +#else + else if (!strncmp(cmd_info[icmd].command, cmd, cmdlen)) +#endif + break; + } + + if (ch->GetPosition() < cmd_info[icmd].minimum_position) + { + switch (ch->GetPosition()) + { + case POS_MOUNTING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ź ¿ ϴ.")); + break; + + case POS_DEAD: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ ϴ.")); + break; + + case POS_SLEEPING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("޼ӿ Կ?")); + break; + + case POS_RESTING: + case POS_SITTING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͼ .")); + break; + + default: + sys_err("unknown position %d", ch->GetPosition()); + break; + } + + return; + } + + if (*cmd_info[icmd].command == '\n') + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + if (cmd_info[icmd].gm_level && (cmd_info[icmd].gm_level > ch->GetGMLevel() || cmd_info[icmd].gm_level == GM_DISABLE)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + if (strncmp("phase", cmd_info[icmd].command, 5) != 0) + sys_log(0, "COMMAND: %s: %s", ch->GetName(), cmd_info[icmd].command); + + ((*cmd_info[icmd].command_pointer) (ch, line, icmd, cmd_info[icmd].subcmd)); + + if (ch->GetGMLevel() >= GM_LOW_WIZARD) + { + if (cmd_info[icmd].gm_level >= GM_LOW_WIZARD) + { + char buf[1024]; + snprintf( buf, sizeof(buf), "%s", argument ); + + LogManager::instance().GMCommandLog(ch->GetPlayerID(), ch->GetName(), ch->GetDesc()->GetHostName(), g_bChannel, buf); + } + } +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cmd.h b/source-server/Srcs/Server/game/src/cmd.h new file mode 100644 index 000000000..30bc1d968 --- /dev/null +++ b/source-server/Srcs/Server/game/src/cmd.h @@ -0,0 +1,75 @@ +#ifndef __INC_METIN_II_GAME_CMD_H__ +#define __INC_METIN_II_GAME_CMD_H__ + +#define ACMD(name) void (name)(LPCHARACTER ch, const char *argument, int cmd, int subcmd) +#define CMD_NAME(name) cmd_info[cmd].command + +struct command_info +{ + const char * command; + void (*command_pointer) (LPCHARACTER ch, const char *argument, int cmd, int subcmd); + int subcmd; + int minimum_position; + int gm_level; +}; + +extern struct command_info cmd_info[]; + +extern void interpret_command(LPCHARACTER ch, const char * argument, size_t len); +extern void interpreter_set_privilege(const char * cmd, int lvl); + +enum SCMD_ACTION +{ + SCMD_SLAP, + SCMD_KISS, + SCMD_FRENCH_KISS, + SCMD_HUG, + SCMD_LONG_HUG, + SCMD_SHOLDER, + SCMD_FOLD_ARM +}; + +enum SCMD_CMD +{ + SCMD_LOGOUT, + SCMD_QUIT, + SCMD_PHASE_SELECT, + SCMD_SHUTDOWN, +}; + +enum SCMD_RESTART +{ + SCMD_RESTART_TOWN, + SCMD_RESTART_HERE +}; + +enum SCMD_XMAS +{ + SCMD_XMAS_BOOM, + SCMD_XMAS_SNOW, + SCMD_XMAS_SANTA, +}; + +extern void Shutdown(int iSec); +extern void SendLog(const char * c_pszBuf); +#ifdef ENABLE_FULL_NOTICE +extern void SendNotice(const char * c_pszBuf, bool bBigFont=false); +extern void BroadcastNotice(const char * c_pszBuf, bool bBigFont=false); +#else +extern void SendNotice(const char * c_pszBuf); +extern void BroadcastNotice(const char * c_pszBuf); +#endif +extern void SendNoticeMap(const char* c_pszBuf, int nMapIndex, bool bBigFont); +extern void SendMonarchNotice(BYTE bEmpire, const char * c_pszBuf); + +// LUA_ADD_BGM_INFO +void CHARACTER_SetBGMVolumeEnable(); +void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol); +// END_OF_LUA_ADD_BGM_INFO + +// LUA_ADD_GOTO_INFO +extern void CHARACTER_AddGotoInfo(const std::string& c_st_name, BYTE empire, int mapIndex, DWORD x, DWORD y); +// END_OF_LUA_ADD_GOTO_INFO + +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cmd_emotion.cpp b/source-server/Srcs/Server/game/src/cmd_emotion.cpp new file mode 100644 index 000000000..230d9ca69 --- /dev/null +++ b/source-server/Srcs/Server/game/src/cmd_emotion.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "utils.h" +#include "char.h" +#include "char_manager.h" +#include "motion.h" +#include "packet.h" +#include "buffer_manager.h" +#include "unique_item.h" +#include "wedding.h" + +#define NEED_TARGET (1 << 0) +#define NEED_PC (1 << 1) +#define WOMAN_ONLY (1 << 2) +#define OTHER_SEX_ONLY (1 << 3) +#define SELF_DISARM (1 << 4) +#define TARGET_DISARM (1 << 5) +#define BOTH_DISARM (SELF_DISARM | TARGET_DISARM) + +struct emotion_type_s +{ + const char * command; + const char * command_to_client; + long flag; + float extra_delay; +} emotion_types[] = { + { "Ű", "french_kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 2.0f }, + { "ǻ", "kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 1.5f }, + { "", "slap", NEED_PC | SELF_DISARM, 1.5f }, + { "ڼ", "clap", 0, 1.0f }, + { "", "cheer1", 0, 1.0f }, + { "", "cheer2", 0, 1.0f }, + + // DANCE + { "1", "dance1", 0, 1.0f }, + { "2", "dance2", 0, 1.0f }, + { "3", "dance3", 0, 1.0f }, + { "4", "dance4", 0, 1.0f }, + { "5", "dance5", 0, 1.0f }, + { "6", "dance6", 0, 1.0f }, + // END_OF_DANCE + { "", "congratulation", 0, 1.0f }, + { "뼭", "forgive", 0, 1.0f }, + { "ȭ", "angry", 0, 1.0f }, + { "Ȥ", "attractive", 0, 1.0f }, + { "", "sad", 0, 1.0f }, + { "", "shy", 0, 1.0f }, + { "", "cheerup", 0, 1.0f }, + { "", "banter", 0, 1.0f }, + { "", "joy", 0, 1.0f }, + { "\n", "\n", 0, 0.0f }, +}; + +std::set > s_emotion_set; + +ACMD(do_emotion_allow) +{ + if ( ch->GetArena() ) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD val = 0; str_to_number(val, arg1); + s_emotion_set.emplace(ch->GetVID(), val); +} + +#ifdef ENABLE_NEWSTUFF +#include "config.h" +#endif + +bool CHARACTER_CanEmotion(CHARACTER& rch) +{ +#ifdef ENABLE_NEWSTUFF + if (g_bDisableEmotionMask) + return true; +#endif + + if (marriage::WeddingManager::instance().IsWeddingMap(rch.GetMapIndex())) + return true; + + if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK)) + return true; + + if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK2)) + return true; + + return false; +} + +ACMD(do_emotion) +{ + int i; + { + if (ch->IsRiding()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ǥ ϴ.")); + return; + } + } + + for (i = 0; *emotion_types[i].command != '\n'; ++i) + { + if (!strcmp(cmd_info[cmd].command, emotion_types[i].command)) + break; + + if (!strcmp(cmd_info[cmd].command, emotion_types[i].command_to_client)) + break; + } + + if (*emotion_types[i].command == '\n') + { + sys_err("cannot find emotion"); + return; + } + + if (!CHARACTER_CanEmotion(*ch)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ÿ ֽϴ.")); + return; + } + + if (IS_SET(emotion_types[i].flag, WOMAN_ONLY) && SEX_MALE==GET_SEX(ch)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڸ ֽϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + LPCHARACTER victim = NULL; + + if (*arg1) + victim = ch->FindCharacterInView(arg1, IS_SET(emotion_types[i].flag, NEED_PC)); + + if (IS_SET(emotion_types[i].flag, NEED_TARGET | NEED_PC)) + { + if (!victim) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ϴ.")); + return; + } + } + + if (victim) + { + if (!victim->IsPC() || victim == ch) + return; + + if (victim->IsRiding()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ǥ ϴ.")); + return; + } + + long distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY()); + + if (distance < 10) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ʹ ֽϴ.")); + return; + } + + if (distance > 500) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ʹ ָ ֽϴ")); + return; + } + + if (IS_SET(emotion_types[i].flag, OTHER_SEX_ONLY)) + { + if (GET_SEX(ch)==GET_SEX(victim)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̼ ֽϴ.")); + return; + } + } + + if (IS_SET(emotion_types[i].flag, NEED_PC)) + { + if (s_emotion_set.find(std::make_pair(victim->GetVID(), ch->GetVID())) == s_emotion_set.end()) + { + if (true == marriage::CManager::instance().IsMarried( ch->GetPlayerID() )) + { + const marriage::TMarriage* marriageInfo = marriage::CManager::instance().Get( ch->GetPlayerID() ); + + const DWORD other = marriageInfo->GetOther( ch->GetPlayerID() ); + + if (0 == other || other != victim->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ൿ ȣ Ͽ մϴ.")); + return; + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ൿ ȣ Ͽ մϴ.")); + return; + } + } + + s_emotion_set.emplace(ch->GetVID(), victim->GetVID()); + } + } + + char chatbuf[256+1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "%s %u %u", + emotion_types[i].command_to_client, + (DWORD) ch->GetVID(), victim ? (DWORD) victim->GetVID() : 0); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + ch->PacketAround(buf.read_peek(), buf.size()); + + if (victim) + sys_log(1, "ACTION: %s TO %s", emotion_types[i].command, victim->GetName()); + else + sys_log(1, "ACTION: %s", emotion_types[i].command); +} +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cmd_general.cpp b/source-server/Srcs/Server/game/src/cmd_general.cpp new file mode 100644 index 000000000..6b311fa87 --- /dev/null +++ b/source-server/Srcs/Server/game/src/cmd_general.cpp @@ -0,0 +1,2402 @@ +#include "stdafx.h" +#ifdef __FreeBSD__ +#include +#else +#include "../../libthecore/include/xmd5.h" +#endif + +#include "utils.h" +#include "config.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "char.h" +#include "char_manager.h" +#include "motion.h" +#include "packet.h" +#include "affect.h" +#include "pvp.h" +#include "start_position.h" +#include "party.h" +#include "guild_manager.h" +#include "p2p.h" +#include "dungeon.h" +#include "messenger_manager.h" +#include "war_map.h" +#include "questmanager.h" +#include "item_manager.h" +#include "monarch.h" +#include "mob_manager.h" +#include "item.h" +#include "arena.h" +#include "buffer_manager.h" +#include "unique_item.h" +#include "threeway_war.h" +#include "log.h" +#include "../../common/VnumHelper.h" + +ACMD(do_user_horse_ride) +{ + if (ch->IsObserverMode()) + return; + + if (ch->IsDead() || ch->IsStun()) + return; + + if (ch->IsHorseRiding() == false) + { + if (ch->GetMountVnum()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ż ̿Դϴ.")); + return; + } + + if (ch->GetHorse() == NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + return; + } + + ch->StartRiding(); + } + else + { + ch->StopRiding(); + } +} + +ACMD(do_user_horse_back) +{ + if (ch->GetHorse() != NULL) + { + ch->HorseSummon(false); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ½ϴ.")); + } + else if (ch->IsHorseRiding() == true) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ.")); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + } +} + +ACMD(do_user_horse_feed) +{ + if (ch->GetMyShop()) + return; + + if (ch->GetHorse() == NULL) + { + if (ch->IsHorseRiding() == false) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ̸ ϴ.")); + return; + } + + DWORD dwFood = ch->GetHorseGrade() + 50054 - 1; + + auto foodTable = ITEM_MANAGER::instance().GetTable(dwFood); + auto foodName = (foodTable) ? foodTable->szLocaleName : "NONAME"; // @fixme307 + + if (ch->CountSpecifyItem(dwFood) > 0) + { + ch->RemoveSpecifyItem(dwFood, 1); + ch->FeedHorse(); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %s%s ־ϴ."), foodName, ""); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ʿմϴ"), foodName); + } +} + +#define MAX_REASON_LEN 128 + +EVENTINFO(TimedEventInfo) +{ + DynamicCharacterPtr ch; + int subcmd; + int left_second; + char szReason[MAX_REASON_LEN]; + + TimedEventInfo() + : ch() + , subcmd( 0 ) + , left_second( 0 ) + { + ::memset( szReason, 0, MAX_REASON_LEN ); + } +}; + +struct SendDisconnectFunc +{ + void operator () (LPDESC d) + { + if (d->GetCharacter()) + { + if (d->GetCharacter()->GetGMLevel() == GM_PLAYER) + d->GetCharacter()->ChatPacket(CHAT_TYPE_COMMAND, "quit Shutdown(SendDisconnectFunc)"); + } + } +}; + +struct DisconnectFunc +{ + void operator () (LPDESC d) + { + if (d->GetType() == DESC_TYPE_CONNECTOR) + return; + + if (d->IsPhase(PHASE_P2P)) + return; + + if (d->GetCharacter()) + d->GetCharacter()->Disconnect("Shutdown(DisconnectFunc)"); + + d->SetPhase(PHASE_CLOSE); + } +}; + +EVENTINFO(shutdown_event_data) +{ + int seconds; + + shutdown_event_data() + : seconds( 0 ) + { + } +}; + +EVENTFUNC(shutdown_event) +{ + shutdown_event_data* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "shutdown_event> Null pointer" ); + return 0; + } + + int * pSec = & (info->seconds); + + if (*pSec < 0) + { + sys_log(0, "shutdown_event sec %d", *pSec); + + if (--*pSec == -10) + { + const DESC_MANAGER::DESC_SET & c_set_desc = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_set_desc.begin(), c_set_desc.end(), DisconnectFunc()); + return passes_per_sec; + } + else if (*pSec < -10) + return 0; + + return passes_per_sec; + } + else if (*pSec == 0) + { + const DESC_MANAGER::DESC_SET & c_set_desc = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_set_desc.begin(), c_set_desc.end(), SendDisconnectFunc()); + g_bNoMoreClient = true; + --*pSec; + return passes_per_sec; + } + else + { + char buf[64]; + snprintf(buf, sizeof(buf), LC_TEXT("˴ٿ %d ҽϴ."), *pSec); + SendNotice(buf); + + --*pSec; + return passes_per_sec; + } +} + +void Shutdown(int iSec) +{ + if (g_bNoMoreClient) + { + thecore_shutdown(); + return; + } + + CWarMapManager::instance().OnShutdown(); + + char buf[64]; + snprintf(buf, sizeof(buf), LC_TEXT("%d ˴ٿ ˴ϴ."), iSec); + + SendNotice(buf); + + shutdown_event_data* info = AllocEventInfo(); + info->seconds = iSec; + + event_create(shutdown_event, info, 1); +} + +ACMD(do_shutdown) +{ + sys_err("Accept shutdown command from %s.", (ch) ? ch->GetName() : "NONAME"); + + TPacketGGShutdown p; + p.bHeader = HEADER_GG_SHUTDOWN; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGShutdown)); + + Shutdown(10); +} + +EVENTFUNC(timed_event) +{ + TimedEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "timed_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + LPDESC d = ch->GetDesc(); + + if (info->left_second <= 0) + { + ch->m_pkTimedEvent = NULL; + + switch (info->subcmd) + { + case SCMD_LOGOUT: + case SCMD_QUIT: + case SCMD_PHASE_SELECT: + { + TPacketNeedLoginLogInfo acc_info; + acc_info.dwPlayerID = ch->GetDesc()->GetAccountTable().id; + + db_clientdesc->DBPacket( HEADER_GD_VALID_LOGOUT, 0, &acc_info, sizeof(acc_info) ); + + LogManager::instance().DetailLoginLog( false, ch ); + } + break; + } + + switch (info->subcmd) + { + case SCMD_LOGOUT: + if (d) + d->SetPhase(PHASE_CLOSE); + break; + + case SCMD_QUIT: + ch->ChatPacket(CHAT_TYPE_COMMAND, "quit"); + if (d) // @fixme197 + d->DelayedDisconnect(1); + break; + + case SCMD_PHASE_SELECT: + ch->Disconnect("timed_event - SCMD_PHASE_SELECT"); + if (d) + d->SetPhase(PHASE_SELECT); + break; + } + + return 0; + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ҽϴ."), info->left_second); + --info->left_second; + } + + return PASSES_PER_SEC(1); +} + +ACMD(do_cmd) +{ + if (ch->m_pkTimedEvent) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿϴ.")); + event_cancel(&ch->m_pkTimedEvent); + return; + } + + switch (subcmd) + { + case SCMD_LOGOUT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("α ȭ ϴ. ø ٸ.")); + break; + + case SCMD_QUIT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. ø ٸ.")); + break; + + case SCMD_PHASE_SELECT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ij͸ ȯ մϴ. ø ٸ.")); + break; + } + + int nExitLimitTime = 10; + + if (ch->IsHack(false, true, nExitLimitTime) && + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex()) && + (!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG)) + { + return; + } + + switch (subcmd) + { + case SCMD_LOGOUT: + case SCMD_QUIT: + case SCMD_PHASE_SELECT: + { + TimedEventInfo* info = AllocEventInfo(); + + { + if (ch->IsPosition(POS_FIGHTING)) + info->left_second = 10; + else + info->left_second = 3; + } + + info->ch = ch; + info->subcmd = subcmd; + strlcpy(info->szReason, argument, sizeof(info->szReason)); + + ch->m_pkTimedEvent = event_create(timed_event, info, 1); + } + break; + } +} + +ACMD(do_mount) +{ +} + +ACMD(do_fishing) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + ch->SetRotation(atof(arg1)); + ch->fishing(); +} + +ACMD(do_console) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "ConsoleEnable"); +} + +ACMD(do_restart) +{ + if (false == ch->IsDead()) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + ch->StartRecoveryEvent(); + return; + } + + if (NULL == ch->m_pkDeadEvent) + return; + + int iTimeToDead = (event_time(ch->m_pkDeadEvent) / passes_per_sec); + + if (subcmd != SCMD_RESTART_TOWN && (!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG)) + { + if (!test_server) + { + if (ch->IsHack()) + { + if (false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - (180 - g_nPortalLimitTime)); + return; + } + } +#define eFRS_HERESEC 170 + if (iTimeToDead > eFRS_HERESEC) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - eFRS_HERESEC); + return; + } + } + } + + //PREVENT_HACK + + if (subcmd == SCMD_RESTART_TOWN) + { + if (ch->IsHack()) + { + if ((!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG) || + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - (180 - g_nPortalLimitTime)); + return; + } + } + +#define eFRS_TOWNSEC 173 + if (iTimeToDead > eFRS_TOWNSEC) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - eFRS_TOWNSEC); + return; + } + } + //END_PREVENT_HACK + + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + + ch->GetDesc()->SetPhase(PHASE_GAME); + ch->SetPosition(POS_STANDING); + ch->StartRecoveryEvent(); + + //FORKED_LOAD + + if (1 == quest::CQuestManager::instance().GetEventFlag("threeway_war")) + { + if (subcmd == SCMD_RESTART_TOWN || subcmd == SCMD_RESTART_HERE) + { + if (true == CThreeWayWar::instance().IsThreeWayWarMapIndex(ch->GetMapIndex()) && + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + + ch->ReviveInvisible(5); + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + + return; + } + + if (true == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + if (CThreeWayWar::instance().GetReviveTokenForPlayer(ch->GetPlayerID()) <= 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ȱ ȸ Ҿϴ! ̵մϴ!")); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + else + { + ch->Show(ch->GetMapIndex(), GetSungziStartX(ch->GetEmpire()), GetSungziStartY(ch->GetEmpire())); + } + + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + + return; + } + } + } + //END_FORKED_LOAD + + if (ch->GetDungeon()) + ch->GetDungeon()->UseRevive(ch); + + if (ch->GetWarMap() && !ch->IsObserverMode()) + { + CWarMap * pMap = ch->GetWarMap(); + DWORD dwGuildOpponent = pMap ? pMap->GetGuildOpponent(ch) : 0; + + if (dwGuildOpponent) + { + switch (subcmd) + { + case SCMD_RESTART_TOWN: + sys_log(0, "do_restart: restart town"); + PIXEL_POSITION pos; + + if (CWarMapManager::instance().GetStartPosition(ch->GetMapIndex(), ch->GetGuild()->GetID() < dwGuildOpponent ? 0 : 1, pos)) + ch->Show(ch->GetMapIndex(), pos.x, pos.y); + else + ch->ExitToSavedLocation(); + + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + break; + + case SCMD_RESTART_HERE: + sys_log(0, "do_restart: restart here"); + ch->RestartAtSamePos(); + //ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY()); + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + break; + } + + return; + } + } + switch (subcmd) + { + case SCMD_RESTART_TOWN: + sys_log(0, "do_restart: restart town"); + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + ch->WarpSet(pos.x, pos.y); + else + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + ch->PointChange(POINT_HP, 50 - ch->GetHP()); + ch->DeathPenalty(1); + break; + + case SCMD_RESTART_HERE: + sys_log(0, "do_restart: restart here"); + ch->RestartAtSamePos(); + //ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY()); + ch->PointChange(POINT_HP, 50 - ch->GetHP()); + ch->DeathPenalty(0); + ch->ReviveInvisible(5); + break; + } +} + +#define MAX_STAT g_iStatusPointSetMaxValue + +ACMD(do_stat_reset) +{ + ch->PointChange(POINT_STAT_RESET_COUNT, 12 - ch->GetPoint(POINT_STAT_RESET_COUNT)); +} + +ACMD(do_stat_minus) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (ch->IsPolymorphed()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (ch->GetPoint(POINT_STAT_RESET_COUNT) <= 0) + return; + + if (!strcmp(arg1, "st")) + { + if (ch->GetRealPoint(POINT_ST) <= JobInitialPoints[ch->GetJob()].st) + return; + + ch->SetRealPoint(POINT_ST, ch->GetRealPoint(POINT_ST) - 1); + ch->SetPoint(POINT_ST, ch->GetPoint(POINT_ST) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_ST, 0); + } + else if (!strcmp(arg1, "dx")) + { + if (ch->GetRealPoint(POINT_DX) <= JobInitialPoints[ch->GetJob()].dx) + return; + + ch->SetRealPoint(POINT_DX, ch->GetRealPoint(POINT_DX) - 1); + ch->SetPoint(POINT_DX, ch->GetPoint(POINT_DX) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_DX, 0); + } + else if (!strcmp(arg1, "ht")) + { + if (ch->GetRealPoint(POINT_HT) <= JobInitialPoints[ch->GetJob()].ht) + return; + + ch->SetRealPoint(POINT_HT, ch->GetRealPoint(POINT_HT) - 1); + ch->SetPoint(POINT_HT, ch->GetPoint(POINT_HT) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_HT, 0); + ch->PointChange(POINT_MAX_HP, 0); + } + else if (!strcmp(arg1, "iq")) + { + if (ch->GetRealPoint(POINT_IQ) <= JobInitialPoints[ch->GetJob()].iq) + return; + + ch->SetRealPoint(POINT_IQ, ch->GetRealPoint(POINT_IQ) - 1); + ch->SetPoint(POINT_IQ, ch->GetPoint(POINT_IQ) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_IQ, 0); + ch->PointChange(POINT_MAX_SP, 0); + } + else + return; + + ch->PointChange(POINT_STAT, +1); + ch->PointChange(POINT_STAT_RESET_COUNT, -1); + ch->ComputePoints(); +} + +ACMD(do_stat) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (ch->IsPolymorphed()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (ch->GetPoint(POINT_STAT) <= 0) + return; + + BYTE idx = 0; + + if (!strcmp(arg1, "st")) + idx = POINT_ST; + else if (!strcmp(arg1, "dx")) + idx = POINT_DX; + else if (!strcmp(arg1, "ht")) + idx = POINT_HT; + else if (!strcmp(arg1, "iq")) + idx = POINT_IQ; + else + return; + + // ch->ChatPacket(CHAT_TYPE_INFO, "%s GRP(%d) idx(%u), MAX_STAT(%d), expr(%d)", __FUNCTION__, ch->GetRealPoint(idx), idx, MAX_STAT, ch->GetRealPoint(idx) >= MAX_STAT); + if (ch->GetRealPoint(idx) >= MAX_STAT) + return; + + ch->SetRealPoint(idx, ch->GetRealPoint(idx) + 1); + ch->SetPoint(idx, ch->GetPoint(idx) + 1); + ch->ComputePoints(); + ch->PointChange(idx, 0); + + if (idx == POINT_IQ) + { + ch->PointChange(POINT_MAX_HP, 0); + } + else if (idx == POINT_HT) + { + ch->PointChange(POINT_MAX_SP, 0); + } + + ch->PointChange(POINT_STAT, -1); + ch->ComputePoints(); +} + +ACMD(do_pvp) +{ + if (ch->GetArena() != NULL || CArenaManager::instance().IsArenaMap(ch->GetMapIndex()) == true) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(vid); + + if (!pkVictim) + return; + + if (pkVictim->IsNPC()) + return; + + if (pkVictim->GetArena() != NULL) + { + pkVictim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ.")); + return; + } + + CPVPManager::instance().Insert(ch, pkVictim); +} + +ACMD(do_guildskillup) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (!ch->GetGuild()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 忡 ʽϴ.")); + return; + } + + CGuild* g = ch->GetGuild(); + TGuildMember* gm = g->GetMember(ch->GetPlayerID()); + if (gm->grade == GUILD_LEADER_GRADE) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + g->SkillLevelUp(vnum); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ų ϴ.")); + } +} + +ACMD(do_skillup) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vnum = 0; + str_to_number(vnum, arg1); + + if (true == ch->CanUseSkill(vnum)) + { + ch->SkillLevelUp(vnum); + } + else + { + switch(vnum) + { + case SKILL_HORSE_WILDATTACK: + case SKILL_HORSE_CHARGE: + case SKILL_HORSE_ESCAPE: + case SKILL_HORSE_WILDATTACK_RANGE: + + case SKILL_7_A_ANTI_TANHWAN: + case SKILL_7_B_ANTI_AMSEOP: + case SKILL_7_C_ANTI_SWAERYUNG: + case SKILL_7_D_ANTI_YONGBI: + + case SKILL_8_A_ANTI_GIGONGCHAM: + case SKILL_8_B_ANTI_YEONSA: + case SKILL_8_C_ANTI_MAHWAN: + case SKILL_8_D_ANTI_BYEURAK: + + case SKILL_ADD_HP: + case SKILL_RESIST_PENETRATE: + ch->SkillLevelUp(vnum); + break; + } + } +} + +// +// +ACMD(do_safebox_close) +{ + ch->CloseSafebox(); +} + +// +// +ACMD(do_safebox_password) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + ch->ReqSafeboxLoad(arg1); +} + +ACMD(do_safebox_change_password) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || strlen(arg1)>6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + if (!*arg2 || strlen(arg2)>6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + TSafeboxChangePasswordPacket p; + + p.dwID = ch->GetDesc()->GetAccountTable().id; + strlcpy(p.szOldPassword, arg1, sizeof(p.szOldPassword)); + strlcpy(p.szNewPassword, arg2, sizeof(p.szNewPassword)); + + db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_CHANGE_PASSWORD, ch->GetDesc()->GetHandle(), &p, sizeof(p)); +} + +ACMD(do_mall_password) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1 || strlen(arg1) > 6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + int iPulse = thecore_pulse(); + + if (ch->GetMall()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â ̹ ֽϴ.")); + return; + } + + if (iPulse - ch->GetMallLoadTime() < passes_per_sec * 10) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â 10 ȿ ϴ.")); + return; + } + + ch->SetMallLoadTime(iPulse); + + TSafeboxLoadPacket p; + p.dwID = ch->GetDesc()->GetAccountTable().id; + strlcpy(p.szLogin, ch->GetDesc()->GetAccountTable().login, sizeof(p.szLogin)); + strlcpy(p.szPassword, arg1, sizeof(p.szPassword)); + + db_clientdesc->DBPacket(HEADER_GD_MALL_LOAD, ch->GetDesc()->GetHandle(), &p, sizeof(p)); +} + +ACMD(do_mall_close) +{ + if (ch->GetMall()) + { + ch->SetMallLoadTime(thecore_pulse()); + ch->CloseMall(); + ch->Save(); + } +} + +ACMD(do_ungroup) +{ + if (!ch->GetParty()) + return; + + if (!CPartyManager::instance().IsEnablePCParty()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return; + } + + if (ch->GetDungeon()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ϴ.")); + return; + } + + LPPARTY pParty = ch->GetParty(); + + if (pParty->GetMemberCount() == 2) + { + // party disband + CPartyManager::instance().DeleteParty(pParty); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ̽ϴ.")); + //pParty->SendPartyRemoveOneToAll(ch); + pParty->Quit(ch->GetPlayerID()); + //pParty->SendPartyRemoveAllToOne(ch); + } +} + +ACMD(do_close_shop) +{ + if (ch->GetMyShop()) + { + ch->CloseMyShop(); + return; + } +} + +ACMD(do_set_walk_mode) +{ + ch->SetNowWalking(true); + ch->SetWalking(true); +} + +ACMD(do_set_run_mode) +{ + ch->SetNowWalking(false); + ch->SetWalking(false); +} + +ACMD(do_war) +{ + CGuild * g = ch->GetGuild(); + + if (!g) + return; + + if (g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ ٸ £ Դϴ.")); + return; + } + + char arg1[256], arg2[256]; + DWORD type = GUILD_WAR_TYPE_FIELD; //fixme102 base int modded uint + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + return; + + if (*arg2) + { + str_to_number(type, arg2); + + if (type >= GUILD_WAR_TYPE_MAX_NUM) + type = GUILD_WAR_TYPE_FIELD; + } + + DWORD gm_pid = g->GetMasterPID(); + + if (gm_pid != ch->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϴ.")); + return; + } + + CGuild * opp_g = CGuildManager::instance().FindGuildByName(arg1); + + if (!opp_g) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ׷ 尡 ϴ.")); + return; + } + + switch (g->GetGuildWarState(opp_g->GetID())) + { + case GUILD_WAR_NONE: + { + if (opp_g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 尡 ̹ Դϴ.")); + return; + } + + int iWarPrice = KOR_aGuildWarInfo[type].iWarPrice; + + if (g->GetGuildMoney() < iWarPrice) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + + if (opp_g->GetGuildMoney() < iWarPrice) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + } + break; + + case GUILD_WAR_SEND_DECLARE: + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Դϴ.")); + return; + } + break; + + case GUILD_WAR_RECV_DECLARE: + { + if (opp_g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 尡 ̹ Դϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + } + } + break; + + case GUILD_WAR_RESERVE: + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ Դϴ.")); + return; + } + break; + + case GUILD_WAR_END: + return; + + default: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ Դϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + } + + if (!g->CanStartWar(type)) + { + if (g->GetLadderPoint() == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ڶ ϴ.")); + sys_log(0, "GuildWar.StartError.NEED_LADDER_POINT"); + } + else if (g->GetMemberCount() < GUILD_WAR_MIN_MEMBER_COUNT) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϱ ؼ ּ %d ־ մϴ."), GUILD_WAR_MIN_MEMBER_COUNT); + sys_log(0, "GuildWar.StartError.NEED_MINIMUM_MEMBER[%d]", GUILD_WAR_MIN_MEMBER_COUNT); + } + else + { + sys_log(0, "GuildWar.StartError.UNKNOWN_ERROR"); + } + return; + } + + if (!opp_g->CanStartWar(GUILD_WAR_TYPE_FIELD)) + { + if (opp_g->GetLadderPoint() == 0) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ڶ ϴ.")); + else if (opp_g->GetMemberCount() < GUILD_WAR_MIN_MEMBER_COUNT) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + + do + { + if (g->GetMasterCharacter() != NULL) + break; + + CCI *pCCI = P2P_MANAGER::instance().FindByPID(g->GetMasterPID()); + + if (pCCI != NULL) + break; + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ƴմϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + + } while (false); + + do + { + if (opp_g->GetMasterCharacter() != NULL) + break; + + CCI *pCCI = P2P_MANAGER::instance().FindByPID(opp_g->GetMasterPID()); + + if (pCCI != NULL) + break; + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ƴմϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + + } while (false); + + g->RequestDeclareWar(opp_g->GetID(), type); +} + +ACMD(do_nowar) +{ + CGuild* g = ch->GetGuild(); + if (!g) + return; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD gm_pid = g->GetMasterPID(); + + if (gm_pid != ch->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϴ.")); + return; + } + + CGuild* opp_g = CGuildManager::instance().FindGuildByName(arg1); + + if (!opp_g) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ׷ 尡 ϴ.")); + return; + } + + g->RequestRefuseWar(opp_g->GetID()); +} + +ACMD(do_detaillog) +{ + ch->DetailLog(); +} + +ACMD(do_monsterlog) +{ + ch->ToggleMonsterLog(); +} + +ACMD(do_pkmode) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + BYTE mode = 0; + str_to_number(mode, arg1); + + if (mode == PK_MODE_PROTECT) + return; + + if (ch->GetLevel() < PK_PROTECT_LEVEL && mode != 0) + return; + + ch->SetPKMode(mode); +} + +ACMD(do_messenger_auth) +{ + if (ch->GetArena()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + char answer = LOWER(*arg1); + // @fixme130 AuthToAdd void -> bool + bool bIsDenied = answer != 'y'; + bool bIsAdded = MessengerManager::instance().AuthToAdd(ch->GetName(), arg2, bIsDenied); // DENY + if (bIsAdded && bIsDenied) + { + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg2); + + if (tch) + tch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ģ ź ߽ϴ."), ch->GetName()); + } +} + +ACMD(do_setblockmode) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + BYTE flag = 0; + str_to_number(flag, arg1); + ch->SetBlockMode(flag); + } +} + +ACMD(do_unmount) +{ + if (true == ch->UnEquipSpecialRideUniqueItem()) + { + ch->RemoveAffect(AFFECT_MOUNT); + ch->RemoveAffect(AFFECT_MOUNT_BONUS); + + if (ch->IsHorseRiding()) + { + ch->StopRiding(); + } + } + else + { + ch->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("κ丮 ϴ.")); + } +} + +ACMD(do_observer_exit) +{ + if (ch->IsObserverMode()) + { + if (ch->GetWarMap()) + ch->SetWarMap(NULL); + + if (ch->GetArena() != NULL || ch->GetArenaObserverMode() == true) + { + ch->SetArenaObserverMode(false); + + if (ch->GetArena() != NULL) + ch->GetArena()->RemoveObserver(ch->GetPlayerID()); + + ch->SetArena(NULL); + ch->WarpSet(ARENA_RETURN_POINT_X(ch->GetEmpire()), ARENA_RETURN_POINT_Y(ch->GetEmpire())); + } + else + { + ch->ExitToSavedLocation(); + } + ch->SetObserverMode(false); + } +} + +ACMD(do_view_equip) +{ + if (ch->GetGMLevel() <= GM_PLAYER) + return; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (!tch) + return; + + if (!tch->IsPC()) + return; + + tch->SendEquipment(ch); + } +} + +ACMD(do_party_request) +{ + if (ch->GetArena()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + if (ch->GetParty()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ƽ Ƿ Խû ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + if (!ch->RequestToParty(tch)) + ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +ACMD(do_party_request_accept) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + ch->AcceptToParty(tch); +} + +ACMD(do_party_request_deny) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + ch->DenyToParty(tch); +} + +ACMD(do_monarch_warpto) +{ + if (!CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + return; + } + + if (!ch->IsMCOK(CHARACTER::MI_WARP)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_WARP)); + return; + } + + const int WarpPrice = 10000; + + if (!CMonarch::instance().IsMoneyOk(WarpPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, WarpPrice); + return; + } + + int x = 0, y = 0; + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(": warpto ")); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bEmpire != ch->GetEmpire()) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("Ÿ Դ ̵Ҽ ϴ")); + return; + } + + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ش %d äο ֽϴ. ( ä %d)"), pkCCI->bChannel, g_bChannel); + return; + } + if (!IsMonarchWarpZone(pkCCI->lMapIndex)) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + + PIXEL_POSITION pos; + + if (!SECTREE_MANAGER::instance().GetCenterPositionOfMap(pkCCI->lMapIndex, pos)) + ch->ChatPacket(CHAT_TYPE_INFO, "Cannot find map (index %d)", pkCCI->lMapIndex); + else + { + //ch->ChatPacket(CHAT_TYPE_INFO, "You warp to (%d, %d)", pos.x, pos.y); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Է ̵մϴ"), arg1); + ch->WarpSet(pos.x, pos.y); + + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + ch->SetMC(CHARACTER::MI_WARP); + } + } + else if (NULL == CHARACTER_MANAGER::instance().FindPC(arg1)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no one by that name"); + } + + return; + } + else + { + if (tch->GetEmpire() != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ÿ Դ ̵Ҽ ϴ")); + return; + } + if (!IsMonarchWarpZone(tch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + x = tch->GetX(); + y = tch->GetY(); + } + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Է ̵մϴ"), arg1); + ch->WarpSet(x, y); + ch->Stop(); + + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + ch->SetMC(CHARACTER::MI_WARP); +} + +ACMD(do_monarch_transfer) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(": transfer ")); + return; + } + + if (!CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + return; + } + + if (!ch->IsMCOK(CHARACTER::MI_TRANSFER)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_TRANSFER)); + return; + } + + const int WarpPrice = 10000; + + if (!CMonarch::instance().IsMoneyOk(WarpPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, WarpPrice); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bEmpire != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ȯ ϴ.")); + return; + } + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s %d äο Դϴ. ( ä: %d)"), arg1, pkCCI->bChannel, g_bChannel); + return; + } + if (!IsMonarchWarpZone(pkCCI->lMapIndex)) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + if (!IsMonarchWarpZone(ch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ȯ ϴ.")); + return; + } + + TPacketGGTransfer pgg; + + pgg.bHeader = HEADER_GG_TRANSFER; + strlcpy(pgg.szName, arg1, sizeof(pgg.szName)); + pgg.lX = ch->GetX(); + pgg.lY = ch->GetY(); + + P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGTransfer)); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȯϿϴ."), arg1); + + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + ch->SetMC(CHARACTER::MI_TRANSFER); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ԷϽ ̸ ڰ ϴ.")); + } + + return; + } + + if (ch == tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڽ ȯ ϴ.")); + return; + } + + if (tch->GetEmpire() != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ȯ ϴ.")); + return; + } + if (!IsMonarchWarpZone(tch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + if (!IsMonarchWarpZone(ch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ȯ ϴ.")); + return; + } + + //tch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ()); + tch->WarpSet(ch->GetX(), ch->GetY(), ch->GetMapIndex()); + + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + ch->SetMC(CHARACTER::MI_TRANSFER); +} + +ACMD(do_monarch_info) +{ + if (CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ")); + TMonarchInfo * p = CMonarch::instance().GetMonarch(); + for (int n = 1; n < 4; ++n) + { + if (n == ch->GetEmpire()) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s ݾ %lld "), EMPIRE_NAME(n), p->name[n], p->money[n]); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s "), EMPIRE_NAME(n), p->name[n]); + + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ")); + TMonarchInfo * p = CMonarch::instance().GetMonarch(); + for (int n = 1; n < 4; ++n) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s "), EMPIRE_NAME(n), p->name[n]); + + } + } +} + +ACMD(do_elect) +{ + db_clientdesc->DBPacketHeader(HEADER_GD_COME_TO_VOTE, ch->GetDesc()->GetHandle(), 0); +} + +// LUA_ADD_GOTO_INFO +struct GotoInfo +{ + std::string st_name; + + BYTE empire; + int mapIndex; + DWORD x, y; + + GotoInfo() + { + st_name = ""; + empire = 0; + mapIndex = 0; + + x = 0; + y = 0; + } + + GotoInfo(const GotoInfo& c_src) + { + __copy__(c_src); + } + + void operator = (const GotoInfo& c_src) + { + __copy__(c_src); + } + + void __copy__(const GotoInfo& c_src) + { + st_name = c_src.st_name; + empire = c_src.empire; + mapIndex = c_src.mapIndex; + + x = c_src.x; + y = c_src.y; + } +}; + +ACMD(do_monarch_tax) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: monarch_tax <1-50>"); + return; + } + + if (!ch->IsMonarch()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Ҽ ִ Դϴ")); + return; + } + + int tax = 0; + str_to_number(tax, arg1); + + if (tax < 1 || tax > 50) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("1-50 ġ ּ")); + + quest::CQuestManager::instance().SetEventFlag("trade_tax", tax); + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d % Ǿϴ")); + + char szMsg[1024]; + + snprintf(szMsg, sizeof(szMsg), " %d %% Ǿϴ", tax); + BroadcastNotice(szMsg); + + snprintf(szMsg, sizeof(szMsg), "δ ŷ ݾ %d %% Ե˴ϴ.", tax); + BroadcastNotice(szMsg); + + ch->SetMC(CHARACTER::MI_TAX); +} + +static const DWORD cs_dwMonarchMobVnums[] = +{ + 191, + 192, + 193, + 194, + 391, + 392, + 393, + 394, + 491, + 492, + 493, + 494, + 591, + 691, + 791, + 1304, + 1901, + 2091, + 2191, + 2206, + 0, +}; + +ACMD(do_monarch_mob) +{ + char arg1[256]; + LPCHARACTER tch; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!ch->IsMonarch()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Ҽ ִ Դϴ")); + return; + } + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mmob "); + return; + } + +#ifdef ENABLE_MONARCH_MOB_CMD_MAP_CHECK // @warme006 + BYTE pcEmpire = ch->GetEmpire(); + BYTE mapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(ch->GetMapIndex()); + if (mapEmpire != pcEmpire && mapEmpire != 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڱ 信 ִ Դϴ")); + return; + } +#endif + + const int SummonPrice = 5000000; + + if (!ch->IsMCOK(CHARACTER::MI_SUMMON)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_SUMMON)); + return; + } + + if (!CMonarch::instance().IsMoneyOk(SummonPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, SummonPrice); + return; + } + + const CMob * pkMob; + DWORD vnum = 0; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + DWORD count; + + for (count = 0; cs_dwMonarchMobVnums[count] != 0; ++count) + if (cs_dwMonarchMobVnums[count] == vnum) + break; + + if (0 == cs_dwMonarchMobVnums[count]) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȯҼ Դϴ. ȯ ʹ Ȩ ϼ")); + return; + } + + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE, + true); + + if (tch) + { + CMonarch::instance().SendtoDBDecMoney(SummonPrice, ch->GetEmpire(), ch); + + ch->SetMC(CHARACTER::MI_SUMMON); + } +} + +static const char* FN_point_string(int apply_number) +{ + switch (apply_number) + { + case POINT_MAX_HP: return LC_TEXT("ִ +%d"); + case POINT_MAX_SP: return LC_TEXT("ִ ŷ +%d"); + case POINT_HT: return LC_TEXT("ü +%d"); + case POINT_IQ: return LC_TEXT(" +%d"); + case POINT_ST: return LC_TEXT("ٷ +%d"); + case POINT_DX: return LC_TEXT("ø +%d"); + case POINT_ATT_SPEED: return LC_TEXT("ݼӵ +%d"); + case POINT_MOV_SPEED: return LC_TEXT("̵ӵ %d"); + case POINT_CASTING_SPEED: return LC_TEXT("Ÿ -%d"); + case POINT_HP_REGEN: return LC_TEXT(" ȸ +%d"); + case POINT_SP_REGEN: return LC_TEXT("ŷ ȸ +%d"); + case POINT_POISON_PCT: return LC_TEXT(" %d"); +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_BLEEDING_PCT: return LC_TEXT(" %d"); +#endif + case POINT_STUN_PCT: return LC_TEXT(" +%d"); + case POINT_SLOW_PCT: return LC_TEXT("ο +%d"); + case POINT_CRITICAL_PCT: return LC_TEXT("%d%% Ȯ ġŸ "); + case POINT_RESIST_CRITICAL: return LC_TEXT(" ġŸ Ȯ %d%% "); + case POINT_PENETRATE_PCT: return LC_TEXT("%d%% Ȯ "); + case POINT_RESIST_PENETRATE: return LC_TEXT(" Ȯ %d%% "); + case POINT_ATTBONUS_HUMAN: return LC_TEXT("ΰ Ÿġ +%d%%"); + case POINT_ATTBONUS_ANIMAL: return LC_TEXT(" Ÿġ +%d%%"); + case POINT_ATTBONUS_ORC: return LC_TEXT(" Ÿġ +%d%%"); + case POINT_ATTBONUS_MILGYO: return LC_TEXT("б Ÿġ +%d%%"); + case POINT_ATTBONUS_UNDEAD: return LC_TEXT("ü Ÿġ +%d%%"); + case POINT_ATTBONUS_DEVIL: return LC_TEXT("Ǹ Ÿġ +%d%%"); + case POINT_STEAL_HP: return LC_TEXT("Ÿġ %d%% "); + case POINT_STEAL_SP: return LC_TEXT("Ÿġ %d%% ŷ "); + case POINT_MANA_BURN_PCT: return LC_TEXT("%d%% Ȯ Ÿݽ ŷ Ҹ"); + case POINT_DAMAGE_SP_RECOVER: return LC_TEXT("%d%% Ȯ ؽ ŷ ȸ"); + case POINT_BLOCK: return LC_TEXT("Ÿݽ Ȯ %d%%"); + case POINT_DODGE: return LC_TEXT("Ȱ ȸ Ȯ %d%%"); + case POINT_RESIST_SWORD: return LC_TEXT("Ѽհ %d%%"); + case POINT_RESIST_TWOHAND: return LC_TEXT("հ %d%%"); + case POINT_RESIST_DAGGER: return LC_TEXT("μհ %d%%"); + case POINT_RESIST_BELL: return LC_TEXT(" %d%%"); + case POINT_RESIST_FAN: return LC_TEXT("ä %d%%"); + case POINT_RESIST_BOW: return LC_TEXT("Ȱ %d%%"); +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_RESIST_CLAW: return LC_TEXT("μհ %d%%"); +#endif + case POINT_RESIST_FIRE: return LC_TEXT("ȭ %d%%"); + case POINT_RESIST_ELEC: return LC_TEXT(" %d%%"); + case POINT_RESIST_MAGIC: return LC_TEXT(" %d%%"); +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + case POINT_RESIST_MAGIC_REDUCTION: return LC_TEXT(" %d%%"); +#endif + case POINT_RESIST_WIND: return LC_TEXT("ٶ %d%%"); + case POINT_RESIST_ICE: return LC_TEXT("ñ %d%%"); + case POINT_RESIST_EARTH: return LC_TEXT(" %d%%"); + case POINT_RESIST_DARK: return LC_TEXT(" %d%%"); + case POINT_REFLECT_MELEE: return LC_TEXT(" Ÿġ ݻ Ȯ : %d%%"); + case POINT_REFLECT_CURSE: return LC_TEXT(" ǵ Ȯ %d%%"); + case POINT_POISON_REDUCE: return LC_TEXT(" %d%%"); +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_BLEEDING_REDUCE: return LC_TEXT(" %d%%"); +#endif + case POINT_KILL_SP_RECOVER: return LC_TEXT("%d%% Ȯ ġ ŷ ȸ"); + case POINT_EXP_DOUBLE_BONUS: return LC_TEXT("%d%% Ȯ ġ ġ ߰ "); + case POINT_GOLD_DOUBLE_BONUS: return LC_TEXT("%d%% Ȯ ġ 2 "); + case POINT_ITEM_DROP_BONUS: return LC_TEXT("%d%% Ȯ ġ 2 "); + case POINT_POTION_BONUS: return LC_TEXT(" %d%% "); + case POINT_KILL_HP_RECOVERY: return LC_TEXT("%d%% Ȯ ġ ȸ"); + case POINT_ATT_GRADE_BONUS: return LC_TEXT("ݷ +%d"); + case POINT_DEF_GRADE_BONUS: return LC_TEXT(" +%d"); + case POINT_MAGIC_ATT_GRADE: return LC_TEXT(" ݷ +%d"); + case POINT_MAGIC_DEF_GRADE: return LC_TEXT(" +%d"); + case POINT_MAX_STAMINA: return LC_TEXT("ִ +%d"); + case POINT_ATTBONUS_WARRIOR: return LC_TEXT("翡 +%d%%"); + case POINT_ATTBONUS_ASSASSIN: return LC_TEXT("ڰ +%d%%"); + case POINT_ATTBONUS_SURA: return LC_TEXT("󿡰 +%d%%"); + case POINT_ATTBONUS_SHAMAN: return LC_TEXT("翡 +%d%%"); +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_ATTBONUS_WOLFMAN: return LC_TEXT("翡 +%d%%"); +#endif + case POINT_ATTBONUS_MONSTER: return LC_TEXT("Ϳ +%d%%"); + case POINT_MALL_ATTBONUS: return LC_TEXT("ݷ +%d%%"); + case POINT_MALL_DEFBONUS: return LC_TEXT(" +%d%%"); + case POINT_MALL_EXPBONUS: return LC_TEXT("ġ %d%%"); + case POINT_MALL_ITEMBONUS: return LC_TEXT(" %d"); // @fixme180 float to int + case POINT_MALL_GOLDBONUS: return LC_TEXT(" %d"); // @fixme180 float to int + case POINT_MAX_HP_PCT: return LC_TEXT("ִ +%d%%"); + case POINT_MAX_SP_PCT: return LC_TEXT("ִ ŷ +%d%%"); + case POINT_SKILL_DAMAGE_BONUS: return LC_TEXT("ų %d%%"); + case POINT_NORMAL_HIT_DAMAGE_BONUS: return LC_TEXT("Ÿ %d%%"); + case POINT_SKILL_DEFEND_BONUS: return LC_TEXT("ų %d%%"); + case POINT_NORMAL_HIT_DEFEND_BONUS: return LC_TEXT("Ÿ %d%%"); + case POINT_RESIST_WARRIOR: return LC_TEXT("ݿ %d%% "); + case POINT_RESIST_ASSASSIN: return LC_TEXT("ڰݿ %d%% "); + case POINT_RESIST_SURA: return LC_TEXT("ݿ %d%% "); + case POINT_RESIST_SHAMAN: return LC_TEXT("ݿ %d%% "); +#ifdef ENABLE_WOLFMAN_CHARACTER + case POINT_RESIST_WOLFMAN: return LC_TEXT("ݿ %d%% "); +#endif + default: return "UNK_ID %d%%"; // @fixme180 + } +} + +static bool FN_hair_affect_string(LPCHARACTER ch, char *buf, size_t bufsiz) +{ + if (NULL == ch || NULL == buf) + return false; + + CAffect* aff = NULL; + time_t expire = 0; + struct tm ltm; + int year, mon, day; + int offset = 0; + + aff = ch->FindAffect(AFFECT_HAIR); + + if (NULL == aff) + return false; + + expire = ch->GetQuestFlag("hair.limit_time"); + + if (expire < get_global_time()) + return false; + + // set apply string + offset = snprintf(buf, bufsiz, FN_point_string(aff->bApplyOn), aff->lApplyValue); + + if (offset < 0 || offset >= (int) bufsiz) + offset = bufsiz - 1; + + localtime_r(&expire, <m); + + year = ltm.tm_year + 1900; + mon = ltm.tm_mon + 1; + day = ltm.tm_mday; + + snprintf(buf + offset, bufsiz - offset, LC_TEXT(" ( : %d %d %d)"), year, mon, day); + + return true; +} + +ACMD(do_costume) +{ + char buf[1024]; // @warme015 + const size_t bufferSize = sizeof(buf); + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + CItem* pBody = ch->GetWear(WEAR_COSTUME_BODY); + CItem* pHair = ch->GetWear(WEAR_COSTUME_HAIR); +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + CItem* pMount = ch->GetWear(WEAR_COSTUME_MOUNT); +#endif +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + CItem* pAcce = ch->GetWear(WEAR_COSTUME_ACCE); +#endif +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + CItem* pWeapon = ch->GetWear(WEAR_COSTUME_WEAPON); +#endif + + ch->ChatPacket(CHAT_TYPE_INFO, "COSTUME status:"); + + if (pHair) + { + const char* itemName = pHair->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " HAIR : %s", itemName); + + for (int i = 0; i < pHair->GetAttributeCount(); ++i) + { + const TPlayerItemAttribute& attr = pHair->GetAttribute(i); + if (0 < attr.bType) + { + snprintf(buf, bufferSize, FN_point_string(attr.bType), attr.sValue); + ch->ChatPacket(CHAT_TYPE_INFO, " %s", buf); + } + } + + if (pHair->IsEquipped() && arg1[0] == 'h') + ch->UnequipItem(pHair); + } + + if (pBody) + { + const char* itemName = pBody->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " BODY : %s", itemName); + + if (pBody->IsEquipped() && arg1[0] == 'b') + ch->UnequipItem(pBody); + } + +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + if (pMount) + { + const char* itemName = pMount->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " MOUNT : %s", itemName); + + if (pMount->IsEquipped() && arg1[0] == 'm') + ch->UnequipItem(pMount); + } +#endif + +#ifdef ENABLE_ACCE_COSTUME_SYSTEM + if (pAcce) + { + const char* itemName = pAcce->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " ACCE : %s", itemName); + + if (pAcce->IsEquipped() && arg1[0] == 'a') + ch->UnequipItem(pAcce); + } +#endif + +#ifdef ENABLE_WEAPON_COSTUME_SYSTEM + if (pWeapon) + { + const char* itemName = pWeapon->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " WEAPON : %s", itemName); + + if (pWeapon->IsEquipped() && arg1[0] == 'w') + ch->UnequipItem(pWeapon); + } +#endif +} + +ACMD(do_hair) +{ + char buf[256]; + + if (false == FN_hair_affect_string(ch, buf, sizeof(buf))) + return; + + ch->ChatPacket(CHAT_TYPE_INFO, buf); +} + +ACMD(do_inventory) +{ + int index = 0; + int count = 1; + + char arg1[256]; + char arg2[256]; + + LPITEM item; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: inventory "); + return; + } + + if (!*arg2) + { + index = 0; + str_to_number(count, arg1); + } + else + { + str_to_number(index, arg1); index = MIN(index, INVENTORY_MAX_NUM); + str_to_number(count, arg2); count = MIN(count, INVENTORY_MAX_NUM); + } + + for (int i = 0; i < count; ++i) + { + if (index >= INVENTORY_MAX_NUM) + break; + + item = ch->GetInventoryItem(index); + + ch->ChatPacket(CHAT_TYPE_INFO, "inventory [%d] = %s", + index, item ? item->GetName() : ""); + ++index; + } +} + +//gift notify quest command +ACMD(do_gift) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "gift"); +} + +ACMD(do_cube) +{ + if (!ch->CanDoCube()) + return; + + sys_log(1, "CUBE COMMAND <%s>: %s", ch->GetName(), argument); + int cube_index = 0, inven_index = 0; + const char *line; + + char arg1[256], arg2[256], arg3[256]; + + line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(line, arg3, sizeof(arg3)); + + if (0 == arg1[0]) + { + // print usage + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: cube open"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube close"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube add "); + ch->ChatPacket(CHAT_TYPE_INFO, " cube delete "); + ch->ChatPacket(CHAT_TYPE_INFO, " cube list"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube cancel"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube make [all]"); + return; + } + + const std::string& strArg1 = std::string(arg1); + + // r_info (request information) + + // (Server -> Client) /cube r_list npcVNUM resultCOUNT 123,1/125,1/128,1/130,5 + + // (Server -> Client) /cube m_info startIndex count 125,1|126,2|127,2|123,5&555,5&555,4/120000@125,1|126,2|127,2|123,5&555,5&555,4/120000 + + if (strArg1 == "r_info") + { + if (0 == arg2[0]) + Cube_request_result_list(ch); + else + { + if (isdigit(*arg2)) + { + int listIndex = 0, requestCount = 1; + str_to_number(listIndex, arg2); + + if (0 != arg3[0] && isdigit(*arg3)) + str_to_number(requestCount, arg3); + + Cube_request_material_info(ch, listIndex, requestCount); + } + } + + return; + } + + switch (LOWER(arg1[0])) + { + case 'o': // open + Cube_open(ch); + break; + + case 'c': // close + Cube_close(ch); + break; + + case 'l': // list + Cube_show_list(ch); + break; + + case 'a': // add cue_index inven_index + { + if (0 == arg2[0] || !isdigit(*arg2) || + 0 == arg3[0] || !isdigit(*arg3)) + return; + + str_to_number(cube_index, arg2); + str_to_number(inven_index, arg3); + Cube_add_item (ch, cube_index, inven_index); + } + break; + + case 'd': // delete + { + if (0 == arg2[0] || !isdigit(*arg2)) + return; + + str_to_number(cube_index, arg2); + Cube_delete_item (ch, cube_index); + } + break; + + case 'm': // make + if (0 != arg2[0]) + { + while (true == Cube_make(ch)) + sys_log(1, "cube make success"); + } + else + Cube_make(ch); + break; + + default: + return; + } +} + +ACMD(do_in_game_mall) +{ + if (LC_IsEurope() == true) + { + char country_code[3]; + + switch (LC_GetLocalType()) + { + case LC_GERMANY: country_code[0] = 'd'; country_code[1] = 'e'; country_code[2] = '\0'; break; + case LC_FRANCE: country_code[0] = 'f'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_ITALY: country_code[0] = 'i'; country_code[1] = 't'; country_code[2] = '\0'; break; + case LC_SPAIN: country_code[0] = 'e'; country_code[1] = 's'; country_code[2] = '\0'; break; + case LC_UK: country_code[0] = 'e'; country_code[1] = 'n'; country_code[2] = '\0'; break; + case LC_TURKEY: country_code[0] = 't'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_POLAND: country_code[0] = 'p'; country_code[1] = 'l'; country_code[2] = '\0'; break; + case LC_PORTUGAL: country_code[0] = 'p'; country_code[1] = 't'; country_code[2] = '\0'; break; + case LC_GREEK: country_code[0] = 'g'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_RUSSIA: country_code[0] = 'r'; country_code[1] = 'u'; country_code[2] = '\0'; break; + case LC_DENMARK: country_code[0] = 'd'; country_code[1] = 'k'; country_code[2] = '\0'; break; + case LC_BULGARIA: country_code[0] = 'b'; country_code[1] = 'g'; country_code[2] = '\0'; break; + case LC_CROATIA: country_code[0] = 'h'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_MEXICO: country_code[0] = 'm'; country_code[1] = 'x'; country_code[2] = '\0'; break; + case LC_ARABIA: country_code[0] = 'a'; country_code[1] = 'e'; country_code[2] = '\0'; break; + case LC_CZECH: country_code[0] = 'c'; country_code[1] = 'z'; country_code[2] = '\0'; break; + case LC_ROMANIA: country_code[0] = 'r'; country_code[1] = 'o'; country_code[2] = '\0'; break; + case LC_HUNGARY: country_code[0] = 'h'; country_code[1] = 'u'; country_code[2] = '\0'; break; + case LC_NETHERLANDS: country_code[0] = 'n'; country_code[1] = 'l'; country_code[2] = '\0'; break; + case LC_USA: country_code[0] = 'u'; country_code[1] = 's'; country_code[2] = '\0'; break; + case LC_CANADA: country_code[0] = 'c'; country_code[1] = 'a'; country_code[2] = '\0'; break; + default: + if (test_server == true) + { + country_code[0] = 'd'; country_code[1] = 'e'; country_code[2] = '\0'; + } + break; + } + + char buf[512+1]; + char sas[33]; + MD5_CTX ctx; + const char sas_key[] = "GF9001"; + + snprintf(buf, sizeof(buf), "%u%u%s", ch->GetPlayerID(), ch->GetAID(), sas_key); + + MD5Init(&ctx); + MD5Update(&ctx, (const unsigned char *) buf, strlen(buf)); +#ifdef __FreeBSD__ + MD5End(&ctx, sas); +#else + static const char hex[] = "0123456789abcdef"; + unsigned char digest[16]; + MD5Final(digest, &ctx); + int i; + for (i = 0; i < 16; ++i) { + sas[i+i] = hex[digest[i] >> 4]; + sas[i+i+1] = hex[digest[i] & 0x0f]; + } + sas[i+i] = '\0'; +#endif + + snprintf(buf, sizeof(buf), "mall http://%s/ishop?pid=%u&c=%s&sid=%d&sas=%s", + g_strWebMallURL.c_str(), ch->GetPlayerID(), country_code, g_server_id, sas); + + ch->ChatPacket(CHAT_TYPE_COMMAND, buf); + } +} + +ACMD(do_dice) +{ + char arg1[256], arg2[256]; + int start = 1, end = 100; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (*arg1 && *arg2) + { + start = atoi(arg1); + end = atoi(arg2); + } + else if (*arg1 && !*arg2) + { + start = 1; + end = atoi(arg1); + } + + end = MAX(start, end); + start = MIN(start, end); + + int n = number(start, end); + +#ifdef ENABLE_DICE_SYSTEM + if (ch->GetParty()) + ch->GetParty()->ChatPacketToAllMember(CHAT_TYPE_DICE_INFO, LC_TEXT("%s ֻ %d Խϴ. (%d-%d)"), ch->GetName(), n, start, end); + else + ch->ChatPacket(CHAT_TYPE_DICE_INFO, LC_TEXT(" ֻ %d Խϴ. (%d-%d)"), n, start, end); +#else + if (ch->GetParty()) + ch->GetParty()->ChatPacketToAllMember(CHAT_TYPE_INFO, LC_TEXT("%s ֻ %d Խϴ. (%d-%d)"), ch->GetName(), n, start, end); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֻ %d Խϴ. (%d-%d)"), n, start, end); +#endif +} + +#if defined(ENABLE_CHEQUE_SYSTEM) && defined(ENABLE_WON_EXCHANGE_WINDOW) +ACMD(do_won_exchange) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + if (!*arg1 || !*arg2) + return; + + int won = 0; + str_to_number(won, arg2); + + ch->WonExchange(arg1, won); +} +#endif + +#ifdef ENABLE_NEWSTUFF +ACMD(do_click_safebox) +{ + if ((ch->GetGMLevel() <= GM_PLAYER) && (ch->GetDungeon() || ch->GetWarMap())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot open the safebox in dungeon or at war.")); + return; + } + + ch->SetSafeboxOpenPosition(); + ch->ChatPacket(CHAT_TYPE_COMMAND, "ShowMeSafeboxPassword"); +} +ACMD(do_force_logout) +{ + LPDESC pDesc=DESC_MANAGER::instance().FindByCharacterName(ch->GetName()); + if (!pDesc) + return; + pDesc->DelayedDisconnect(0); +} +#endif + +ACMD(do_click_mall) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "ShowMeMallPassword"); +} + +ACMD(do_ride) +{ + sys_log(1, "[DO_RIDE] start"); + if (ch->IsDead() || ch->IsStun()) + return; + + { + if (ch->IsHorseRiding()) + { + sys_log(1, "[DO_RIDE] stop riding"); + ch->StopRiding(); + return; + } + + if (ch->GetMountVnum()) + { + sys_log(1, "[DO_RIDE] unmount"); + do_unmount(ch, NULL, 0, 0); + return; + } + } + + { + if (ch->GetHorse() != NULL) + { + sys_log(1, "[DO_RIDE] start riding"); + ch->StartRiding(); + return; + } + + for (BYTE i=0; iGetInventoryItem(i); + if (NULL == item) + continue; + + if (item->IsRideItem()) + { + if ( + NULL==ch->GetWear(WEAR_UNIQUE1) + || NULL==ch->GetWear(WEAR_UNIQUE2) +#ifdef ENABLE_MOUNT_COSTUME_SYSTEM + || NULL==ch->GetWear(WEAR_COSTUME_MOUNT) +#endif + ) + { + sys_log(1, "[DO_RIDE] USE UNIQUE ITEM"); + //ch->EquipItem(item); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + } + + switch (item->GetVnum()) + { + case 71114: + case 71116: + case 71118: + case 71120: + sys_log(1, "[DO_RIDE] USE QUEST ITEM"); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + + if( (item->GetVnum() > 52000) && (item->GetVnum() < 52091) ) { + sys_log(1, "[DO_RIDE] USE QUEST ITEM"); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + } + } + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); +} + +#ifdef ENABLE_MOVE_CHANNEL +ACMD(DoChangeChannel) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + if (!*arg1) + return; + + WORD channel = 0; + str_to_number(channel, arg1); + if (!channel) + return; + + ch->ChangeChannel(channel); +} +#endif +//martysama0134's 623a0779c74cb7565145d45548376308 diff --git a/source-server/Srcs/Server/game/src/cmd_gm.cpp b/source-server/Srcs/Server/game/src/cmd_gm.cpp new file mode 100644 index 000000000..198a82c60 --- /dev/null +++ b/source-server/Srcs/Server/game/src/cmd_gm.cpp @@ -0,0 +1,4674 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "char.h" +#include "char_manager.h" +#include "item_manager.h" +#include "sectree_manager.h" +#include "mob_manager.h" +#include "packet.h" +#include "cmd.h" +#include "regen.h" +#include "guild.h" +#include "guild_manager.h" +#include "p2p.h" +#include "buffer_manager.h" +#include "fishing.h" +#include "mining.h" +#include "questmanager.h" +#include "vector.h" +#include "affect.h" +#include "db.h" +#include "priv_manager.h" +#include "building.h" +#include "battle.h" +#include "arena.h" +#include "start_position.h" +#include "party.h" +#include "monarch.h" +#include "castle.h" +#include "xmas_event.h" +#include "log.h" +#include "threeway_war.h" +#include "unique_item.h" +#include "DragonSoul.h" +#include "../../common/CommonDefines.h" + +extern bool DropEvent_RefineBox_SetValue(const std::string& name, int value); + +// ADD_COMMAND_SLOW_STUN +enum +{ + COMMANDAFFECT_STUN, + COMMANDAFFECT_SLOW, +}; + +void Command_ApplyAffect(LPCHARACTER ch, const char* argument, const char* affectName, int cmdAffect) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + sys_log(0, arg1); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: %s ", affectName); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s is not in same map", arg1); + return; + } + + switch (cmdAffect) + { + case COMMANDAFFECT_STUN: + SkillAttackAffect(tch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, 30, "GM_STUN"); + break; + case COMMANDAFFECT_SLOW: + SkillAttackAffect(tch, 1000, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 30, "GM_SLOW"); + break; + } + + sys_log(0, "%s %s", arg1, affectName); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s %s", arg1, affectName); +} +// END_OF_ADD_COMMAND_SLOW_STUN + +ACMD(do_stun) +{ + Command_ApplyAffect(ch, argument, "stun", COMMANDAFFECT_STUN); +} + +ACMD(do_slow) +{ + Command_ApplyAffect(ch, argument, "slow", COMMANDAFFECT_SLOW); +} + +ACMD(do_transfer) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: transfer "); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Target(%s) is in %d channel (my channel %d)", arg1, pkCCI->bChannel, g_bChannel); + return; + } + + TPacketGGTransfer pgg; + + pgg.bHeader = HEADER_GG_TRANSFER; + strlcpy(pgg.szName, arg1, sizeof(pgg.szName)); + pgg.lX = ch->GetX(); + pgg.lY = ch->GetY(); + + P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGTransfer)); + ch->ChatPacket(CHAT_TYPE_INFO, "Transfer requested."); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, "There is no character(%s) by that name", arg1); + + return; + } + + if (ch == tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Transfer me?!?"); + return; + } + + //tch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ()); + tch->WarpSet(ch->GetX(), ch->GetY(), ch->GetMapIndex()); +} + +// LUA_ADD_GOTO_INFO +struct GotoInfo +{ + std::string st_name; + + BYTE empire; + int mapIndex; + DWORD x, y; + + GotoInfo() + { + st_name = ""; + empire = 0; + mapIndex = 0; + + x = 0; + y = 0; + } + GotoInfo(const GotoInfo& c_src) + { + __copy__(c_src); + } + void operator = (const GotoInfo& c_src) + { + __copy__(c_src); + } + void __copy__(const GotoInfo& c_src) + { + st_name = c_src.st_name; + empire = c_src.empire; + mapIndex = c_src.mapIndex; + + x = c_src.x; + y = c_src.y; + } +}; + +static std::vector gs_vec_gotoInfo; + +void CHARACTER_AddGotoInfo(const std::string& c_st_name, BYTE empire, int mapIndex, DWORD x, DWORD y) +{ + GotoInfo newGotoInfo; + newGotoInfo.st_name = c_st_name; + newGotoInfo.empire = empire; + newGotoInfo.mapIndex = mapIndex; + newGotoInfo.x = x; + newGotoInfo.y = y; + gs_vec_gotoInfo.emplace_back(newGotoInfo); + + sys_log(0, "AddGotoInfo(name=%s, empire=%d, mapIndex=%d, pos=(%d, %d))", c_st_name.c_str(), empire, mapIndex, x, y); +} + +bool FindInString(const char * c_pszFind, const char * c_pszIn) +{ + const char * c = c_pszIn; + const char * p; + + p = strchr(c, '|'); + + if (!p) + return (0 == strncasecmp(c_pszFind, c_pszIn, strlen(c_pszFind))); + else + { + char sz[64 + 1]; + + do + { + strlcpy(sz, c, MIN(sizeof(sz), (p - c) + 1)); + + if (!strncasecmp(c_pszFind, sz, strlen(c_pszFind))) + return true; + + c = p + 1; + } while ((p = strchr(c, '|'))); + + strlcpy(sz, c, sizeof(sz)); + + if (!strncasecmp(c_pszFind, sz, strlen(c_pszFind))) + return true; + } + + return false; +} + +bool CHARACTER_GoToName(LPCHARACTER ch, BYTE empire, int mapIndex, const char* gotoName) +{ + std::vector::iterator i; + for (i = gs_vec_gotoInfo.begin(); i != gs_vec_gotoInfo.end(); ++i) + { + const GotoInfo& c_eachGotoInfo = *i; + + if (mapIndex != 0) + { + if (mapIndex != c_eachGotoInfo.mapIndex) + continue; + } + else if (!FindInString(gotoName, c_eachGotoInfo.st_name.c_str())) + continue; + + if (c_eachGotoInfo.empire == 0 || c_eachGotoInfo.empire == empire) + { + int x = c_eachGotoInfo.x * 100; + int y = c_eachGotoInfo.y * 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); + ch->Stop(); + return true; + } + } + return false; +} + +// END_OF_LUA_ADD_GOTO_INFO + +ACMD(do_goto) +{ + char arg1[256], arg2[256]; + int x = 0, y = 0, z = 0; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 && !*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: goto "); + return; + } + + if (isnhdigit(*arg1) && isnhdigit(*arg2)) + { + str_to_number(x, arg1); + str_to_number(y, arg2); + + PIXEL_POSITION p; + + if (SECTREE_MANAGER::instance().GetMapBasePosition(ch->GetX(), ch->GetY(), p)) + { + x += p.x / 100; + y += p.y / 100; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "You goto ( %d, %d )", x, y); + } + else + { + int mapIndex = 0; + BYTE empire = 0; + + if (*arg1 == '#') + str_to_number(mapIndex, (arg1 + 1)); + + if (*arg2 && isnhdigit(*arg2)) + { + str_to_number(empire, arg2); + empire = MINMAX(1, empire, 3); + } + else + empire = ch->GetEmpire(); + + if (CHARACTER_GoToName(ch, empire, mapIndex, arg1)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Cannot find map command syntax: /goto [empire]"); + return; + } + + return; + + /* + int iMapIndex = 0; + for (int i = 0; aWarpInfo[i].c_pszName != NULL; ++i) + { + if (iMapIndex != 0) + { + if (iMapIndex != aWarpInfo[i].iMapIndex) + continue; + } + else if (!FindInString(arg1, aWarpInfo[i].c_pszName)) + continue; + + if (aWarpInfo[i].bEmpire == 0 || aWarpInfo[i].bEmpire == bEmpire) + { + x = aWarpInfo[i].x * 100; + y = aWarpInfo[i].y * 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); + ch->Stop(); + return; + } + } + */ + + } + + x *= 100; + y *= 100; + + ch->Show(ch->GetMapIndex(), x, y, z); + ch->Stop(); +} + +ACMD(do_warp) +{ + char arg1[256], arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: warp | "); + return; + } + + int x = 0, y = 0; +#ifdef ENABLE_CMD_WARP_IN_DUNGEON + int mapIndex = 0; +#endif + + if (isnhdigit(*arg1) && isnhdigit(*arg2)) + { + str_to_number(x, arg1); + str_to_number(y, arg2); + } + else + { + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (NULL == tch) + { + const CCI* pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (NULL != pkCCI) + { + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Target(%s) is in %d channel (my channel %d)", arg1, pkCCI->bChannel, g_bChannel); + return; + } + + ch->WarpToPID( pkCCI->dwPID ); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no one(%s) by that name", arg1); + } + + return; + } + else + { + x = tch->GetX() / 100; + y = tch->GetY() / 100; +#ifdef ENABLE_CMD_WARP_IN_DUNGEON + mapIndex = tch->GetMapIndex(); +#endif + } + } + + x *= 100; + y *= 100; + +#ifdef ENABLE_CMD_WARP_IN_DUNGEON + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d, %d )", x, y, mapIndex); + ch->WarpSet(x, y, mapIndex); +#else + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); +#endif + ch->Stop(); +} + +#ifdef ENABLE_NEWSTUFF +ACMD(do_rewarp) +{ + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", ch->GetX(), ch->GetY()); + ch->WarpSet(ch->GetX(), ch->GetY()); + ch->Stop(); +} +#endif + +ACMD(do_item) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: item "); + return; + } + + int iCount = 1; + + if (*arg2) + { + str_to_number(iCount, arg2); + iCount = MINMAX(1, iCount, g_bItemCountLimit); + } + + DWORD dwVnum; + + if (isnhdigit(*arg1)) + str_to_number(dwVnum, arg1); + else + { + if (!ITEM_MANAGER::instance().GetVnum(arg1, dwVnum)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%u item not exist by that vnum(%s).", dwVnum, arg1); + return; + } + } + + LPITEM item = ITEM_MANAGER::instance().CreateItem(dwVnum, iCount, 0, true); + + if (item) + { + int iEmptyPos = ch->GetEmptyInventoryEx(item); + if (iEmptyPos != -1) + { + item->AddToCharacter(ch, TItemPos(item->GetWindowInventoryEx(), iEmptyPos)); + LogManager::instance().ItemLog(ch, item, "GM", item->GetName()); + } + else + { + M2_DESTROY_ITEM(item); + ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space."); + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%u item not exist by that vnum(%s).", dwVnum, arg1); + } +} + +ACMD(do_group_random) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: grrandom "); + return; + } + + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + CHARACTER_MANAGER::instance().SpawnGroupGroup(dwVnum, ch->GetMapIndex(), ch->GetX() - 500, ch->GetY() - 500, ch->GetX() + 500, ch->GetY() + 500); +} + +ACMD(do_group) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: group "); + return; + } + + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + if (test_server) + sys_log(0, "COMMAND GROUP SPAWN %u at %u %u %u", dwVnum, ch->GetMapIndex(), ch->GetX(), ch->GetY()); + + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, ch->GetMapIndex(), ch->GetX() - 500, ch->GetY() - 500, ch->GetX() + 500, ch->GetY() + 500); +} + +ACMD(do_mob_coward) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + LPCHARACTER tch; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mc "); + return; + } + + const CMob * pkMob; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob(%s) by that vnum", arg1); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + iCount = MIN(20, iCount); + + while (iCount--) + { + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + if (tch) + tch->SetCoward(); + } +} + +ACMD(do_mob_map) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: mm "); + return; + } + + DWORD vnum = 0; + str_to_number(vnum, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRandomPosition(vnum, ch->GetMapIndex()); + + if (tch) + ch->ChatPacket(CHAT_TYPE_INFO, "%s spawned in %dx%d", tch->GetName(), tch->GetX(), tch->GetY()); + else + ch->ChatPacket(CHAT_TYPE_INFO, "Spawn failed."); +} + +ACMD(do_mob_aggresive) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + LPCHARACTER tch; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob * pkMob; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob(%s) by that vnum", arg1); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + iCount = MIN(20, iCount); + + while (iCount--) + { + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + if (tch) + tch->SetAggressive(); + } +} + +ACMD(do_mob) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob* pkMob = NULL; + + if (isnhdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob(%s) by that vnum", arg1); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + if (test_server) + iCount = MIN(40, iCount); + else + iCount = MIN(20, iCount); + + while (iCount--) + { + CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + } +} + +ACMD(do_mob_ld) +{ + char arg1[256], arg2[256], arg3[256], arg4[256]; + DWORD vnum = 0; + + two_arguments(two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3), arg4, sizeof(arg4)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob* pkMob = NULL; + + if (isnhdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob(%s) by that vnum", arg1); + return; + } + + int dir = 1; + long x=0,y=0; + + if (*arg2) + str_to_number(x, arg2); + if (*arg3) + str_to_number(y, arg3); + if (*arg4) + str_to_number(dir, arg4); + + CHARACTER_MANAGER::instance().SpawnMob(vnum, + ch->GetMapIndex(), + x*100, + y*100, + ch->GetZ(), + pkMob->m_table.bType == CHAR_TYPE_STONE, + dir); +} + +struct FuncPurge +{ + LPCHARACTER m_pkGM; + bool m_bAll; + + FuncPurge(LPCHARACTER ch) : m_pkGM(ch), m_bAll(false) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY()); + + if (!m_bAll && iDist >= 1000) + return; + + sys_log(0, "PURGE: %s %d", pkChr->GetName(), iDist); + + if (pkChr->IsNPC() && !pkChr->IsPet() && pkChr->GetRider() == NULL) + { + M2_DESTROY_CHARACTER(pkChr); + } + } +}; + +ACMD(do_purge) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + FuncPurge func(ch); + + if (*arg1 && !strcmp(arg1, "all")) + func.m_bAll = true; + + LPSECTREE sectree = ch->GetSectree(); + if (sectree) // #431 + sectree->ForEachAround(func); + else + sys_err("PURGE_ERROR.NULL_SECTREE(mapIndex=%d, pos=(%d, %d)", ch->GetMapIndex(), ch->GetX(), ch->GetY()); +} + +#define ENABLE_CMD_IPURGE_EX +ACMD(do_item_purge) +{ + ch->ComputePoints(); //@fixme300 +#ifdef ENABLE_CMD_IPURGE_EX + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: ipurge "); + ch->ChatPacket(CHAT_TYPE_INFO, "List of the available windows:"); + ch->ChatPacket(CHAT_TYPE_INFO, " all"); + ch->ChatPacket(CHAT_TYPE_INFO, " inventory or inv"); + ch->ChatPacket(CHAT_TYPE_INFO, " equipment or equip"); + ch->ChatPacket(CHAT_TYPE_INFO, " dragonsoul or ds"); + ch->ChatPacket(CHAT_TYPE_INFO, " belt"); + return; + } + + int i{}; + LPITEM item{}; + std::string strArg(arg1); + if (!strArg.compare(0, 3, "all")) + { + for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + { + if ((item = ch->GetInventoryItem(i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = ch->GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i )))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + } + } + } + else if (!strArg.compare(0, 3, "inv")) + { + for (i = 0; i < INVENTORY_MAX_NUM; ++i) + { + if ((item = ch->GetInventoryItem(i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + } + else if (!strArg.compare(0, 5, "equip")) + { + for (i = 0; i < WEAR_MAX_NUM; ++i) + { + if ((item = ch->GetInventoryItem(INVENTORY_MAX_NUM + i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, INVENTORY_MAX_NUM + i, 255); + } + } + } + else if (!strArg.compare(0, 6, "dragon") || !strArg.compare(0, 2, "ds")) + { + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = ch->GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i )))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + } + } + } + else if (!strArg.compare(0, 4, "belt")) + { + for (i = 0; i < BELT_INVENTORY_SLOT_COUNT; ++i) + { + if ((item = ch->GetInventoryItem(BELT_INVENTORY_SLOT_START + i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, BELT_INVENTORY_SLOT_START + i, 255); + } + } + } +#else + int i{}; + LPITEM item{}; + for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + { + if ((item = ch->GetInventoryItem(i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = ch->GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i )))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + } + } +#endif + ch->ComputePoints(); //@fixme300 +} + +ACMD(do_state) +{ + char arg1[256]; + LPCHARACTER tch; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + if (arg1[0] == '#') + { + tch = CHARACTER_MANAGER::instance().Find(strtoul(arg1+1, NULL, 10)); + } + else + { + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + + if (!d) + tch = NULL; + else + tch = d->GetCharacter(); + } + } + else + tch = ch; + + if (!tch) + return; + + char buf[256]; + + snprintf(buf, sizeof(buf), "%s's State: ", tch->GetName()); + + if (tch->IsPosition(POS_FIGHTING)) + strlcat(buf, "Battle", sizeof(buf)); + else if (tch->IsPosition(POS_DEAD)) + strlcat(buf, "Dead", sizeof(buf)); + else + strlcat(buf, "Standing", sizeof(buf)); + + if (ch->GetShop()) + strlcat(buf, ", Shop", sizeof(buf)); + + if (ch->GetExchange()) + strlcat(buf, ", Exchange", sizeof(buf)); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + + int len = snprintf(buf, sizeof(buf), "Coordinate %ldx%ld (%ldx%ld)", + tch->GetX(), tch->GetY(), tch->GetX() / 100, tch->GetY() / 100); + + len = snprintf(buf, sizeof(buf), "Hostname %s Channel %u (port %u)", g_stHostname.c_str(), g_bChannel, mother_port); + + if (len < 0 || len >= (int) sizeof(buf)) + len = sizeof(buf) - 1; + + LPSECTREE pSec = SECTREE_MANAGER::instance().Get(tch->GetMapIndex(), tch->GetX(), tch->GetY()); + if (pSec) + { + TMapSetting& map_setting = SECTREE_MANAGER::instance().GetMap(tch->GetMapIndex())->m_setting; + snprintf(buf + len, sizeof(buf) - len, " MapIndex %ld Attribute %08X Local Position (%ld x %ld)", + tch->GetMapIndex(), pSec->GetAttribute(tch->GetX(), tch->GetY()), (tch->GetX() - map_setting.iBaseX)/100, (tch->GetY() - map_setting.iBaseY)/100); + } + + ch->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + + ch->ChatPacket(CHAT_TYPE_INFO, "LEV %d", tch->GetLevel()); + ch->ChatPacket(CHAT_TYPE_INFO, "HP %d/%d", tch->GetHP(), tch->GetMaxHP()); + ch->ChatPacket(CHAT_TYPE_INFO, "SP %d/%d", tch->GetSP(), tch->GetMaxSP()); + ch->ChatPacket(CHAT_TYPE_INFO, "ATT %d MAGIC_ATT %d SPD %d CRIT %d%% PENE %d%% ATT_BONUS %d%%", + tch->GetPoint(POINT_ATT_GRADE), + tch->GetPoint(POINT_MAGIC_ATT_GRADE), + tch->GetPoint(POINT_ATT_SPEED), + tch->GetPoint(POINT_CRITICAL_PCT), + tch->GetPoint(POINT_PENETRATE_PCT), + tch->GetPoint(POINT_ATT_BONUS)); + ch->ChatPacket(CHAT_TYPE_INFO, "DEF %d MAGIC_DEF %d BLOCK %d%% DODGE %d%% DEF_BONUS %d%%", + tch->GetPoint(POINT_DEF_GRADE), + tch->GetPoint(POINT_MAGIC_DEF_GRADE), + tch->GetPoint(POINT_BLOCK), + tch->GetPoint(POINT_DODGE), + tch->GetPoint(POINT_DEF_BONUS)); + #ifdef ENABLE_MOUNT_COSTUME_EX_SYSTEM + ch->ChatPacket(CHAT_TYPE_INFO, "MOUNT %d", tch->GetPoint(POINT_MOUNT)); + #endif + + ch->ChatPacket(CHAT_TYPE_INFO, "RESISTANCES:"); + ch->ChatPacket(CHAT_TYPE_INFO, " WARR:%3d%% ASAS:%3d%% SURA:%3d%% SHAM:%3d%%" + #ifdef ENABLE_WOLFMAN_CHARACTER + " WOLF:%3d%%" + #endif + " HUMAN:%3d%%" + , + tch->GetPoint(POINT_RESIST_WARRIOR), + tch->GetPoint(POINT_RESIST_ASSASSIN), + tch->GetPoint(POINT_RESIST_SURA), + tch->GetPoint(POINT_RESIST_SHAMAN), + #ifdef ENABLE_WOLFMAN_CHARACTER + tch->GetPoint(POINT_RESIST_WOLFMAN), + #endif + tch->GetPoint(POINT_RESIST_HUMAN) + ); + ch->ChatPacket(CHAT_TYPE_INFO, " SWORD:%3d%% THSWORD:%3d%% DAGGER:%3d%% BELL:%3d%% FAN:%3d%% BOW:%3d%%" + #ifdef ENABLE_WOLFMAN_CHARACTER + " CLAW:%3d%%" + #endif + , + tch->GetPoint(POINT_RESIST_SWORD), + tch->GetPoint(POINT_RESIST_TWOHAND), + tch->GetPoint(POINT_RESIST_DAGGER), + tch->GetPoint(POINT_RESIST_BELL), + tch->GetPoint(POINT_RESIST_FAN), + tch->GetPoint(POINT_RESIST_BOW) + #ifdef ENABLE_WOLFMAN_CHARACTER + ,tch->GetPoint(POINT_RESIST_CLAW) + #endif + ); + + ch->ChatPacket(CHAT_TYPE_INFO, " ELEC:%3d%% FIRE:%3d%% ICE:%3d%% WIND:%3d%% EARTH:%3d%% DARK:%3d%%", + tch->GetPoint(POINT_RESIST_ELEC), + tch->GetPoint(POINT_RESIST_FIRE), + tch->GetPoint(POINT_RESIST_ICE), + tch->GetPoint(POINT_RESIST_WIND), + tch->GetPoint(POINT_RESIST_EARTH), + tch->GetPoint(POINT_RESIST_DARK)); + + ch->ChatPacket(CHAT_TYPE_INFO, " MAGIC:%3d%% CRIT:%3d%% PENE:%3d%% MOUNT_FALL:%3d%%", + tch->GetPoint(POINT_RESIST_MAGIC), + tch->GetPoint(POINT_RESIST_CRITICAL), + tch->GetPoint(POINT_RESIST_PENETRATE), + tch->GetPoint(POINT_RESIST_MOUNT_FALL) + ); + + ch->ChatPacket(CHAT_TYPE_INFO, " ZODIAC:%3d%% INSECT:%3d%% DESERT:%3d%%", + tch->GetPoint(POINT_ATTBONUS_CZ), + tch->GetPoint(POINT_ATTBONUS_INSECT), + tch->GetPoint(POINT_ATTBONUS_DESERT)); + +#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM + ch->ChatPacket(CHAT_TYPE_INFO, " MAGIC REDUCTION:%3d%%", tch->GetPoint(POINT_RESIST_MAGIC_REDUCTION)); +#endif + + ch->ChatPacket(CHAT_TYPE_INFO, "ENCHANT:"); + ch->ChatPacket(CHAT_TYPE_INFO, " ELEC:%3d%% FIRE:%3d%% ICE:%3d%% WIND:%3d%% EARTH:%3d%% DARK:%3d%%", + tch->GetPoint(POINT_ENCHANT_ELECT), + tch->GetPoint(POINT_ENCHANT_FIRE), + tch->GetPoint(POINT_ENCHANT_ICE), + tch->GetPoint(POINT_ENCHANT_WIND), + tch->GetPoint(POINT_ENCHANT_EARTH), + tch->GetPoint(POINT_ENCHANT_DARK)); + + ch->ChatPacket(CHAT_TYPE_INFO, "MALL:"); + ch->ChatPacket(CHAT_TYPE_INFO, " ATT:%3d%% DEF:%3d%% EXP:%3d%% ITEMx%d GOLDx%d", + tch->GetPoint(POINT_MALL_ATTBONUS), + tch->GetPoint(POINT_MALL_DEFBONUS), + tch->GetPoint(POINT_MALL_EXPBONUS), + tch->GetPoint(POINT_MALL_ITEMBONUS) / 10, + tch->GetPoint(POINT_MALL_GOLDBONUS) / 10); + + ch->ChatPacket(CHAT_TYPE_INFO, "BONUS:"); + ch->ChatPacket(CHAT_TYPE_INFO, " SKILL:%3d%% NORMAL:%3d%% SKILL_DEF:%3d%% NORMAL_DEF:%3d%%", + tch->GetPoint(POINT_SKILL_DAMAGE_BONUS), + tch->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS), + tch->GetPoint(POINT_SKILL_DEFEND_BONUS), + tch->GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS)); + + ch->ChatPacket(CHAT_TYPE_INFO, "ATTBONUS:"); + ch->ChatPacket(CHAT_TYPE_INFO, " HUMAN:%3d%% ANIMAL:%3d%% ORC:%3d%% MILGYO:%3d%% UNDEAD:%3d%%", + tch->GetPoint(POINT_ATTBONUS_HUMAN), + tch->GetPoint(POINT_ATTBONUS_ANIMAL), + tch->GetPoint(POINT_ATTBONUS_ORC), + tch->GetPoint(POINT_ATTBONUS_MILGYO), + tch->GetPoint(POINT_ATTBONUS_UNDEAD)); + + ch->ChatPacket(CHAT_TYPE_INFO, " DEVIL:%3d%% INSECT:%3d%% FIRE:%3d%% ICE:%3d%% DESERT:%3d%%", + tch->GetPoint(POINT_ATTBONUS_DEVIL), + tch->GetPoint(POINT_ATTBONUS_INSECT), + tch->GetPoint(POINT_ATTBONUS_FIRE), + tch->GetPoint(POINT_ATTBONUS_ICE), + tch->GetPoint(POINT_ATTBONUS_DESERT)); + + ch->ChatPacket(CHAT_TYPE_INFO, " TREE:%3d%% MONSTER:%3d%%", + tch->GetPoint(POINT_ATTBONUS_TREE), + tch->GetPoint(POINT_ATTBONUS_MONSTER)); + + ch->ChatPacket(CHAT_TYPE_INFO, " WARR:%3d%% ASSA:%3d%% SURA:%3d%% SHAM:%3d%%" + #ifdef ENABLE_WOLFMAN_CHARACTER + " WOLF:%3d%%" + #endif + , + tch->GetPoint(POINT_ATTBONUS_WARRIOR), + tch->GetPoint(POINT_ATTBONUS_ASSASSIN), + tch->GetPoint(POINT_ATTBONUS_SURA), + tch->GetPoint(POINT_ATTBONUS_SHAMAN) + #ifdef ENABLE_WOLFMAN_CHARACTER + ,tch->GetPoint(POINT_ATTBONUS_WOLFMAN) + #endif + ); + + ch->ChatPacket(CHAT_TYPE_INFO, " SWORD:%3d%% THSWORD:%3d%% DAGGER:%3d%% BELL:%3d%% FAN:%3d%% BOW:%3d%%" + #ifdef ENABLE_WOLFMAN_CHARACTER + " CLAW:%3d%%" + #endif + , + tch->GetPoint(POINT_ATTBONUS_SWORD), + tch->GetPoint(POINT_ATTBONUS_TWOHAND), + tch->GetPoint(POINT_ATTBONUS_DAGGER), + tch->GetPoint(POINT_ATTBONUS_BELL), + tch->GetPoint(POINT_ATTBONUS_FAN), + tch->GetPoint(POINT_ATTBONUS_BOW) + #ifdef ENABLE_WOLFMAN_CHARACTER + ,tch->GetPoint(POINT_ATTBONUS_CLAW) + #endif + ); + + ch->ChatPacket(CHAT_TYPE_INFO, "IMMUNE:"); + ch->ChatPacket(CHAT_TYPE_INFO, " STUN:%d SLOW:%d FALL:%d", + tch->GetPoint(POINT_IMMUNE_STUN), + tch->GetPoint(POINT_IMMUNE_SLOW), + tch->GetPoint(POINT_IMMUNE_FALL)); + + for (int i = 0; i < MAX_PRIV_NUM; ++i) + { + if (CPrivManager::instance().GetPriv(tch, i)) + { + int iByEmpire = CPrivManager::instance().GetPrivByEmpire(tch->GetEmpire(), i); + int iByGuild = 0; + + if (tch->GetGuild()) + iByGuild = CPrivManager::instance().GetPrivByGuild(tch->GetGuild()->GetID(), i); + + int iByPlayer = CPrivManager::instance().GetPrivByCharacter(tch->GetPlayerID(), i); + + if (iByEmpire) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for empire : %d", LC_TEXT(c_apszPrivNames[i]), iByEmpire); + + if (iByGuild) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for guild : %d", LC_TEXT(c_apszPrivNames[i]), iByGuild); + + if (iByPlayer) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for player : %d", LC_TEXT(c_apszPrivNames[i]), iByPlayer); + } + } +} + +struct notice_packet_func +{ + const char * m_str; +#ifdef ENABLE_FULL_NOTICE + bool m_bBigFont; + notice_packet_func(const char * str, bool bBigFont=false) : m_str(str), m_bBigFont(bBigFont) +#else + notice_packet_func(const char * str) : m_str(str) +#endif + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; +#ifdef ENABLE_FULL_NOTICE + d->GetCharacter()->ChatPacket((m_bBigFont)?CHAT_TYPE_BIG_NOTICE:CHAT_TYPE_NOTICE, "%s", m_str); +#else + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); +#endif + } +}; + +struct monarch_notice_packet_func +{ + const char * m_str; + BYTE m_bEmpire; + + monarch_notice_packet_func(BYTE bEmpire, const char * str) : m_str(str), m_bEmpire(bEmpire) + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + if (m_bEmpire == d->GetCharacter()->GetEmpire()) + { + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); + } + } +}; + +#ifdef ENABLE_FULL_NOTICE +void SendNotice(const char * c_pszBuf, bool bBigFont) +#else +void SendNotice(const char * c_pszBuf) +#endif +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); +#ifdef ENABLE_FULL_NOTICE + std::for_each(c_ref_set.begin(), c_ref_set.end(), notice_packet_func(c_pszBuf, bBigFont)); +#else + std::for_each(c_ref_set.begin(), c_ref_set.end(), notice_packet_func(c_pszBuf)); +#endif +} + +void SendMonarchNotice(BYTE bEmpire, const char* c_pszBuf) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), monarch_notice_packet_func(bEmpire, c_pszBuf)); +} + +struct notice_map_packet_func +{ + const char* m_str; + int m_mapIndex; + bool m_bBigFont; + + notice_map_packet_func(const char* str, int idx, bool bBigFont) : m_str(str), m_mapIndex(idx), m_bBigFont(bBigFont) + { + } + + void operator() (LPDESC d) + { + if (d->GetCharacter() == NULL) return; + if (d->GetCharacter()->GetMapIndex() != m_mapIndex) return; + + d->GetCharacter()->ChatPacket(m_bBigFont == true ? CHAT_TYPE_BIG_NOTICE : CHAT_TYPE_NOTICE, "%s", m_str); + } +}; + +void SendNoticeMap(const char* c_pszBuf, int nMapIndex, bool bBigFont) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), notice_map_packet_func(c_pszBuf, nMapIndex, bBigFont)); +} + +struct log_packet_func +{ + const char * m_str; + + log_packet_func(const char * str) : m_str(str) + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + if (d->GetCharacter()->GetGMLevel() > GM_PLAYER) + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); + } +}; + +void SendLog(const char * c_pszBuf) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), log_packet_func(c_pszBuf)); +} + +#ifdef ENABLE_FULL_NOTICE +void BroadcastNotice(const char * c_pszBuf, bool bBigFont) +#else +void BroadcastNotice(const char * c_pszBuf) +#endif +{ + TPacketGGNotice p; +#ifdef ENABLE_FULL_NOTICE + p.bHeader = (bBigFont)?HEADER_GG_BIG_NOTICE:HEADER_GG_NOTICE; +#else + p.bHeader = HEADER_GG_NOTICE; +#endif + p.lSize = strlen(c_pszBuf) + 1; + + TEMP_BUFFER buf; + buf.write(&p, sizeof(p)); + buf.write(c_pszBuf, p.lSize); + + P2P_MANAGER::instance().Send(buf.read_peek(), buf.size()); // HEADER_GG_NOTICE + +#ifdef ENABLE_FULL_NOTICE + SendNotice(c_pszBuf, bBigFont); +#else + SendNotice(c_pszBuf); +#endif +} + +void BroadcastMonarchNotice(BYTE bEmpire, const char * c_pszBuf) +{ + TPacketGGMonarchNotice p; + p.bHeader = HEADER_GG_MONARCH_NOTICE; + p.bEmpire = bEmpire; + p.lSize = strlen(c_pszBuf) + 1; + + TEMP_BUFFER buf; + buf.write(&p, sizeof(p)); + buf.write(c_pszBuf, p.lSize); + + P2P_MANAGER::instance().Send(buf.read_peek(), buf.size()); + + SendMonarchNotice(bEmpire, c_pszBuf); +} + +ACMD(do_notice) +{ + BroadcastNotice(argument); +} + +ACMD(do_map_notice) +{ + SendNoticeMap(argument, ch->GetMapIndex(), false); +} + +ACMD(do_big_notice) +{ +#ifdef ENABLE_FULL_NOTICE + BroadcastNotice(argument, true); +#else + ch->ChatPacket(CHAT_TYPE_BIG_NOTICE, "%s", argument); +#endif +} + +#ifdef ENABLE_FULL_NOTICE +ACMD(do_map_big_notice) +{ + SendNoticeMap(argument, ch->GetMapIndex(), true); +} + +ACMD(do_notice_test) +{ + ch->ChatPacket(CHAT_TYPE_NOTICE, "%s", argument); +} + +ACMD(do_big_notice_test) +{ + ch->ChatPacket(CHAT_TYPE_BIG_NOTICE, "%s", argument); +} +#endif + +ACMD(do_monarch_notice) +{ + if (ch->IsMonarch() == true) + { + BroadcastMonarchNotice(ch->GetEmpire(), argument); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + } +} + +ACMD(do_who) +{ + int iTotal; + int * paiEmpireUserCount; + int iLocal; + + DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal); + + ch->ChatPacket(CHAT_TYPE_INFO, "Total [%d] %d / %d / %d (this server %d)", + iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal); +} + +class user_func +{ + public: + LPCHARACTER m_ch; + static int count; + static char str[128]; + static int str_len; + + user_func() + : m_ch(NULL) + {} + + void initialize(LPCHARACTER ch) + { + m_ch = ch; + str_len = 0; + count = 0; + str[0] = '\0'; + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + int len = snprintf(str + str_len, sizeof(str) - str_len, "%-16s ", d->GetCharacter()->GetName()); + + if (len < 0 || len >= (int) sizeof(str) - str_len) + len = (sizeof(str) - str_len) - 1; + + str_len += len; + ++count; + + if (!(count % 4)) + { + m_ch->ChatPacket(CHAT_TYPE_INFO, str); + + str[0] = '\0'; + str_len = 0; + } + } +}; + +int user_func::count = 0; +char user_func::str[128] = { 0, }; +int user_func::str_len = 0; + +ACMD(do_user) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + user_func func; + + func.initialize(ch); + std::for_each(c_ref_set.begin(), c_ref_set.end(), func); + + if (func.count % 4) + ch->ChatPacket(CHAT_TYPE_INFO, func.str); + + ch->ChatPacket(CHAT_TYPE_INFO, "Total %d", func.count); +} + +ACMD(do_disconnect) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /dc "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player.", arg1); + return; + } + + if (tch == ch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "cannot disconnect myself"); + return; + } + + DESC_MANAGER::instance().DestroyLoginKey(d); + DESC_MANAGER::instance().DestroyDesc(d); +} + +ACMD(do_kill) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /kill "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player", arg1); + return; + } + + tch->Dead(); +} + +#ifdef ENABLE_NEWSTUFF +ACMD(do_poison) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /poison "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player", arg1); + return; + } + + tch->AttackedByPoison(NULL); +} +#endif +#ifdef ENABLE_WOLFMAN_CHARACTER +ACMD(do_bleeding) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /bleeding "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player", arg1); + return; + } + + tch->AttackedByBleeding(NULL); +} +#endif + +#define MISC 0 +#define BINARY 1 +#define NUMBER 2 + +namespace DoSetTypes{ + typedef enum do_set_types_s { + GOLD, RACE, SEX, JOB, EXP, MAX_HP, MAX_SP, SKILL, ALIGNMENT, ALIGN + #ifdef ENABLE_CHEQUE_SYSTEM + , CHEQUE, WON + #endif + } do_set_types_t; +} + +const struct set_struct +{ + const char *cmd; + const char type; + const char * help; +} set_fields[] = { + { "gold", NUMBER, NULL }, +#ifdef ENABLE_WOLFMAN_CHARACTER + { "race", NUMBER, "0. Warrior, 1. Ninja, 2. Sura, 3. Shaman, 4. Lycan" }, +#else + { "race", NUMBER, "0. Warrior, 1. Ninja, 2. Sura, 3. Shaman" }, +#endif + { "sex", NUMBER, "0. Male, 1. Female" }, + { "job", NUMBER, "0. None, 1. First, 2. Second" }, + { "exp", NUMBER, NULL }, + { "max_hp", NUMBER, NULL }, + { "max_sp", NUMBER, NULL }, + { "skill", NUMBER, NULL }, + { "alignment", NUMBER, NULL }, + { "align", NUMBER, NULL }, +#ifdef ENABLE_CHEQUE_SYSTEM + { "cheque", NUMBER, NULL }, + { "won", NUMBER, NULL }, +#endif + { "\n", MISC, NULL } +}; + +ACMD(do_set) +{ + char arg1[256], arg2[256], arg3[256]; + + LPCHARACTER tch = NULL; + + int i, len; + const char* line; + + line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(line, arg3, sizeof(arg3)); + + if (!*arg1 || !*arg2 || !*arg3) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: set "); +#ifdef ENABLE_NEWSTUFF + ch->ChatPacket(CHAT_TYPE_INFO, "List of the fields available:"); + for (i = 0; *(set_fields[i].cmd) != '\n'; i++) + { + ch->ChatPacket(CHAT_TYPE_INFO, " %d. %s", i+1, set_fields[i].cmd); + if (set_fields[i].help != NULL) + ch->ChatPacket(CHAT_TYPE_INFO, " Help: %s", set_fields[i].help); + } +#endif + return; + } + + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s not exist", arg1); + return; + } + + len = strlen(arg2); + + for (i = 0; *(set_fields[i].cmd) != '\n'; i++) + if (!strncmp(arg2, set_fields[i].cmd, len)) + break; + + switch (i) + { + case DoSetTypes::GOLD: // gold + { + int gold = 0; + str_to_number(gold, arg3); + DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 3, gold); + tch->PointChange(POINT_GOLD, gold, true); + } + break; + + case DoSetTypes::RACE: // race +#ifdef ENABLE_NEWSTUFF + { + int amount = 0; + str_to_number(amount, arg3); + amount = MINMAX(0, amount, JOB_MAX_NUM); + ESex mySex = GET_SEX(tch); + DWORD dwRace = MAIN_RACE_WARRIOR_M; + switch (amount) + { + case JOB_WARRIOR: + dwRace = (mySex==SEX_MALE)?MAIN_RACE_WARRIOR_M:MAIN_RACE_WARRIOR_W; + break; + case JOB_ASSASSIN: + dwRace = (mySex==SEX_MALE)?MAIN_RACE_ASSASSIN_M:MAIN_RACE_ASSASSIN_W; + break; + case JOB_SURA: + dwRace = (mySex==SEX_MALE)?MAIN_RACE_SURA_M:MAIN_RACE_SURA_W; + break; + case JOB_SHAMAN: + dwRace = (mySex==SEX_MALE)?MAIN_RACE_SHAMAN_M:MAIN_RACE_SHAMAN_W; + break; +#ifdef ENABLE_WOLFMAN_CHARACTER + case JOB_WOLFMAN: + dwRace = (mySex==SEX_MALE)?MAIN_RACE_WOLFMAN_M:MAIN_RACE_WOLFMAN_M; + break; +#endif + } + if (dwRace!=tch->GetRaceNum()) + { + tch->SetRace(dwRace); + tch->ClearSkill(); + tch->SetSkillGroup(0); + // quick mesh change workaround begin + tch->SetPolymorph(101); + tch->SetPolymorph(0); + // quick mesh change workaround end + } + } +#endif + break; + + case DoSetTypes::SEX: // sex +#ifdef ENABLE_NEWSTUFF + { + int amount = 0; + str_to_number(amount, arg3); + amount = MINMAX(SEX_MALE, amount, SEX_FEMALE); + if (amount != GET_SEX(tch)) + { + tch->ChangeSex(); + // quick mesh change workaround begin + tch->SetPolymorph(101); + tch->SetPolymorph(0); + // quick mesh change workaround end + } + } +#endif + break; + + case DoSetTypes::JOB: // job +#ifdef ENABLE_NEWSTUFF + { + int amount = 0; + str_to_number(amount, arg3); + amount = MINMAX(0, amount, 2); + if (amount != tch->GetSkillGroup()) + { + tch->ClearSkill(); + tch->SetSkillGroup(amount); + } + } +#endif + break; + + case DoSetTypes::EXP: // exp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_EXP, amount, true); + } + break; + + case DoSetTypes::MAX_HP: // max_hp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_MAX_HP, amount, true); + } + break; + + case DoSetTypes::MAX_SP: // max_sp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_MAX_SP, amount, true); + } + break; + + case DoSetTypes::SKILL: // active skill point + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_SKILL, amount, true); + } + break; + + case DoSetTypes::ALIGN: // alignment + case DoSetTypes::ALIGNMENT: // alignment + { + int amount = 0; + str_to_number(amount, arg3); + tch->UpdateAlignment(amount - ch->GetRealAlignment()); + } + break; + +#ifdef ENABLE_CHEQUE_SYSTEM + case DoSetTypes::WON: // won + case DoSetTypes::CHEQUE: // cheque + { + int cheque = 0; + str_to_number(cheque, arg3); + tch->PointChange(POINT_CHEQUE, cheque, true); + tch->ChatPacket(CHAT_TYPE_INFO, "Cheque: ADD[%d] TOTAL[%d]", cheque, tch->GetCheque()); + } + break; +#endif + } + + if (set_fields[i].type == NUMBER) + { + int amount = 0; + str_to_number(amount, arg3); + ch->ChatPacket(CHAT_TYPE_INFO, "%s's %s set to [%d]", tch->GetName(), set_fields[i].cmd, amount); + } +} + +ACMD(do_reset) +{ + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->Save(); +} + +ACMD(do_advance) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: advance "); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s not exist", arg1); + return; + } + + int level = 0; + str_to_number(level, arg2); + + tch->ResetPoint(MINMAX(0, level, gPlayerMaxLevel)); +} + +ACMD(do_respawn) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1 && !strcasecmp(arg1, "all")) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Respaw everywhere"); + regen_reset(0, 0); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "Respaw around"); + regen_reset(ch->GetX(), ch->GetY()); + } +} + +ACMD(do_safebox_size) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int size = 0; + + if (*arg1) + str_to_number(size, arg1); + + if (size > 3 || size < 0) + size = 0; + + ch->ChatPacket(CHAT_TYPE_INFO, "Safebox size set to %d", size); + ch->ChangeSafeboxSize(size); +} + +ACMD(do_makeguild) +{ + if (ch->GetGuild()) + return; + + CGuildManager& gm = CGuildManager::instance(); + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + TGuildCreateParameter cp{}; + cp.master = ch; + strlcpy(cp.name, arg1, sizeof(cp.name)); + + if (!check_name(cp.name)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̸ Դϴ.")); + return; + } + + [[maybe_unused]] auto guildID = gm.CreateGuild(cp); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) 尡 Ǿϴ. [ӽ]"), cp.name); + +#ifdef ENABLE_GUILD_TOKEN_AUTH + CGuildManager::instance().GuildRelink(guildID, ch); + // ch->ChatPacket(CHAT_TYPE_INFO, "pid %d, guild id %d, guild leader id %d", ch->GetPlayerID(), ch->GetGuild() ? ch->GetGuild()->GetID() : 0, ch->GetGuild() ? ch->GetGuild()->GetMasterPID() : 0); + ch->SendGuildToken(); +#endif +} + +ACMD(do_deleteguild) +{ + if (ch->GetGuild()) + ch->GetGuild()->RequestDisband(ch->GetPlayerID()); +} + +ACMD(do_greset) +{ + if (ch->GetGuild()) + ch->GetGuild()->Reset(); +} + +// REFINE_ROD_HACK_BUG_FIX +ACMD(do_refine_rod) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + fishing::RealRefineRod(ch, item); +} +// END_OF_REFINE_ROD_HACK_BUG_FIX + +// REFINE_PICK +ACMD(do_refine_pick) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + { + mining::CHEAT_MAX_PICK(ch, item); + mining::RealRefinePick(ch, item); + } +} + +ACMD(do_max_pick) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + { + mining::CHEAT_MAX_PICK(ch, item); + } +} +// END_OF_REFINE_PICK + +ACMD(do_fishing_simul) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + two_arguments(argument, arg2, sizeof(arg2), arg3, sizeof(arg3)); + + int count = 1000; + int prob_idx = 0; + int level = 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: fishing_simul "); + + if (*arg1) + str_to_number(level, arg1); + + if (*arg2) + str_to_number(prob_idx, arg2); + + if (*arg3) + str_to_number(count, arg3); + + fishing::Simulation(level, count, prob_idx, ch); +} + +ACMD(do_invisibility) +{ + if (ch->IsAffectFlag(AFF_INVISIBILITY)) + { + ch->RemoveAffect(AFFECT_INVISIBILITY); + } + else + { + ch->AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, INFINITE_AFFECT_DURATION, 0, true); + } +} + +ACMD(do_event_flag) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!(*arg1) || !(*arg2)) + return; + + int value = 0; + str_to_number(value, arg2); + + if (!strcmp(arg1, "mob_item") || + !strcmp(arg1, "mob_exp") || + !strcmp(arg1, "mob_gold") || + !strcmp(arg1, "mob_dam") || + !strcmp(arg1, "mob_gold_pct") || + !strcmp(arg1, "mob_item_buyer") || + !strcmp(arg1, "mob_exp_buyer") || + !strcmp(arg1, "mob_gold_buyer") || + !strcmp(arg1, "mob_gold_pct_buyer") + ) + value = MINMAX(0, value, 1000); + + //quest::CQuestManager::instance().SetEventFlag(arg1, atoi(arg2)); + quest::CQuestManager::instance().RequestSetEventFlag(arg1, value); + ch->ChatPacket(CHAT_TYPE_INFO, "RequestSetEventFlag %s %d", arg1, value); + sys_log(0, "RequestSetEventFlag %s %d", arg1, value); +} + +ACMD(do_get_event_flag) +{ + quest::CQuestManager::instance().SendEventFlagList(ch); +} + +ACMD(do_private) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: private "); + return; + } + + long lMapIndex; + long map_index = 0; + str_to_number(map_index, arg1); + if ((lMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(map_index))) + { + ch->SaveExitLocation(); + + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + ch->WarpSet(pkSectreeMap->m_setting.posSpawn.x, pkSectreeMap->m_setting.posSpawn.y, lMapIndex); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, "Can't find map by index %d", map_index); +} + +ACMD(do_qf) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID()); + std::string questname = pPC->GetCurrentQuestName(); + + if (!questname.empty()) + { + int value = quest::CQuestManager::Instance().GetQuestStateIndex(questname, arg1); + + pPC->SetFlag(questname + ".__status", value); + pPC->ClearTimer(); + + quest::PC::QuestInfoIterator it = pPC->quest_begin(); + unsigned int questindex = quest::CQuestManager::instance().GetQuestIndexByName(questname); + + while (it!= pPC->quest_end()) + { + if (it->first == questindex) + { + it->second.st = value; + break; + } + + ++it; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag %s %s %d", questname.c_str(), arg1, value); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag failed"); + } +} + +LPCHARACTER chHori, chForge, chLib, chTemple, chTraining, chTree, chPortal, chBall; + +ACMD(do_b1) +{ + chHori = CHARACTER_MANAGER::instance().SpawnMobRange(14017, ch->GetMapIndex(), 304222, 742858, 304222, 742858, true, false); + chHori->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_CONSTRUCTION_SMALL, 65535, 0, true); + chHori->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + + for (int i = 0; i < 30; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 800, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(number(701, 706), + ch->GetMapIndex(), + 304222 + (int)fx, + 742858 + (int)fy, + 304222 + (int)fx, + 742858 + (int)fy, + true, + false); + tch->SetAggressive(); + } + + for (int i = 0; i < 5; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 800, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(8009, + ch->GetMapIndex(), + 304222 + (int)fx, + 742858 + (int)fy, + 304222 + (int)fx, + 742858 + (int)fy, + true, + false); + tch->SetAggressive(); + } +} + +ACMD(do_b2) +{ + chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); +} + +ACMD(do_b3) +{ + chForge = CHARACTER_MANAGER::instance().SpawnMobRange(14003, ch->GetMapIndex(), 307500, 746300, 307500, 746300, true, false); + chForge->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14007, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + + chTemple = CHARACTER_MANAGER::instance().SpawnMobRange(14004, ch->GetMapIndex(), 307700, 741600, 307700, 741600, true, false); + chTemple->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + + chTraining= CHARACTER_MANAGER::instance().SpawnMobRange(14010, ch->GetMapIndex(), 307100, 739500, 307100, 739500, true, false); + chTraining->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + chTree= CHARACTER_MANAGER::instance().SpawnMobRange(14013, ch->GetMapIndex(), 300800, 741600, 300800, 741600, true, false); + chTree->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + chPortal= CHARACTER_MANAGER::instance().SpawnMobRange(14001, ch->GetMapIndex(), 300900, 744500, 300900, 744500, true, false); + chPortal->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + chBall = CHARACTER_MANAGER::instance().SpawnMobRange(14012, ch->GetMapIndex(), 302500, 746600, 302500, 746600, true, false); + chBall->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_b4) +{ + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_UPGRADE, 65535, 0, true); + + for (int i = 0; i < 30; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 1200, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(number(701, 706), + ch->GetMapIndex(), + 307900 + (int)fx, + 744500 + (int)fy, + 307900 + (int)fx, + 744500 + (int)fy, + true, + false); + tch->SetAggressive(); + } + + for (int i = 0; i < 5; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 1200, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(8009, + ch->GetMapIndex(), + 307900 + (int)fx, + 744500 + (int)fy, + 307900 + (int)fx, + 744500 + (int)fy, + true, + false); + tch->SetAggressive(); + } +} + +ACMD(do_b5) +{ + M2_DESTROY_CHARACTER(chLib); + //chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14008, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_b6) +{ + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_UPGRADE, 65535, 0, true); +} +ACMD(do_b7) +{ + M2_DESTROY_CHARACTER(chLib); + //chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14009, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_book) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + CSkillProto * pkProto; + + if (isnhdigit(*arg1)) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + pkProto = CSkillManager::instance().Get(vnum); + } + else + pkProto = CSkillManager::instance().Get(arg1); + + if (!pkProto) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such a skill."); + return; + } + + LPITEM item = ch->AutoGiveItem(50300); + item->SetSocket(0, pkProto->dwVnum); +} + +ACMD(do_setskillother) +{ + char arg1[256], arg2[256], arg3[256]; + argument = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(argument, arg3, sizeof(arg3)); + + if (!*arg1 || !*arg2 || !*arg3 || !isdigit(*arg3)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setskillother "); + return; + } + + LPCHARACTER tch; + + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + CSkillProto * pk; + + if (isdigit(*arg2)) + { + DWORD vnum = 0; + str_to_number(vnum, arg2); + pk = CSkillManager::instance().Get(vnum); + } + else + pk = CSkillManager::instance().Get(arg2); + + if (!pk) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such a skill by that name."); + return; + } + + BYTE level = 0; + str_to_number(level, arg3); + tch->SetSkillLevel(pk->dwVnum, level); + tch->ComputePoints(); + tch->SkillLevelPacket(); +} + +ACMD(do_setskill) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2 || !isdigit(*arg2)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setskill "); + return; + } + + CSkillProto * pk; + + if (isdigit(*arg1)) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + pk = CSkillManager::instance().Get(vnum); + } + + else + pk = CSkillManager::instance().Get(arg1); + + if (!pk) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such a skill by that name."); + return; + } + + BYTE level = 0; + str_to_number(level, arg2); + ch->SetSkillLevel(pk->dwVnum, level); + ch->ComputePoints(); + ch->SkillLevelPacket(); +} + +ACMD(do_set_skill_point) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int skill_point = 0; + if (*arg1) + str_to_number(skill_point, arg1); + + ch->SetRealPoint(POINT_SKILL, skill_point); + ch->SetPoint(POINT_SKILL, ch->GetRealPoint(POINT_SKILL)); + ch->PointChange(POINT_SKILL, 0); +} + +ACMD(do_set_skill_group) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int skill_group = 0; + if (*arg1) + str_to_number(skill_group, arg1); + + ch->SetSkillGroup(skill_group); + + ch->ClearSkill(); + ch->ChatPacket(CHAT_TYPE_INFO, "skill group to %d.", skill_group); +} + +ACMD(do_reload) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + switch (LOWER(*arg1)) + { + case 'u': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading state_user_count."); + LoadStateUserCount(); + break; + + case 'p': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading prototype tables,"); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); + break; + + case 'q': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading quest."); + quest::CQuestManager::instance().Reload(); + break; + + case 'f': + fishing::Initialize(); + break; + + //RELOAD_ADMIN + case 'a': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading Admin infomation."); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0); + sys_log(0, "Reloading admin infomation."); + break; + //END_RELOAD_ADMIN + case 'c': // cube + + Cube_init (); + break; + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading state_user_count."); + LoadStateUserCount(); + + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading prototype tables,"); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); + } +} + +ACMD(do_cooltime) +{ + ch->DisableCooltime(); +} + +ACMD(do_level) +{ + char arg2[256]; + one_argument(argument, arg2, sizeof(arg2)); + + if (!*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: level "); + return; + } + + int level = 0; + str_to_number(level, arg2); + + ch->ResetPoint(MINMAX(1, level, gPlayerMaxLevel)); + + ch->ClearSkill(); + ch->ClearSubSkill(); +} + +ACMD(do_gwlist) +{ + ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" Դϴ")); + CGuildManager::instance().ShowGuildWarList(ch); +} + +ACMD(do_stop_guild_war) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + int id1 = 0, id2 = 0; + + str_to_number(id1, arg1); + str_to_number(id2, arg2); + + if (!id1 || !id2) + return; + + if (id1 > id2) + { + std::swap(id1, id2); + } + + ch->ChatPacket(CHAT_TYPE_TALKING, "%d %d", id1, id2); + CGuildManager::instance().RequestEndWar(id1, id2); +} + +ACMD(do_cancel_guild_war) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + int id1 = 0, id2 = 0; + str_to_number(id1, arg1); + str_to_number(id2, arg2); + + if (id1 > id2) + std::swap(id1, id2); + + CGuildManager::instance().RequestCancelWar(id1, id2); +} + +ACMD(do_guild_state) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + CGuild* pGuild = CGuildManager::instance().FindGuildByName(arg1); + if (pGuild != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "GuildID: %d", pGuild->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, "GuildMasterPID: %d", pGuild->GetMasterPID()); + ch->ChatPacket(CHAT_TYPE_INFO, "IsInWar: %d", pGuild->UnderAnyWar()); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s: ʴ Դϴ."), arg1); + } +} + +struct FuncWeaken +{ + LPCHARACTER m_pkGM; + bool m_bAll; + + FuncWeaken(LPCHARACTER ch) : m_pkGM(ch), m_bAll(false) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY()); + + if (!m_bAll && iDist >= 1000) + return; + + if (pkChr->IsNPC()) + pkChr->PointChange(POINT_HP, (10 - pkChr->GetHP())); + } +}; + +ACMD(do_weaken) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + FuncWeaken func(ch); + + if (*arg1 && !strcmp(arg1, "all")) + func.m_bAll = true; + + ch->GetSectree()->ForEachAround(func); +} + +ACMD(do_getqf) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + LPCHARACTER tch; + + if (!*arg1) + tch = ch; + else + { + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + pPC->SendFlagList(ch); +} + +#define ENABLE_SET_STATE_WITH_TARGET +ACMD(do_set_state) +{ + char arg1[256]; + char arg2[256]; + + argument = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, + "Syntax: set_state " +#ifdef ENABLE_SET_STATE_WITH_TARGET + " []" +#endif + ); + return; + } + +#ifdef ENABLE_SET_STATE_WITH_TARGET + LPCHARACTER tch = ch; + char arg3[256]; + argument = one_argument(argument, arg3, sizeof(arg3)); + if (*arg3) + { + tch = CHARACTER_MANAGER::instance().FindPC(arg3); + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + } + quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(tch->GetPlayerID()); +#else + quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID()); +#endif + std::string questname = arg1; + std::string statename = arg2; + + if (!questname.empty()) + { + int value = quest::CQuestManager::Instance().GetQuestStateIndex(questname, statename); + + pPC->SetFlag(questname + ".__status", value); + pPC->ClearTimer(); + + quest::PC::QuestInfoIterator it = pPC->quest_begin(); + unsigned int questindex = quest::CQuestManager::instance().GetQuestIndexByName(questname); + + while (it!= pPC->quest_end()) + { + if (it->first == questindex) + { + it->second.st = value; + break; + } + + ++it; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag %s %s %d", questname.c_str(), arg1, value); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag failed"); + } +} + +ACMD(do_setqf) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + + one_argument(two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setqf []"); + return; + } + + LPCHARACTER tch = ch; + + if (*arg3) + tch = CHARACTER_MANAGER::instance().FindPC(arg3); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + { + int value = 0; + str_to_number(value, arg2); + pPC->SetFlag(arg1, value); + ch->ChatPacket(CHAT_TYPE_INFO, "Quest flag set: %s %d", arg1, value); + } +} + +ACMD(do_delqf) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: delqf []"); + return; + } + + LPCHARACTER tch = ch; + + if (*arg2) + tch = CHARACTER_MANAGER::instance().FindPC(arg2); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + { + if (pPC->DeleteFlag(arg1)) + ch->ChatPacket(CHAT_TYPE_INFO, "Delete success."); + else + ch->ChatPacket(CHAT_TYPE_INFO, "Delete failed. Quest flag does not exist."); + } +} + +ACMD(do_forgetme) +{ + ch->ForgetMyAttacker(); +} + +ACMD(do_aggregate) +{ + ch->AggregateMonster(); +} + +ACMD(do_attract_ranger) +{ + ch->AttractRanger(); +} + +ACMD(do_pull_monster) +{ + ch->PullMonster(); +} + +ACMD(do_polymorph) +{ + char arg1[256], arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + bool bMaintainStat = false; + if (*arg2) + { + int value = 0; + str_to_number(value, arg2); + bMaintainStat = (value>0); + } + + ch->SetPolymorph(dwVnum, bMaintainStat); + } +} + +ACMD(do_polymorph_item) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + LPITEM item = ITEM_MANAGER::instance().CreateItem(70104, 1, 0, true); + if (item) + { + item->SetSocket(0, dwVnum); + int iEmptyPos = ch->GetEmptyInventory(item->GetSize()); + + if (iEmptyPos != -1) + { + item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); + LogManager::instance().ItemLog(ch, item, "GM", item->GetName()); + } + else + { + M2_DESTROY_ITEM(item); + ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space."); + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d item not exist by that vnum.", 70103); + } + //ch->SetPolymorph(dwVnum, bMaintainStat); + } +} + +ACMD(do_priv_empire) +{ + char arg1[256] = {0}; + char arg2[256] = {0}; + char arg3[256] = {0}; + char arg4[256] = {0}; + int empire = 0; + int type = 0; + int value = 0; + int duration = 0; + + const char* line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + goto USAGE; + + if (!line) + goto USAGE; + + two_arguments(line, arg3, sizeof(arg3), arg4, sizeof(arg4)); + + if (!*arg3 || !*arg4) + goto USAGE; + + str_to_number(empire, arg1); + str_to_number(type, arg2); + str_to_number(value, arg3); + value = MINMAX(0, value, 1000); + str_to_number(duration, arg4); + + if (empire < 0 || 3 < empire) + goto USAGE; + + if (type < 1 || 4 < type) + goto USAGE; + + if (value < 0) + goto USAGE; + + if (duration < 0) + goto USAGE; + + duration = duration * (60*60); + + sys_log(0, "_give_empire_privileage(empire=%d, type=%d, value=%d, duration=%d) by command", + empire, type, value, duration); + CPrivManager::instance().RequestGiveEmpirePriv(empire, type, value, duration); + return; + +USAGE: + ch->ChatPacket(CHAT_TYPE_INFO, "usage : priv_empire "); + ch->ChatPacket(CHAT_TYPE_INFO, " 0 - 3 (0==all)"); + ch->ChatPacket(CHAT_TYPE_INFO, " 1:item_drop, 2:gold_drop, 3:gold10_drop, 4:exp"); + ch->ChatPacket(CHAT_TYPE_INFO, " percent"); + ch->ChatPacket(CHAT_TYPE_INFO, " hour"); +} + +ACMD(do_priv_guild) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + CGuild * g = CGuildManager::instance().FindGuildByName(arg1); + + if (!g) + { + DWORD guild_id = 0; + str_to_number(guild_id, arg1); + g = CGuildManager::instance().FindGuild(guild_id); + } + + if (!g) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ̸ Ǵ ȣ 尡 ϴ.")); + else + { + char buf[1024+1]; + snprintf(buf, sizeof(buf), "%d", g->GetID()); // @fixme177 + + using namespace quest; + PC * pc = CQuestManager::instance().GetPC(ch->GetPlayerID()); + QuestState qs = CQuestManager::instance().OpenState("ADMIN_QUEST", QUEST_FISH_REFINE_STATE_INDEX); + luaL_loadbuffer(qs.co, buf, strlen(buf), "ADMIN_QUEST"); + pc->SetQuest("ADMIN_QUEST", qs); + + QuestState & rqs = *pc->GetRunningQuestState(); + + if (!CQuestManager::instance().RunState(rqs)) + { + CQuestManager::instance().CloseState(rqs); + pc->EndRunning(); + return; + } + } + } +} + +ACMD(do_mount_test) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + ch->MountVnum(vnum); + } +} + +ACMD(do_observer) +{ + ch->SetObserverMode(!ch->IsObserverMode()); +} + +ACMD(do_socket_item) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + int iSocketCount = 0; + str_to_number(iSocketCount, arg2); + + if (!iSocketCount || iSocketCount >= ITEM_SOCKET_MAX_NUM) + iSocketCount = 3; + + if (!dwVnum) + { + if (!ITEM_MANAGER::instance().GetVnum(arg1, dwVnum)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d item not exist by that vnum.", dwVnum); + return; + } + } + + LPITEM item = ch->AutoGiveItem(dwVnum); + + if (item) + { + for (int i = 0; i < iSocketCount; ++i) + item->SetSocket(i, 1); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d cannot create item.", dwVnum); + } + } +} + +ACMD(do_xmas) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int flag = 0; + + if (*arg1) + str_to_number(flag, arg1); + + switch (subcmd) + { + case SCMD_XMAS_SNOW: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_snow", flag); + break; + + case SCMD_XMAS_BOOM: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_boom", flag); + break; + + case SCMD_XMAS_SANTA: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_santa", flag); + break; + } +} + +// BLOCK_CHAT +ACMD(do_block_chat_list) +{ + if (!ch || (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + DBManager::instance().ReturnQuery(QID_BLOCK_CHAT_LIST, ch->GetPlayerID(), NULL, + "SELECT p.name, a.lDuration FROM affect%s as a, player%s as p WHERE a.bType = %d AND a.dwPID = p.id", + get_table_postfix(), get_table_postfix(), AFFECT_BLOCK_CHAT); +} + +ACMD(do_vote_block_chat) +{ + return; + + char arg1[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: vote_block_chat "); + return; + } + + const char* name = arg1; + long lBlockDuration = 10; + sys_log(0, "vote_block_chat %s %d", name, lBlockDuration); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(name); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(name); + + if (pkCCI) + { + TPacketGGBlockChat p; + + p.bHeader = HEADER_GG_BLOCK_CHAT; + strlcpy(p.szName, name, sizeof(p.szName)); + p.lBlockDuration = lBlockDuration; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGBlockChat)); + } + else + { + TPacketBlockChat p; + + strlcpy(p.szName, name, sizeof(p.szName)); + p.lDuration = lBlockDuration; + db_clientdesc->DBPacket(HEADER_GD_BLOCK_CHAT, ch ? ch->GetDesc()->GetHandle() : 0, &p, sizeof(p)); + + } + + if (ch) + ch->ChatPacket(CHAT_TYPE_INFO, "Chat block requested."); + + return; + } + + if (tch && ch != tch) + tch->AddAffect(AFFECT_BLOCK_CHAT, POINT_NONE, 0, AFF_NONE, lBlockDuration, 0, true); +} + +ACMD(do_block_chat) +{ + if (ch && (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + char arg1[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + if (ch) + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: block_chat