dzdev 40 Posted February 5, 2016 Share Posted February 5, 2016 this is my sysser Spoiler SYSERR: Feb 5 16:48:23.399697 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_sg1.dat SYSERR: Feb 5 16:48:23.400283 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_ds.dat SYSERR: Feb 5 16:48:23.400343 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_dss_box.dat SYSERR: Feb 5 16:48:23.401205 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_w20_sound.dat SYSERR: Feb 5 16:48:23.401271 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_w21_etc.dat SYSERR: Feb 5 16:49:16.548289 :: LoadSkillMotion: Motion: Skill exist but no motion data for index 0 mob 3596 skill 257 SYSERR: Feb 5 16:49:16.548339 :: LoadSkillMotion: Motion: Skill exist but no motion data for index 1 mob 3596 skill 258 SYSERR: Feb 5 16:49:16.606729 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun) SYSERR: Feb 5 16:49:16.606762 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun) SYSERR: Feb 5 16:49:16.606799 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun) SYSERR: Feb 5 16:49:16.606826 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun) SYSERR: Feb 5 16:49:16.606858 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun) SYSERR: Feb 5 16:49:16.686299 :: LoadMotion: cannot find accumulation data in file 'data/monster/horse_event1/03.msa' SYSERR: Feb 5 16:49:16.687837 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa' SYSERR: Feb 5 16:49:16.688110 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa' SYSERR: Feb 5 16:49:16.688355 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa' SYSERR: Feb 5 16:49:16.688571 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa' SYSERR: Feb 5 16:49:16.688807 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa' SYSERR: Feb 5 16:49:16.689092 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa' SYSERR: Feb 5 16:49:16.689372 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa' SYSERR: Feb 5 16:49:16.689601 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa' SYSERR: Feb 5 16:49:16.689838 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa' SYSERR: Feb 5 16:49:16.690118 :: LoadMotion: cannot find accumulation data in file 'data/monster/horse_event1/03.msa' SYSERR: Feb 5 16:49:16.690426 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung) SYSERR: Feb 5 16:49:16.690454 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung) SYSERR: Feb 5 16:49:16.690480 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung) SYSERR: Feb 5 16:49:16.690531 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1) SYSERR: Feb 5 16:49:16.690605 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1) SYSERR: Feb 5 16:49:16.690631 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1) SYSERR: Feb 5 16:49:16.690694 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone) SYSERR: Feb 5 16:49:16.690768 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone) SYSERR: Feb 5 16:49:16.690797 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone) SYSERR: Feb 5 16:49:16.690848 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone) SYSERR: Feb 5 16:49:16.690874 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone) SYSERR: Feb 5 16:49:16.690899 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone) SYSERR: Feb 5 16:49:16.690949 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.691021 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.691057 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.691132 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.691166 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.691198 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone) SYSERR: Feb 5 16:49:16.694497 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2) SYSERR: Feb 5 16:49:16.694539 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2) SYSERR: Feb 5 16:49:16.694577 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2) SYSERR: Feb 5 16:49:16.694650 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet) SYSERR: Feb 5 16:49:16.694686 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet) SYSERR: Feb 5 16:49:16.694726 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet) SYSERR: Feb 5 16:49:16.694795 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom) SYSERR: Feb 5 16:49:16.694848 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom) SYSERR: Feb 5 16:49:16.694886 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom) SYSERR: Feb 5 16:49:16.694971 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1) SYSERR: Feb 5 16:49:16.695097 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1) SYSERR: Feb 5 16:49:16.695131 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1) SYSERR: Feb 5 16:49:16.695205 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2) SYSERR: Feb 5 16:49:16.695606 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2) SYSERR: Feb 5 16:49:16.695644 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2) SYSERR: Feb 5 16:49:16.705119 :: heart_idle: losing 52 seconds. (lag occured) SYSERR: Feb 5 16:50:04.710145 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:08.70530 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:08.73308 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686 SYSERR: Feb 5 16:50:08.589246 :: number_ex: number(): first argument is bigger than second argument 3200 -> 49, char_battle.cpp 686 SYSERR: Feb 5 16:50:08.869788 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686 SYSERR: Feb 5 16:50:09.712095 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686 SYSERR: Feb 5 16:50:10.550954 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:50:11.948144 :: number_ex: number(): first argument is bigger than second argument 3700 -> 56, char_battle.cpp 686 SYSERR: Feb 5 16:50:12.749163 :: number_ex: number(): first argument is bigger than second argument 3700 -> 56, char_battle.cpp 686 SYSERR: Feb 5 16:50:15.29466 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:50:15.588165 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:50:21.230816 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686 SYSERR: Feb 5 16:50:33.187739 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:33.192580 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:33.194831 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:33.197404 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:34.429579 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:34.908669 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:36.587687 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:38.267537 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:41.27835 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:50:41.508544 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:50:43.790214 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:51:36.65917 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:51:40.785924 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:51:44.67621 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:51:46.145853 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:51:47.465605 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:51:50.943615 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:51:51.68109 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:51:56.304151 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:51:59.65309 :: number_ex: number(): first argument is bigger than second argument 3200 -> 49, char_battle.cpp 686 SYSERR: Feb 5 16:52:01.144331 :: number_ex: number(): first argument is bigger than second argument 10000 -> 150, char_battle.cpp 686 SYSERR: Feb 5 16:52:01.225078 :: number_ex: number(): first argument is bigger than second argument 10000 -> 150, char_battle.cpp 686 SYSERR: Feb 5 16:52:24.861057 :: number_ex: number(): first argument is bigger than second argument 6400 -> 97, char_battle.cpp 686 SYSERR: Feb 5 16:52:25.141586 :: number_ex: number(): first argument is bigger than second argument 6400 -> 97, char_battle.cpp 686 SYSERR: Feb 5 16:52:31.224743 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:53:01.837879 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:53:04.472344 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686 SYSERR: Feb 5 16:53:06.433788 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:53:07.831522 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:53:09.714158 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:55:03.572948 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:03.812498 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:06.975961 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:07.130624 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:07.370970 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:07.374042 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:07.850839 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:07.853864 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.854011 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.854111 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.854193 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.854430 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.855185 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.855406 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.855508 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.855602 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:07.855730 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659 SYSERR: Feb 5 16:55:08.331137 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:08.572950 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:10.730899 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:11.51885 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:14.250761 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686 SYSERR: Feb 5 16:55:22.851039 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:55:32.23276 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686 SYSERR: Feb 5 16:55:34.385680 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:34.388597 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:34.943246 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686 SYSERR: Feb 5 16:55:35.863349 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:37.589930 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:38.424862 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686 SYSERR: Feb 5 16:55:41.461731 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:55:41.464455 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:43.985510 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:55:49.226485 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686 SYSERR: Feb 5 16:55:50.711143 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:55:51.557464 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:52.387847 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:52.505562 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:55:53.947900 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 SYSERR: Feb 5 16:55:54.946552 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:55:59.65571 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:00.426924 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:00.433965 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:03.425745 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:04.825561 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:06.829597 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686 SYSERR: Feb 5 16:56:07.26973 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686 SYSERR: Feb 5 16:56:08.149937 :: number_ex: number(): first argument is bigger than second argument 19600 -> 294, char_battle.cpp 686 SYSERR: Feb 5 16:56:08.160292 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686 i want to fix Spoiler number_ex: number(): first argument is bigger than second argument help Link to comment Share on other sites More sharing options...
aaron 5 Posted February 6, 2016 Share Posted February 6, 2016 post your char_battle.cpp Link to comment Share on other sites More sharing options...
dzdev 40 Posted February 6, 2016 Author Share Posted February 6, 2016 Spoiler #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 "dev_log.h" #include "ani.h" #include "BattleArena.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" DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp) { if (PLAYER_EXP_TABLE_MAX < 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<double>(exp); if (ret < 1.0) return 1; return static_cast<DWORD>(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()); //PROF_UNIT puAttack("Attack"); if (!CanMove()) return false; // CASTLE if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim)) return false; // CASTLE 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 (test_server && IsPC()) // sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet); 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); if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { return; } 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 (LC_IsYMIR()) // 천마 버전에서는 언제나 용신의 가호 아이템을 체크한다. { 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; } } else 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() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100); if (true == LC_IsYMIR()) { if (PLAYER_EXP_TABLE_MAX < GetLevel()) { iLoss = MIN(500000, iLoss); } else { iLoss = MIN(200000, iLoss); } } else if (true == LC_IsEurope()) { iLoss = MIN(800000, iLoss); } if (bTown) { if (g_iUseLocale) { iLoss = 0; } else { iLoss -= iLoss / 3; } } if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS)) iLoss /= 2; sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, 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<char_event_info*>( event->info ); if ( info == NULL ) { sys_err( "StunEvent> <Factor> Null pointer" ); return 0; } LPCHARACTER ch = info->ch; if (ch == NULL) { // <Factor> 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<char_event_info>(); 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<SCharDeadEventInfo*>(event->info); if ( info == NULL ) { sys_err( "dead_event> <Factor> 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; // 제3의 손 // END_OF_ADD_PREMIUM PIXEL_POSITION pos; if (!isAutoLoot) if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; 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); //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent); if (number(1, 100) > iPercent) return; int iGoldMultipler = GetGoldMultipler(); if (1 == number(1, 50000)) // 1/50000 확률로 돈이 10배 iGoldMultipler *= 10; else if (1 == number(1, 10000)) // 1/10000 확률로 돈이 5배 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)); // MOB_RANK가 BOSS보다 높으면 무조건 돈폭탄 if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0) { if (1 == number(1, iGold10DropPct)) iGoldMultipler *= 10; // 1% 확률로 돈 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); } // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음 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 } } } // 1% 확률로 돈을 10개 떨어 뜨린다. (10배 드롭임) 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; } // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음 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 && !LC_IsYMIR()) 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) // 왜구는 돈을 무조건 드롭 { PIXEL_POSITION pos; if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; 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; } //PROF_UNIT puReward("Reward"); LPCHARACTER pkAttacker = DistributeExp(); if (!pkAttacker) return; //PROF_UNIT pu1("r1"); 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); } } } //pu1.Pop(); if (!bItemDrop) return; PIXEL_POSITION pos = GetXYZ(); if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos)) return; // // 돈 드롭 // //PROF_UNIT pu2("r2"); if (test_server) sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName()); RewardGold(pkAttacker); //pu2.Pop(); // // 아이템 드롭 // //PROF_UNIT pu3("r3"); LPITEM item; static std::vector<LPITEM> 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); if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false) { 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 from %s", item->GetName(), pos.x, pos.y, GetName()); } else { int iItemIdx = s_vec_item.size() - 1; std::priority_queue<std::pair<int, LPCHARACTER> > 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<LPCHARACTER> v; while (!pq.empty() && pq.top().first * 10 >= total_dam) { v.push_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); // 10% 이하 데미지 준 사람끼리는 소유권없음 //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<LPCHARACTER>::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(); if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false) { item->SetOwnership(ch); } 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 (false == LC_IsYMIR()) { if (GetLevel() < 50) return; } if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { 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<std::pair<LPITEM, int> > 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; } if (bDropInventory) // Drop Inventory { std::vector<BYTE> vec_bSlots; for (i = 0; i < INVENTORY_MAX_NUM; ++i) if (GetInventoryItem(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { 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); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY)); } } else if (iAlignIndex == 8) isDropAllEquipments = true; } if (bDropEquipment) // Drop Equipment { std::vector<BYTE> vec_bSlots; for (i = 0; i < WEAR_MAX_NUM; ++i) if (GetWear(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { 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); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255); vec_item.push_back(std::make_pair(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.push_back(std::make_pair(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.push_back(std::make_pair(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.first; int window = vec_item.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; { if (IsHorseRiding()) { StopRiding(); } else if (GetMountVnum()) { RemoveAffect(AFFECT_MOUNT_BONUS); m_dwMountVnum = 0; UnEquipSpecialRideUniqueItem(); UpdatePacket(); } } if (!pkKiller && m_dwKillerPID) pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID); m_dwKillerPID = 0; // 반드시 초기화 해야함 DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE 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); } } //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) { // TODO : 입구로 날리는 코드를 넣어야 한다. } 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 (g_iUseLocale) { 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 (g_iUseLocale && 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++); } // // Dead 이벤트 생성, // // Dead 이벤트에서는 몬스터의 경우 몇초 후에 Destroy 되도록 해주며, // PC의 경우 3분 있다가 마을에서 나오도록 해 준다. 3분 내에는 유저로부터 // 마을에서 시작할 건지, 여기서 시작할 건지 결정을 받는다. 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<SCharDeadEventInfo>(); 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 != NULL) { m_pkExchange->Cancel(); } if (IsCubeOpen() == true) { Cube_close(this); } CShopManager::instance().StopShopping(this); CloseMyShop(); CloseSafebox(); if (true == IsMonster() && 2493 == GetMobTable().dwVnum) { if (NULL != pkKiller && NULL != 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)); } /* if (GetArenaObserverMode() == false && GetArena() != NULL) { GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo)); } */ } } // // CHARACTER::Damage 메소드는 this가 데미지를 입게 한다. // // Arguments // pAttacker : 공격자 // dam : 데미지 // EDamageType : 어떤 형식의 공격인가? // // Return value // true : dead // false : not dead yet // bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead { 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 (GetRaceNum() == 5001) { bool bDropMoney = false; int 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(); } } } } // 평타가 아닐 때는 공포 처리 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; } } int iCurHP = GetHP(); int iCurSP = GetSP(); bool IsCritical = false; bool IsPenetrate = false; bool IsDeathBlow = false; 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), }; //PROF_UNIT puAttr("Attr"); // // 마법형 스킬과, 레인지형 스킬은(궁자객) 크리티컬과, 관통공격 계산을 한다. // 원래는 하지 않아야 하는데 Nerf(다운밸런스)패치를 할 수 없어서 크리티컬과 // 관통공격의 원래 값을 쓰지 않고, /2 이상하여 적용한다. // // 무사 이야기가 많아서 밀리 스킬도 추가 // // 20091109 : 무사가 결과적으로 엄청나게 강해진 것으로 결론남, 독일 기준 무사 비율 70% 육박 // 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) // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20% iCriticalPct = 5 + (iCriticalPct - 10) / 4; else // 10보다 작으면 단순히 반으로 깎음, 10 = 5% 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<int>(pkSk->kPointPoly.Eval()); } } if (iPenetratePct >= 10) { // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20% iPenetratePct = 5 + (iPenetratePct - 10) / 4; } else { // 10보다 작으면 단순히 반으로 깎음, 10 = 5% 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); } } } } } // // 콤보 공격, 활 공격, 즉 평타 일 때만 속성값들을 계산을 한다. // 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; // NOTE: 공격자가 IMMUNE_REFLECT 속성을 갖고있다면 반사를 안 하는 게 // 아니라 1/3 데미지로 고정해서 들어가도록 기획에서 요청. 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<int>(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; } } // HP 스틸 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); } } } // SP 스틸 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); } } // 칠 때마다 HP회복 if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% 확률 { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; if (i) { CreateFly(FLY_HP_SMALL, pAttacker); pAttacker->PointChange(POINT_HP, i); } } // 칠 때마다 SP회복 if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% 확률 { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; 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)) { // POINT_MANASHIELD 는 작아질수록 좋다 int iDamageSPPart = dam / 3; int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100; int iSP = GetSP(); // SP가 있으면 무조건 데미지 절반 감소 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; } // // 제국으로 인한 보너스 (한국 올드 버전만 적용) // int iEmpire = GetEmpire(); long lMapIndex = GetMapIndex(); int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); if (LC_IsYMIR() == true) { if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { dam += (dam * 30) / 100; } } if (pAttacker->IsPC()) { iEmpire = pAttacker->GetEmpire(); lMapIndex = pAttacker->GetMapIndex(); iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); // 다른 제국 사람인 경우 데미지 10% 감소 if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { int percent = 10; if (184 <= lMapIndex && lMapIndex <= 189) { if (LC_IsYMIR() == true) percent = 7; else percent = 9; } else { if (LC_IsYMIR() == true) percent = 8; else percent = 9; } dam = dam * percent / 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())) { // 10% 피해 증가 dam += dam / 10; } if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire())) { // 10% 피해 감소 dam -= dam / 10; } } //puAttr.Pop(); 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; } } // ------------------------ // 독일 프리미엄 모드 // ----------------------- if (LC_IsGermany() && pAttacker && pAttacker->IsPC()) { int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker); dam = dam * iDmgPct / 100; } // STONE SKIN : 피해 반으로 감소 if (IsMonster() && IsStoneSkinner()) { if (GetHPPct() < GetMobTable().bStoneSkinPoint) dam /= 2; } //PROF_UNIT puRest1("Rest1"); if (pAttacker) { // DEATH BLOW : 확률 적으로 4배 피해 (!? 현재 이벤트나 공성전용 몬스터만 사용함) if (pAttacker->IsMonster() && pAttacker->IsDeathBlower()) { if (pAttacker->IsDeathBlow()) { if (number(1, 4) == GetJob()) { IsDeathBlow = true; dam = dam * 4; } } } dam = BlueDragon_Damage(this, pAttacker, dam); BYTE damageFlag = 0; if (type == DAMAGE_TYPE_POISON) damageFlag = DAMAGE_POISON; 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) { if(pAttacker) { pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s", GetName(), dam, GetHP(), (GetHP() * 100) / GetMaxHP(), IsCritical ? "crit " : "", IsPenetrate ? "pene " : "", IsDeathBlow ? "deathblow " : ""); } ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s", pAttacker ? pAttacker->GetName() : 0, dam, GetHP(), (GetHP() * 100) / GetMaxHP(), 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()); } } // // !!!!!!!!! 실제 HP를 줄이는 부분 !!!!!!!!! // if (!cannot_dead) { PointChange(POINT_HP, -dam, false); } //puRest1.Pop(); //PROF_UNIT puRest2("Rest2"); if (pAttacker && dam > 0 && IsNPC()) { //PROF_UNIT puRest20("Rest20"); TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); if (it == m_map_kDamage.end()) { m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0))); it = m_map_kDamage.find(pAttacker->GetVID()); } else { it->second.iTotalDamage += dam; } //puRest20.Pop(); //PROF_UNIT puRest21("Rest21"); StartRecoveryEvent(); // 몬스터는 데미지를 입으면 회복을 시작한다. //puRest21.Pop(); //PROF_UNIT puRest22("Rest22"); UpdateAggrPointEx(pAttacker, type, dam, it->second); //puRest22.Pop(); } //puRest2.Pop(); //PROF_UNIT puRest3("Rest3"); 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; } static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) { // 레벨차 경험치 가감비율 iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp); // 외부 테스트 서버 경험치 3배 보너스 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; // 1.2배 (20%) // 아이템 경험치 두배 속성 if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) iExp += iExp * 30 / 100; // 1.3배 (30%) // 경험의 반지 (2시간짜리) 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 (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada()) { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 50 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 50 / 100); } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // 결혼 보너스 iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else if (/*LC_IsNewCIBN() || */LC_IsBrazil()) { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += iExp; } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += iExp; } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // 결혼 보너스 iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 20 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 20 / 100); } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/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 (speed_server) { iExp += iExp * CSpeedServerManager::ExpBonus(); } */ 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) ); } // 기획측 조정값 2005.04.21 현재 85% 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); } iExp = AdjustExpByLevel(to, iExp); to->PointChange(POINT_EXP, iExp, true); from->CreateFly(FLY_EXP, to); { LPCHARACTER you = to->GetMarryPartner(); // 부부가 서로 파티중이면 금슬이 오른다 if (you) { // 1억이 100% 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 } } } 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) { if (LC_IsYMIR()) total += ch->GetLevel(); else total += party_exp_distribute_table[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: if (LC_IsYMIR()) iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total); else iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[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()); } // 경험치 몰아주기 (파티가 획득한 경험치를 5% 빼서 먼저 줌) 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; LPCHARACTER CHARACTER::DistributeExp() { int iExpToDistribute = GetExp(); if (iExpToDistribute <= 0) return NULL; int iTotalDam = 0; LPCHARACTER pkChrMostAttacked = NULL; int iMostDam = 0; typedef std::vector<TDamageInfo> TDamageInfoTable; TDamageInfoTable damage_info_table; std::map<LPPARTY, TDamageInfo> map_party_damage; damage_info_table.reserve(m_map_kDamage.size()); TDamageMap::iterator it = m_map_kDamage.begin(); // 일단 주위에 없는 사람을 걸러 낸다. (50m) 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); // NPC가 때리기도 하나? -.-; 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<LPPARTY, TDamageInfo>::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.insert(std::make_pair(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.push_back(di); } } for (std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it) { damage_info_table.push_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) // 데미지 준게 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; // 제일 데미지를 많이 준 사람이 HP 회복을 한다. DistributeHP(pkChrMostAttacked); // 만두 시스템 { // 제일 데미지를 많이 준 사람이나 파티가 총 경험치의 20% + 자기가 때린만큼의 경험치를 먹는다. 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); // 100% 다 먹었으면 리턴한다. if (fPercent == 1.0f) return pkChrMostAttacked; di->Clear(); } { // 남은 80%의 경험치를 분배한다. 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) { return 0; } iArrowCount = MIN(iArrowCount, pkArrow->GetCount()); *ppkBow = pkBow; *ppkArrow = pkArrow; return iArrowCount; } void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount) { 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); /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK) m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/ } 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; } 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); // 데미지 계산 iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 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_MAGIC); // 타격치 계산부 끝 } break; case SKILL_YEONSA: // 연사 { //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100); 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_MARYUNG: //case SKILL_GUMHWAN: case SKILL_TUSOK: case SKILL_BIPABU: case SKILL_NOEJEON: case SKILL_GEOMPUNG: case SKILL_SANGONG: case SKILL_MAHWAN: case SKILL_PABEOB: //case SKILL_CURSE: { 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); // TODO 여러명에게 슉 슉 슉 하기 } break; case SKILL_YONGBI: { m_me->OnMove(true); } break; /*case SKILL_BUDONG: { m_me->OnMove(true); pkVictim->OnMove(); DWORD * pdw; DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw); pdw[0] = m_me->GetVID(); pdw[1] = pkVictim->GetVID(); event_create(budong_event_func, dwEI, PASSES_PER_SEC(1)); } 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.push_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; int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60; if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds)) 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) // 임시로 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.insert(TDamageMap::value_type(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) // 3초는 기다려야한다 return; if (pNewVictim == GetVictim()) { if (m_iMaxAggro < iNewAggro) { m_iMaxAggro = iNewAggro; return; } // Aggro가 감소한 경우 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; } } } here is it Spoiler #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 "dev_log.h" #include "ani.h" #include "BattleArena.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" DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp) { if (PLAYER_EXP_TABLE_MAX < 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<double>(exp); if (ret < 1.0) return 1; return static_cast<DWORD>(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()); //PROF_UNIT puAttack("Attack"); if (!CanMove()) return false; // CASTLE if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim)) return false; // CASTLE 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 (test_server && IsPC()) // sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet); 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); if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { return; } 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 (LC_IsYMIR()) // 천마 버전에서는 언제나 용신의 가호 아이템을 체크한다. { 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; } } else 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() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100); if (true == LC_IsYMIR()) { if (PLAYER_EXP_TABLE_MAX < GetLevel()) { iLoss = MIN(500000, iLoss); } else { iLoss = MIN(200000, iLoss); } } else if (true == LC_IsEurope()) { iLoss = MIN(800000, iLoss); } if (bTown) { if (g_iUseLocale) { iLoss = 0; } else { iLoss -= iLoss / 3; } } if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS)) iLoss /= 2; sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, 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<char_event_info*>( event->info ); if ( info == NULL ) { sys_err( "StunEvent> <Factor> Null pointer" ); return 0; } LPCHARACTER ch = info->ch; if (ch == NULL) { // <Factor> 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<char_event_info>(); 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<SCharDeadEventInfo*>(event->info); if ( info == NULL ) { sys_err( "dead_event> <Factor> 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; // 제3의 손 // END_OF_ADD_PREMIUM PIXEL_POSITION pos; if (!isAutoLoot) if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; 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); //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent); if (number(1, 100) > iPercent) return; int iGoldMultipler = GetGoldMultipler(); if (1 == number(1, 50000)) // 1/50000 확률로 돈이 10배 iGoldMultipler *= 10; else if (1 == number(1, 10000)) // 1/10000 확률로 돈이 5배 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)); // MOB_RANK가 BOSS보다 높으면 무조건 돈폭탄 if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0) { if (1 == number(1, iGold10DropPct)) iGoldMultipler *= 10; // 1% 확률로 돈 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); } // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음 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 } } } // 1% 확률로 돈을 10개 떨어 뜨린다. (10배 드롭임) 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; } // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음 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 && !LC_IsYMIR()) 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) // 왜구는 돈을 무조건 드롭 { PIXEL_POSITION pos; if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; 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; } //PROF_UNIT puReward("Reward"); LPCHARACTER pkAttacker = DistributeExp(); if (!pkAttacker) return; //PROF_UNIT pu1("r1"); 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); } } } //pu1.Pop(); if (!bItemDrop) return; PIXEL_POSITION pos = GetXYZ(); if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos)) return; // // 돈 드롭 // //PROF_UNIT pu2("r2"); if (test_server) sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName()); RewardGold(pkAttacker); //pu2.Pop(); // // 아이템 드롭 // //PROF_UNIT pu3("r3"); LPITEM item; static std::vector<LPITEM> 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); if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false) { 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 from %s", item->GetName(), pos.x, pos.y, GetName()); } else { int iItemIdx = s_vec_item.size() - 1; std::priority_queue<std::pair<int, LPCHARACTER> > 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<LPCHARACTER> v; while (!pq.empty() && pq.top().first * 10 >= total_dam) { v.push_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); // 10% 이하 데미지 준 사람끼리는 소유권없음 //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<LPCHARACTER>::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(); if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false) { item->SetOwnership(ch); } 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 (false == LC_IsYMIR()) { if (GetLevel() < 50) return; } if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { 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<std::pair<LPITEM, int> > 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; } if (bDropInventory) // Drop Inventory { std::vector<BYTE> vec_bSlots; for (i = 0; i < INVENTORY_MAX_NUM; ++i) if (GetInventoryItem(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { 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); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY)); } } else if (iAlignIndex == 8) isDropAllEquipments = true; } if (bDropEquipment) // Drop Equipment { std::vector<BYTE> vec_bSlots; for (i = 0; i < WEAR_MAX_NUM; ++i) if (GetWear(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { 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); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255); vec_item.push_back(std::make_pair(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.push_back(std::make_pair(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.push_back(std::make_pair(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.first; int window = vec_item.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; { if (IsHorseRiding()) { StopRiding(); } else if (GetMountVnum()) { RemoveAffect(AFFECT_MOUNT_BONUS); m_dwMountVnum = 0; UnEquipSpecialRideUniqueItem(); UpdatePacket(); } } if (!pkKiller && m_dwKillerPID) pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID); m_dwKillerPID = 0; // 반드시 초기화 해야함 DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE 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); } } //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) { // TODO : 입구로 날리는 코드를 넣어야 한다. } 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 (g_iUseLocale) { 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 (g_iUseLocale && 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++); } // // Dead 이벤트 생성, // // Dead 이벤트에서는 몬스터의 경우 몇초 후에 Destroy 되도록 해주며, // PC의 경우 3분 있다가 마을에서 나오도록 해 준다. 3분 내에는 유저로부터 // 마을에서 시작할 건지, 여기서 시작할 건지 결정을 받는다. 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<SCharDeadEventInfo>(); 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 != NULL) { m_pkExchange->Cancel(); } if (IsCubeOpen() == true) { Cube_close(this); } CShopManager::instance().StopShopping(this); CloseMyShop(); CloseSafebox(); if (true == IsMonster() && 2493 == GetMobTable().dwVnum) { if (NULL != pkKiller && NULL != 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)); } /* if (GetArenaObserverMode() == false && GetArena() != NULL) { GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo)); } */ } } // // CHARACTER::Damage 메소드는 this가 데미지를 입게 한다. // // Arguments // pAttacker : 공격자 // dam : 데미지 // EDamageType : 어떤 형식의 공격인가? // // Return value // true : dead // false : not dead yet // bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead { 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 (GetRaceNum() == 5001) { bool bDropMoney = false; int 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(); } } } } // 평타가 아닐 때는 공포 처리 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; } } int iCurHP = GetHP(); int iCurSP = GetSP(); bool IsCritical = false; bool IsPenetrate = false; bool IsDeathBlow = false; 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), }; //PROF_UNIT puAttr("Attr"); // // 마법형 스킬과, 레인지형 스킬은(궁자객) 크리티컬과, 관통공격 계산을 한다. // 원래는 하지 않아야 하는데 Nerf(다운밸런스)패치를 할 수 없어서 크리티컬과 // 관통공격의 원래 값을 쓰지 않고, /2 이상하여 적용한다. // // 무사 이야기가 많아서 밀리 스킬도 추가 // // 20091109 : 무사가 결과적으로 엄청나게 강해진 것으로 결론남, 독일 기준 무사 비율 70% 육박 // 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) // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20% iCriticalPct = 5 + (iCriticalPct - 10) / 4; else // 10보다 작으면 단순히 반으로 깎음, 10 = 5% 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<int>(pkSk->kPointPoly.Eval()); } } if (iPenetratePct >= 10) { // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20% iPenetratePct = 5 + (iPenetratePct - 10) / 4; } else { // 10보다 작으면 단순히 반으로 깎음, 10 = 5% 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); } } } } } // // 콤보 공격, 활 공격, 즉 평타 일 때만 속성값들을 계산을 한다. // 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; // NOTE: 공격자가 IMMUNE_REFLECT 속성을 갖고있다면 반사를 안 하는 게 // 아니라 1/3 데미지로 고정해서 들어가도록 기획에서 요청. 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<int>(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; } } // HP 스틸 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); } } } // SP 스틸 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); } } // 칠 때마다 HP회복 if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% 확률 { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; if (i) { CreateFly(FLY_HP_SMALL, pAttacker); pAttacker->PointChange(POINT_HP, i); } } // 칠 때마다 SP회복 if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% 확률 { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; 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)) { // POINT_MANASHIELD 는 작아질수록 좋다 int iDamageSPPart = dam / 3; int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100; int iSP = GetSP(); // SP가 있으면 무조건 데미지 절반 감소 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; } // // 제국으로 인한 보너스 (한국 올드 버전만 적용) // int iEmpire = GetEmpire(); long lMapIndex = GetMapIndex(); int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); if (LC_IsYMIR() == true) { if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { dam += (dam * 30) / 100; } } if (pAttacker->IsPC()) { iEmpire = pAttacker->GetEmpire(); lMapIndex = pAttacker->GetMapIndex(); iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); // 다른 제국 사람인 경우 데미지 10% 감소 if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { int percent = 10; if (184 <= lMapIndex && lMapIndex <= 189) { if (LC_IsYMIR() == true) percent = 7; else percent = 9; } else { if (LC_IsYMIR() == true) percent = 8; else percent = 9; } dam = dam * percent / 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())) { // 10% 피해 증가 dam += dam / 10; } if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire())) { // 10% 피해 감소 dam -= dam / 10; } } //puAttr.Pop(); 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; } } // ------------------------ // 독일 프리미엄 모드 // ----------------------- if (LC_IsGermany() && pAttacker && pAttacker->IsPC()) { int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker); dam = dam * iDmgPct / 100; } // STONE SKIN : 피해 반으로 감소 if (IsMonster() && IsStoneSkinner()) { if (GetHPPct() < GetMobTable().bStoneSkinPoint) dam /= 2; } //PROF_UNIT puRest1("Rest1"); if (pAttacker) { // DEATH BLOW : 확률 적으로 4배 피해 (!? 현재 이벤트나 공성전용 몬스터만 사용함) if (pAttacker->IsMonster() && pAttacker->IsDeathBlower()) { if (pAttacker->IsDeathBlow()) { if (number(1, 4) == GetJob()) { IsDeathBlow = true; dam = dam * 4; } } } dam = BlueDragon_Damage(this, pAttacker, dam); BYTE damageFlag = 0; if (type == DAMAGE_TYPE_POISON) damageFlag = DAMAGE_POISON; 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) { if(pAttacker) { pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s", GetName(), dam, GetHP(), (GetHP() * 100) / GetMaxHP(), IsCritical ? "crit " : "", IsPenetrate ? "pene " : "", IsDeathBlow ? "deathblow " : ""); } ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s", pAttacker ? pAttacker->GetName() : 0, dam, GetHP(), (GetHP() * 100) / GetMaxHP(), 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()); } } // // !!!!!!!!! 실제 HP를 줄이는 부분 !!!!!!!!! // if (!cannot_dead) { PointChange(POINT_HP, -dam, false); } //puRest1.Pop(); //PROF_UNIT puRest2("Rest2"); if (pAttacker && dam > 0 && IsNPC()) { //PROF_UNIT puRest20("Rest20"); TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); if (it == m_map_kDamage.end()) { m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0))); it = m_map_kDamage.find(pAttacker->GetVID()); } else { it->second.iTotalDamage += dam; } //puRest20.Pop(); //PROF_UNIT puRest21("Rest21"); StartRecoveryEvent(); // 몬스터는 데미지를 입으면 회복을 시작한다. //puRest21.Pop(); //PROF_UNIT puRest22("Rest22"); UpdateAggrPointEx(pAttacker, type, dam, it->second); //puRest22.Pop(); } //puRest2.Pop(); //PROF_UNIT puRest3("Rest3"); 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; } static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) { // 레벨차 경험치 가감비율 iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp); // 외부 테스트 서버 경험치 3배 보너스 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; // 1.2배 (20%) // 아이템 경험치 두배 속성 if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) iExp += iExp * 30 / 100; // 1.3배 (30%) // 경험의 반지 (2시간짜리) 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 (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada()) { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 50 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 50 / 100); } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // 결혼 보너스 iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else if (/*LC_IsNewCIBN() || */LC_IsBrazil()) { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += iExp; } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += iExp; } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // 결혼 보너스 iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else { // 아이템 몰: 경험치 결제 if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 20 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 20 / 100); } // PC방 아템 경치 보너스 if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/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 (speed_server) { iExp += iExp * CSpeedServerManager::ExpBonus(); } */ 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) ); } // 기획측 조정값 2005.04.21 현재 85% 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); } iExp = AdjustExpByLevel(to, iExp); to->PointChange(POINT_EXP, iExp, true); from->CreateFly(FLY_EXP, to); { LPCHARACTER you = to->GetMarryPartner(); // 부부가 서로 파티중이면 금슬이 오른다 if (you) { // 1억이 100% 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 } } } 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) { if (LC_IsYMIR()) total += ch->GetLevel(); else total += party_exp_distribute_table[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: if (LC_IsYMIR()) iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total); else iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[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()); } // 경험치 몰아주기 (파티가 획득한 경험치를 5% 빼서 먼저 줌) 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; LPCHARACTER CHARACTER::DistributeExp() { int iExpToDistribute = GetExp(); if (iExpToDistribute <= 0) return NULL; int iTotalDam = 0; LPCHARACTER pkChrMostAttacked = NULL; int iMostDam = 0; typedef std::vector<TDamageInfo> TDamageInfoTable; TDamageInfoTable damage_info_table; std::map<LPPARTY, TDamageInfo> map_party_damage; damage_info_table.reserve(m_map_kDamage.size()); TDamageMap::iterator it = m_map_kDamage.begin(); // 일단 주위에 없는 사람을 걸러 낸다. (50m) 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); // NPC가 때리기도 하나? -.-; 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<LPPARTY, TDamageInfo>::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.insert(std::make_pair(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.push_back(di); } } for (std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it) { damage_info_table.push_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) // 데미지 준게 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; // 제일 데미지를 많이 준 사람이 HP 회복을 한다. DistributeHP(pkChrMostAttacked); // 만두 시스템 { // 제일 데미지를 많이 준 사람이나 파티가 총 경험치의 20% + 자기가 때린만큼의 경험치를 먹는다. 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); // 100% 다 먹었으면 리턴한다. if (fPercent == 1.0f) return pkChrMostAttacked; di->Clear(); } { // 남은 80%의 경험치를 분배한다. 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) { return 0; } iArrowCount = MIN(iArrowCount, pkArrow->GetCount()); *ppkBow = pkBow; *ppkArrow = pkArrow; return iArrowCount; } void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount) { 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); /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK) m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/ } 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; } 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); // 데미지 계산 iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 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_MAGIC); // 타격치 계산부 끝 } break; case SKILL_YEONSA: // 연사 { //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100); 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_MARYUNG: //case SKILL_GUMHWAN: case SKILL_TUSOK: case SKILL_BIPABU: case SKILL_NOEJEON: case SKILL_GEOMPUNG: case SKILL_SANGONG: case SKILL_MAHWAN: case SKILL_PABEOB: //case SKILL_CURSE: { 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); // TODO 여러명에게 슉 슉 슉 하기 } break; case SKILL_YONGBI: { m_me->OnMove(true); } break; /*case SKILL_BUDONG: { m_me->OnMove(true); pkVictim->OnMove(); DWORD * pdw; DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw); pdw[0] = m_me->GetVID(); pdw[1] = pkVictim->GetVID(); event_create(budong_event_func, dwEI, PASSES_PER_SEC(1)); } 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.push_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; int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60; if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds)) 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) // 임시로 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.insert(TDamageMap::value_type(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) // 3초는 기다려야한다 return; if (pNewVictim == GetVictim()) { if (m_iMaxAggro < iNewAggro) { m_iMaxAggro = iNewAggro; return; } // Aggro가 감소한 경우 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; } } } here is it 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now