Hab folgendes Problem:
Hab den Thread mit dem
Expand Maximum Level
gemacht und beim compilen kommen folgende Error Meldungen:
Bitte melden Sie sich an, um diesen Link zu sehen.
Ich muss anscheinend in der char.cpp & char.h
ändern.
Hab ausch schon diverses verändert, Meldungen kommen aber trotzdem.
Irgendwo sind noch Fehler...
char.cpp
Code
- #include "stdafx.h"
- #include "../../common/teen_packet.h"
- #include "../../common/VnumHelper.h"
- #include "char.h"
- #include "config.h"
- #include "utils.h"
- #include "crc32.h"
- #include "char_manager.h"
- #include "desc_client.h"
- #include "desc_manager.h"
- #include "buffer_manager.h"
- #include "item_manager.h"
- #include "motion.h"
- #include "vector.h"
- #include "packet.h"
- #include "cmd.h"
- #include "fishing.h"
- #include "exchange.h"
- #include "battle.h"
- #include "affect.h"
- #include "shop.h"
- #include "shop_manager.h"
- #include "safebox.h"
- #include "regen.h"
- #include "pvp.h"
- #include "party.h"
- #include "start_position.h"
- #include "questmanager.h"
- #include "log.h"
- #include "p2p.h"
- #include "guild.h"
- #include "guild_manager.h"
- #include "dungeon.h"
- #include "messenger_manager.h"
- #include "unique_item.h"
- #include "priv_manager.h"
- #include "war_map.h"
- #include "xmas_event.h"
- #include "banword.h"
- #include "target.h"
- #include "wedding.h"
- #include "mob_manager.h"
- #include "mining.h"
- #include "monarch.h"
- #include "castle.h"
- #include "arena.h"
- #include "dev_log.h"
- #include "horsename_manager.h"
- #include "pcbang.h"
- #include "gm.h"
- #include "map_location.h"
- #include "BlueDragon_Binder.h"
- // #include "HackShield.h"
- #include "skill_power.h"
- #include "buff_on_attributes.h"
- #ifdef __PET_SYSTEM__
- #include "PetSystem.h"
- #endif
- #include "DragonSoul.h"
- extern const BYTE g_aBuffOnAttrPoints;
- extern bool RaceToJob(unsigned race, unsigned *ret_job);
- extern int g_nPortalLimitTime;
- extern int test_server;
- extern bool IS_SUMMONABLE_ZONE(int map_index); // char_item.cpp
- bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index);
- bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index)
- {
- switch (map_index)
- {
- case 301:
- case 302:
- case 303:
- case 304:
- if (ch->GetLevel() < 90)
- return false;
- }
- return true;
- }
- // <Factor> DynamicCharacterPtr member function definitions
- LPCHARACTER DynamicCharacterPtr::Get() const {
- LPCHARACTER p = NULL;
- if (is_pc) {
- p = CHARACTER_MANAGER::instance().FindByPID(id);
- } else {
- p = CHARACTER_MANAGER::instance().Find(id);
- }
- return p;
- }
- DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) {
- if (character == NULL) {
- Reset();
- return *this;
- }
- if (character->IsPC()) {
- is_pc = true;
- id = character->GetPlayerID();
- } else {
- is_pc = false;
- id = character->GetVID();
- }
- return *this;
- }
- CHARACTER::CHARACTER()
- {
- m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty);
- m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
- m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty);
- Initialize();
- }
- CHARACTER::~CHARACTER()
- {
- Destroy();
- }
- void CHARACTER::Initialize()
- {
- CEntity::Initialize(ENTITY_CHARACTER);
- m_bNoOpenedShop = true;
- m_bOpeningSafebox = false;
- m_fSyncTime = get_float_time()-3;
- m_dwPlayerID = 0;
- m_dwKillerPID = 0;
- m_iMoveCount = 0;
- CountDrops = 0;
- LastDropTime = 0;
- m_iLastPMPulse = 0;
- m_iPMCounter = 0;
- m_pkRegen = NULL;
- regen_id_ = 0;
- m_posRegen.x = m_posRegen.y = m_posRegen.z = 0;
- m_posStart.x = m_posStart.y = 0;
- m_posDest.x = m_posDest.y = 0;
- m_fRegenAngle = 0.0f;
- m_pkMobData = NULL;
- m_pkMobInst = NULL;
- m_pkShop = NULL;
- m_pkChrShopOwner = NULL;
- m_pkMyShop = NULL;
- m_pkExchange = NULL;
- m_pkParty = NULL;
- m_pkPartyRequestEvent = NULL;
- m_pGuild = NULL;
- m_pkChrTarget = NULL;
- m_pkMuyeongEvent = NULL;
- m_pkWarpNPCEvent = NULL;
- m_pkDeadEvent = NULL;
- m_pkStunEvent = NULL;
- m_pkSaveEvent = NULL;
- m_pkRecoveryEvent = NULL;
- m_pkTimedEvent = NULL;
- m_pkFishingEvent = NULL;
- m_pkWarpEvent = NULL;
- // MINING
- m_pkMiningEvent = NULL;
- // END_OF_MINING
- m_pkPoisonEvent = NULL;
- m_pkFireEvent = NULL;
- m_pkCheckSpeedHackEvent = NULL;
- m_speed_hack_count = 0;
- m_pkAffectEvent = NULL;
- m_afAffectFlag = TAffectFlag(0, 0);
- m_pkDestroyWhenIdleEvent = NULL;
- m_pkChrSyncOwner = NULL;
- memset(&m_points, 0, sizeof(m_points));
- memset(&m_pointsInstant, 0, sizeof(m_pointsInstant));
- memset(&m_quickslot, 0, sizeof(m_quickslot));
- m_bCharType = CHAR_TYPE_MONSTER;
- SetPosition(POS_STANDING);
- m_dwPlayStartTime = m_dwLastMoveTime = get_dword_time();
- GotoState(m_stateIdle);
- m_dwStateDuration = 1;
- m_dwLastAttackTime = get_dword_time() - 20000;
- m_bAddChrState = 0;
- m_pkChrStone = NULL;
- m_pkSafebox = NULL;
- m_iSafeboxSize = -1;
- m_iSafeboxLoadTime = 0;
- m_pkMall = NULL;
- m_iMallLoadTime = 0;
- m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
- m_lWarpMapIndex = 0;
- m_posExit.x = m_posExit.y = m_posExit.z = 0;
- m_lExitMapIndex = 0;
- m_pSkillLevels = NULL;
- m_dwMoveStartTime = 0;
- m_dwMoveDuration = 0;
- m_dwFlyTargetID = 0;
- m_dwNextStatePulse = 0;
- m_dwLastDeadTime = get_dword_time()-180000;
- m_bSkipSave = false;
- m_bItemLoaded = false;
- m_bHasPoisoned = false;
- m_pkDungeon = NULL;
- m_iEventAttr = 0;
- m_kAttackLog.dwVID = 0;
- m_kAttackLog.dwTime = 0;
- m_bNowWalking = m_bWalking = false;
- ResetChangeAttackPositionTime();
- m_bDetailLog = false;
- m_bMonsterLog = false;
- m_bDisableCooltime = false;
- m_iAlignment = 0;
- m_iRealAlignment = 0;
- m_iKillerModePulse = 0;
- m_bPKMode = PK_MODE_PEACE;
- m_dwQuestNPCVID = 0;
- m_dwQuestByVnum = 0;
- m_pQuestItem = NULL;
- m_szMobileAuth[0] = '\0';
- m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000;
- m_bUnderRefine = false;
- // REFINE_NPC
- m_dwRefineNPCVID = 0;
- // END_OF_REFINE_NPC
- m_dwPolymorphRace = 0;
- m_bStaminaConsume = false;
- ResetChainLightningIndex();
- m_dwMountVnum = 0;
- m_chHorse = NULL;
- m_chRider = NULL;
- m_pWarMap = NULL;
- m_pWeddingMap = NULL;
- m_bChatCounter = 0;
- ResetStopTime();
- m_dwLastVictimSetTime = get_dword_time() - 3000;
- m_iMaxAggro = -100;
- m_bSendHorseLevel = 0;
- m_bSendHorseHealthGrade = 0;
- m_bSendHorseStaminaGrade = 0;
- m_dwLoginPlayTime = 0;
- m_pkChrMarried = NULL;
- m_posSafeboxOpen.x = -1000;
- m_posSafeboxOpen.y = -1000;
- // EQUIP_LAST_SKILL_DELAY
- m_dwLastSkillTime = get_dword_time();
- // END_OF_EQUIP_LAST_SKILL_DELAY
- // MOB_SKILL_COOLTIME
- memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime));
- // END_OF_MOB_SKILL_COOLTIME
- m_isinPCBang = false;
- // ARENA
- m_pArena = NULL;
- m_nPotionLimit = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count");
- // END_ARENA
- //PREVENT_TRADE_WINDOW
- m_isOpenSafebox = 0;
- //END_PREVENT_TRADE_WINDOW
- //PREVENT_REFINE_HACK
- m_iRefineTime = 0;
- //END_PREVENT_REFINE_HACK
- //RESTRICT_USE_SEED_OR_MOONBOTTLE
- m_iSeedTime = 0;
- //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
- //PREVENT_PORTAL_AFTER_EXCHANGE
- m_iExchangeTime = 0;
- //END_PREVENT_PORTAL_AFTER_EXCHANGE
- //
- m_iSafeboxLoadTime = 0;
- m_iMyShopTime = 0;
- InitMC();
- m_deposit_pulse = 0;
- SET_OVER_TIME(this, OT_NONE);
- m_strNewName = "";
- m_known_guild.clear();
- m_dwLogOffInterval = 0;
- m_bComboSequence = 0;
- m_dwLastComboTime = 0;
- m_bComboIndex = 0;
- m_iComboHackCount = 0;
- m_dwSkipComboAttackByTime = 0;
- m_dwMountTime = 0;
- m_dwLastGoldDropTime = 0;
- // m_HackShieldCheckEvent = NULL;
- // m_HackShieldCheckMode = false;
- m_bIsLoadedAffect = false;
- cannot_dead = false;
- #ifdef __PET_SYSTEM__
- m_petSystem = 0;
- m_bIsPet = false;
- #endif
- m_fAttMul = 1.0f;
- m_fDamMul = 1.0f;
- m_pointsInstant.iDragonSoulActiveDeck = -1;
- memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime));
- m_iSyncHackCount = 0;
- }
- void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC)
- {
- static int s_crc = 172814;
- char crc_string[128+1];
- snprintf(crc_string, sizeof(crc_string), "%s%p%d", c_pszName, this, ++s_crc);
- m_vid = VID(vid, GetCRC32(crc_string, strlen(crc_string)));
- if (isPC)
- m_stName = c_pszName;
- }
- void CHARACTER::Destroy()
- {
- CloseMyShop();
- if (m_pkRegen)
- {
- if (m_pkDungeon) {
- // Dungeon regen may not be valid at this point
- if (m_pkDungeon->IsValidRegen(m_pkRegen, regen_id_)) {
- --m_pkRegen->count;
- }
- } else {
- // Is this really safe?
- --m_pkRegen->count;
- }
- m_pkRegen = NULL;
- }
- if (m_pkDungeon)
- {
- SetDungeon(NULL);
- }
- #ifdef __PET_SYSTEM__
- if (m_petSystem)
- {
- m_petSystem->Destroy();
- delete m_petSystem;
- m_petSystem = 0;
- }
- #endif
- HorseSummon(false);
- if (GetRider())
- GetRider()->ClearHorseInfo();
- // if( IsPC() )
- // {
- // if (isHackShieldEnable)
- // {
- // CHackShieldManager::instance().DeleteClientHandle(GetPlayerID());
- // }
- // }
- if (GetDesc())
- {
- GetDesc()->BindCharacter(NULL);
- // BindDesc(NULL);
- }
- if (m_pkExchange)
- m_pkExchange->Cancel();
- SetVictim(NULL);
- if (GetShop())
- {
- GetShop()->RemoveGuest(this);
- SetShop(NULL);
- }
- ClearStone();
- ClearSync();
- ClearTarget();
- if (NULL == m_pkMobData)
- {
- DragonSoul_CleanUp();
- ClearItem();
- }
- // <Factor> m_pkParty becomes NULL after CParty destructor call!
- LPPARTY party = m_pkParty;
- if (party)
- {
- if (party->GetLeaderPID() == GetVID() && !IsPC())
- {
- M2_DELETE(party);
- }
- else
- {
- party->Unlink(this);
- if (!IsPC())
- party->Quit(GetVID());
- }
- SetParty(NULL); // 안해도 되지만 안전하게.
- }
- if (m_pkMobInst)
- {
- M2_DELETE(m_pkMobInst);
- m_pkMobInst = NULL;
- }
- m_pkMobData = NULL;
- if (m_pkSafebox)
- {
- M2_DELETE(m_pkSafebox);
- m_pkSafebox = NULL;
- }
- if (m_pkMall)
- {
- M2_DELETE(m_pkMall);
- m_pkMall = NULL;
- }
- m_set_pkChrSpawnedBy.clear();
- StopMuyeongEvent();
- event_cancel(&m_pkWarpNPCEvent);
- event_cancel(&m_pkRecoveryEvent);
- event_cancel(&m_pkDeadEvent);
- event_cancel(&m_pkSaveEvent);
- event_cancel(&m_pkTimedEvent);
- event_cancel(&m_pkStunEvent);
- event_cancel(&m_pkFishingEvent);
- event_cancel(&m_pkPoisonEvent);
- event_cancel(&m_pkFireEvent);
- event_cancel(&m_pkPartyRequestEvent);
- //DELAYED_WARP
- event_cancel(&m_pkWarpEvent);
- event_cancel(&m_pkCheckSpeedHackEvent);
- //END_DELAYED_WARP
- // RECALL_DELAY
- //event_cancel(&m_pkRecallEvent);
- // END_OF_RECALL_DELAY
- // MINING
- event_cancel(&m_pkMiningEvent);
- // END_OF_MINING
- // StopHackShieldCheckCycle();
- for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
- {
- LPEVENT pkEvent = it->second;
- event_cancel(&pkEvent);
- }
- m_mapMobSkillEvent.clear();
- //event_cancel(&m_pkAffectEvent);
- ClearAffect();
- for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
- {
- if (NULL != it->second)
- {
- M2_DELETE(it->second);
- }
- }
- m_map_buff_on_attrs.clear();
- event_cancel(&m_pkDestroyWhenIdleEvent);
- if (m_pSkillLevels)
- {
- M2_DELETE_ARRAY(m_pSkillLevels);
- m_pSkillLevels = NULL;
- }
- CEntity::Destroy();
- if (GetSectree())
- GetSectree()->RemoveEntity(this);
- if (m_bMonsterLog)
- CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
- }
- const char * CHARACTER::GetName() const
- {
- return m_stName.empty() ? (m_pkMobData ? m_pkMobData->m_table.szLocaleName : "") : m_stName.c_str();
- }
- void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount)
- {
- if (GetPart(PART_MAIN) > 2)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("갑옷을 벗어야 개인 상점을 열 수 있습니다."));
- return;
- }
- if (GetMyShop()) // 이미 샵이 열려 있으면 닫는다.
- {
- CloseMyShop();
- return;
- }
- // 진행중인 퀘스트가 있으면 상점을 열 수 없다.
- quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
- // GetPCForce는 NULL일 수 없으므로 따로 확인하지 않음
- if (pPC->IsRunning())
- return;
- if (bItemCount == 0)
- return;
- int64_t nTotalMoney = 0;
- for (int n = 0; n < bItemCount; ++n)
- {
- nTotalMoney += static_cast<int64_t>((pTable+n)->price);
- }
- nTotalMoney += static_cast<int64_t>(GetGold());
- if (GOLD_MAX <= nTotalMoney)
- {
- sys_err("[OVERFLOW_GOLD] Overflow (GOLD_MAX) id %u name %s", GetPlayerID(), GetName());
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20억 냥을 초과하여 상점을 열수가 없습니다"));
- return;
- }
- char szSign[SHOP_SIGN_MAX_LEN+1];
- strlcpy(szSign, c_pszSign, sizeof(szSign));
- m_stShopSign = szSign;
- if (m_stShopSign.length() == 0)
- return;
- if (LC_IsCanada() == false)
- {
- if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("비속어나 은어가 포함된 상점 이름으로 상점을 열 수 없습니다."));
- return;
- }
- }
- // MYSHOP_PRICE_LIST
- std::map<DWORD, DWORD> itemkind; // 아이템 종류별 가격, first: vnum, second: 단일 수량 가격
- // END_OF_MYSHOP_PRICE_LIST
- std::set<TItemPos> cont;
- for (BYTE i = 0; i < bItemCount; ++i)
- {
- if (cont.find((pTable + i)->pos) != cont.end())
- {
- sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName());
- return;
- }
- // ANTI_GIVE, ANTI_MYSHOP check
- LPITEM pkItem = GetItem((pTable + i)->pos);
- if (pkItem)
- {
- const TItemTable * item_table = pkItem->GetProto();
- if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP)))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("유료화 아이템은 개인상점에서 판매할 수 없습니다."));
- return;
- }
- if (pkItem->IsEquipped() == true)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("장비중인 아이템은 개인상점에서 판매할 수 없습니다."));
- return;
- }
- if (true == pkItem->isLocked())
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용중인 아이템은 개인상점에서 판매할 수 없습니다."));
- return;
- }
- // MYSHOP_PRICE_LIST
- itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount();
- // END_OF_MYSHOP_PRICE_LIST
- }
- cont.insert((pTable + i)->pos);
- }
- // MYSHOP_PRICE_LIST
- // 보따리 개수를 감소시킨다.
- if (CountSpecifyItem(71049)) { // 비단 보따리는 없애지 않고 가격정보를 저장한다.
- //
- // 아이템 가격정보를 저장하기 위해 아이템 가격정보 패킷을 만들어 DB 캐시에 보낸다.
- //
- TPacketMyshopPricelistHeader header;
- TItemPriceInfo info;
- header.dwOwnerID = GetPlayerID();
- header.byCount = itemkind.size();
- TEMP_BUFFER buf;
- buf.write(&header, sizeof(header));
- for (itertype(itemkind) it = itemkind.begin(); it != itemkind.end(); ++it)
- {
- info.dwVnum = it->first;
- info.dwPrice = it->second;
- buf.write(&info, sizeof(info));
- }
- db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_UPDATE, 0, buf.read_peek(), buf.size());
- }
- // END_OF_MYSHOP_PRICE_LIST
- else if (CountSpecifyItem(50200))
- RemoveSpecifyItem(50200, 1);
- else
- return; // 보따리가 없으면 중단.
- if (m_pkExchange)
- m_pkExchange->Cancel();
- TPacketGCShopSign p;
- p.bHeader = HEADER_GC_SHOP_SIGN;
- p.dwVID = GetVID();
- strlcpy(p.szSign, c_pszSign, sizeof(p.szSign));
- PacketAround(&p, sizeof(TPacketGCShopSign));
- m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount);
- if (IsPolymorphed() == true)
- {
- RemoveAffect(AFFECT_POLYMORPH);
- }
- if (GetHorse())
- {
- HorseSummon( false, true );
- }
- // new mount 이용 중에, 개인 상점 열면 자동 unmount
- // StopRiding으로 뉴마운트까지 처리하면 좋은데 왜 그렇게 안해놨는지 알 수 없다.
- else if (GetMountVnum())
- {
- RemoveAffect(AFFECT_MOUNT);
- RemoveAffect(AFFECT_MOUNT_BONUS);
- }
- //if (!LC_IsNewCIBN())
- SetPolymorph(30000, true);
- }
- void CHARACTER::CloseMyShop()
- {
- if (GetMyShop())
- {
- m_stShopSign.clear();
- CShopManager::instance().DestroyPCShop(this);
- m_pkMyShop = NULL;
- TPacketGCShopSign p;
- p.bHeader = HEADER_GC_SHOP_SIGN;
- p.dwVID = GetVID();
- p.szSign[0] = '\0';
- PacketAround(&p, sizeof(p));
- //if (!LC_IsNewCIBN())
- SetPolymorph(GetJob(), true);
- }
- }
- void EncodeMovePacket(TPacketGCMove & pack, DWORD dwVID, BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, BYTE bRot)
- {
- pack.bHeader = HEADER_GC_MOVE;
- pack.bFunc = bFunc;
- pack.bArg = bArg;
- pack.dwVID = dwVID;
- pack.dwTime = dwTime ? dwTime : get_dword_time();
- pack.bRot = bRot;
- pack.lX = x;
- pack.lY = y;
- pack.dwDuration = dwDuration;
- }
- void CHARACTER::RestartAtSamePos()
- {
- if (m_bIsObserver)
- return;
- EncodeRemovePacket(this);
- EncodeInsertPacket(this);
- ENTITY_MAP::iterator it = m_map_view.begin();
- while (it != m_map_view.end())
- {
- LPENTITY entity = (it++)->first;
- EncodeRemovePacket(entity);
- if (!m_bIsObserver)
- EncodeInsertPacket(entity);
- if( entity->IsType(ENTITY_CHARACTER) )
- {
- LPCHARACTER lpChar = (LPCHARACTER)entity;
- if( lpChar->IsPC() || lpChar->IsNPC() || lpChar->IsMonster() )
- {
- if (!entity->IsObserverMode())
- entity->EncodeInsertPacket(this);
- }
- }
- else
- {
- if( !entity->IsObserverMode())
- {
- entity->EncodeInsertPacket(this);
- }
- }
- }
- }
- // Entity에 내가 나타났다고 패킷을 보낸다.
- void CHARACTER::EncodeInsertPacket(LPENTITY entity)
- {
- LPDESC d;
- if (!(d = entity->GetDesc()))
- return;
- // 길드이름 버그 수정 코드
- LPCHARACTER ch = (LPCHARACTER) entity;
- ch->SendGuildName(GetGuild());
- // 길드이름 버그 수정 코드
- TPacketGCCharacterAdd pack;
- pack.header = HEADER_GC_CHARACTER_ADD;
- pack.dwVID = m_vid;
- pack.bType = GetCharType();
- pack.angle = GetRotation();
- pack.x = GetX();
- pack.y = GetY();
- pack.z = GetZ();
- pack.wRaceNum = GetRaceNum();
- if (IsPet())
- {
- pack.bMovingSpeed = 150;
- }
- else
- {
- pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED);
- }
- pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED);
- pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
- pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
- pack.bStateFlag = m_bAddChrState;
- int iDur = 0;
- if (m_posDest.x != pack.x || m_posDest.y != pack.y)
- {
- iDur = (m_dwMoveStartTime + m_dwMoveDuration) - get_dword_time();
- if (iDur <= 0)
- {
- pack.x = m_posDest.x;
- pack.y = m_posDest.y;
- }
- }
- d->Packet(&pack, sizeof(pack));
- if (IsPC() == true || m_bCharType == CHAR_TYPE_NPC)
- {
- TPacketGCCharacterAdditionalInfo addPacket;
- memset(&addPacket, 0, sizeof(TPacketGCCharacterAdditionalInfo));
- addPacket.header = HEADER_GC_CHAR_ADDITIONAL_INFO;
- addPacket.dwVID = m_vid;
- addPacket.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
- addPacket.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
- addPacket.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
- addPacket.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
- addPacket.bPKMode = m_bPKMode;
- addPacket.dwMountVnum = GetMountVnum();
- addPacket.bEmpire = m_bEmpire;
- if (IsPC() == true && (LC_IsEurope() == true || LC_IsCanada() == true || LC_IsSingapore() == true))
- {
- addPacket.dwLevel = GetLevel();
- }
- else
- {
- addPacket.dwLevel = 0;
- }
- if (false)
- {
- LPCHARACTER ch = (LPCHARACTER) entity;
- if (GetEmpire() == ch->GetEmpire() || ch->GetGMLevel() > GM_PLAYER || m_bCharType == CHAR_TYPE_NPC)
- {
- goto show_all_info;
- }
- else
- {
- memset(addPacket.name, 0, CHARACTER_NAME_MAX_LEN);
- addPacket.dwGuildID = 0;
- addPacket.sAlignment = 0;
- }
- }
- else
- {
- show_all_info:
- strlcpy(addPacket.name, GetName(), sizeof(addPacket.name));
- if (GetGuild() != NULL)
- {
- addPacket.dwGuildID = GetGuild()->GetID();
- }
- else
- {
- addPacket.dwGuildID = 0;
- }
- addPacket.sAlignment = m_iAlignment / 10;
- }
- d->Packet(&addPacket, sizeof(TPacketGCCharacterAdditionalInfo));
- }
- if (iDur)
- {
- TPacketGCMove pack;
- EncodeMovePacket(pack, GetVID(), FUNC_MOVE, 0, m_posDest.x, m_posDest.y, iDur, 0, (BYTE) (GetRotation() / 5));
- d->Packet(&pack, sizeof(pack));
- TPacketGCWalkMode p;
- p.vid = GetVID();
- p.header = HEADER_GC_WALK_MODE;
- p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
- d->Packet(&p, sizeof(p));
- }
- if (entity->IsType(ENTITY_CHARACTER) && GetDesc())
- {
- LPCHARACTER ch = (LPCHARACTER) entity;
- if (ch->IsWalking())
- {
- TPacketGCWalkMode p;
- p.vid = ch->GetVID();
- p.header = HEADER_GC_WALK_MODE;
- p.mode = ch->m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
- GetDesc()->Packet(&p, sizeof(p));
- }
- }
- if (GetMyShop())
- {
- TPacketGCShopSign p;
- p.bHeader = HEADER_GC_SHOP_SIGN;
- p.dwVID = GetVID();
- strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
- d->Packet(&p, sizeof(TPacketGCShopSign));
- }
- if (entity->IsType(ENTITY_CHARACTER))
- {
- sys_log(3, "EntityInsert %s (RaceNum %d) (%d %d) TO %s",
- GetName(), GetRaceNum(), GetX() / SECTREE_SIZE, GetY() / SECTREE_SIZE, ((LPCHARACTER)entity)->GetName());
- }
- }
- void CHARACTER::EncodeRemovePacket(LPENTITY entity)
- {
- if (entity->GetType() != ENTITY_CHARACTER)
- return;
- LPDESC d;
- if (!(d = entity->GetDesc()))
- return;
- TPacketGCCharacterDelete pack;
- pack.header = HEADER_GC_CHARACTER_DEL;
- pack.id = m_vid;
- d->Packet(&pack, sizeof(TPacketGCCharacterDelete));
- if (entity->IsType(ENTITY_CHARACTER))
- sys_log(3, "EntityRemove %s(%d) FROM %s", GetName(), (DWORD) m_vid, ((LPCHARACTER) entity)->GetName());
- }
- void CHARACTER::UpdatePacket()
- {
- if (GetSectree() == NULL) return;
- TPacketGCCharacterUpdate pack;
- TPacketGCCharacterUpdate pack2;
- pack.header = HEADER_GC_CHARACTER_UPDATE;
- pack.dwVID = m_vid;
- pack.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
- pack.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
- pack.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
- pack.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
- pack.bMovingSpeed = GetLimitPoint(POINT_MOV_SPEED);
- pack.bAttackSpeed = GetLimitPoint(POINT_ATT_SPEED);
- pack.bStateFlag = m_bAddChrState;
- pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
- pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
- pack.dwGuildID = 0;
- pack.sAlignment = m_iAlignment / 10;
- pack.bPKMode = m_bPKMode;
- if (GetGuild())
- pack.dwGuildID = GetGuild()->GetID();
- pack.dwMountVnum = GetMountVnum();
- pack2 = pack;
- pack2.dwGuildID = 0;
- pack2.sAlignment = 0;
- if (false)
- {
- if (m_bIsObserver != true)
- {
- for (ENTITY_MAP::iterator iter = m_map_view.begin(); iter != m_map_view.end(); iter++)
- {
- LPENTITY pEntity = iter->first;
- if (pEntity != NULL)
- {
- if (pEntity->IsType(ENTITY_CHARACTER) == true)
- {
- if (pEntity->GetDesc() != NULL)
- {
- LPCHARACTER pChar = (LPCHARACTER)pEntity;
- if (GetEmpire() == pChar->GetEmpire() || pChar->GetGMLevel() > GM_PLAYER)
- {
- pEntity->GetDesc()->Packet(&pack, sizeof(pack));
- }
- else
- {
- pEntity->GetDesc()->Packet(&pack2, sizeof(pack2));
- }
- }
- }
- else
- {
- if (pEntity->GetDesc() != NULL)
- {
- pEntity->GetDesc()->Packet(&pack, sizeof(pack));
- }
- }
- }
- }
- }
- if (GetDesc() != NULL)
- {
- GetDesc()->Packet(&pack, sizeof(pack));
- }
- }
- else
- {
- PacketAround(&pack, sizeof(pack));
- }
- }
- LPCHARACTER CHARACTER::FindCharacterInView(const char * c_pszName, bool bFindPCOnly)
- {
- ENTITY_MAP::iterator it = m_map_view.begin();
- for (; it != m_map_view.end(); ++it)
- {
- if (!it->first->IsType(ENTITY_CHARACTER))
- continue;
- LPCHARACTER tch = (LPCHARACTER) it->first;
- if (bFindPCOnly && tch->IsNPC())
- continue;
- if (!strcasecmp(tch->GetName(), c_pszName))
- return (tch);
- }
- return NULL;
- }
- void CHARACTER::SetPosition(int pos)
- {
- if (pos == POS_STANDING)
- {
- REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
- REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
- event_cancel(&m_pkDeadEvent);
- event_cancel(&m_pkStunEvent);
- }
- else if (pos == POS_DEAD)
- SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
- if (!IsStone())
- {
- switch (pos)
- {
- case POS_FIGHTING:
- if (!IsState(m_stateBattle))
- MonsterLog("[BATTLE] 싸우는 상태");
- GotoState(m_stateBattle);
- break;
- default:
- if (!IsState(m_stateIdle))
- MonsterLog("[IDLE] 쉬는 상태");
- GotoState(m_stateIdle);
- break;
- }
- }
- m_pointsInstant.position = pos;
- }
- void CHARACTER::Save()
- {
- if (!m_bSkipSave)
- CHARACTER_MANAGER::instance().DelayedSave(this);
- }
- void CHARACTER::CreatePlayerProto(TPlayerTable & tab)
- {
- memset(&tab, 0, sizeof(TPlayerTable));
- if (GetNewName().empty())
- {
- strlcpy(tab.name, GetName(), sizeof(tab.name));
- }
- else
- {
- strlcpy(tab.name, GetNewName().c_str(), sizeof(tab.name));
- }
- strlcpy(tab.ip, GetDesc()->GetHostName(), sizeof(tab.ip));
- tab.id = m_dwPlayerID;
- tab.voice = GetPoint(POINT_VOICE);
- tab.level = GetLevel();
- tab.level_step = GetPoint(POINT_LEVEL_STEP);
- tab.exp = GetExp();
- tab.gold = GetGold();
- tab.job = m_points.job;
- tab.part_base = m_pointsInstant.bBasePart;
- tab.skill_group = m_points.skill_group;
- DWORD dwPlayedTime = (get_dword_time() - m_dwPlayStartTime);
- if (dwPlayedTime > 60000)
- {
- if (GetSectree() && !GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
- {
- if (GetRealAlignment() < 0)
- {
- if (IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_TIME))
- UpdateAlignment(120 * (dwPlayedTime / 60000));
- else
- UpdateAlignment(60 * (dwPlayedTime / 60000));
- }
- else
- UpdateAlignment(5 * (dwPlayedTime / 60000));
- }
- SetRealPoint(POINT_PLAYTIME, GetRealPoint(POINT_PLAYTIME) + dwPlayedTime / 60000);
- ResetPlayTime(dwPlayedTime % 60000);
- }
- tab.playtime = GetRealPoint(POINT_PLAYTIME);
- tab.lAlignment = m_iRealAlignment;
- if (m_posWarp.x != 0 || m_posWarp.y != 0)
- {
- tab.x = m_posWarp.x;
- tab.y = m_posWarp.y;
- tab.z = 0;
- tab.lMapIndex = m_lWarpMapIndex;
- }
- else
- {
- tab.x = GetX();
- tab.y = GetY();
- tab.z = GetZ();
- tab.lMapIndex = GetMapIndex();
- }
- if (m_lExitMapIndex == 0)
- {
- tab.lExitMapIndex = tab.lMapIndex;
- tab.lExitX = tab.x;
- tab.lExitY = tab.y;
- }
- else
- {
- tab.lExitMapIndex = m_lExitMapIndex;
- tab.lExitX = m_posExit.x;
- tab.lExitY = m_posExit.y;
- }
- sys_log(0, "SAVE: %s %dx%d", GetName(), tab.x, tab.y);
- tab.st = GetRealPoint(POINT_ST);
- tab.ht = GetRealPoint(POINT_HT);
- tab.dx = GetRealPoint(POINT_DX);
- tab.iq = GetRealPoint(POINT_IQ);
- tab.stat_point = GetPoint(POINT_STAT);
- tab.skill_point = GetPoint(POINT_SKILL);
- tab.sub_skill_point = GetPoint(POINT_SUB_SKILL);
- tab.horse_skill_point = GetPoint(POINT_HORSE_SKILL);
- tab.stat_reset_count = GetPoint(POINT_STAT_RESET_COUNT);
- tab.hp = GetHP();
- tab.sp = GetSP();
- tab.stamina = GetStamina();
- tab.sRandomHP = m_points.iRandomHP;
- tab.sRandomSP = m_points.iRandomSP;
- for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
- tab.quickslot[i] = m_quickslot[i];
- if (m_stMobile.length() && !*m_szMobileAuth)
- strlcpy(tab.szMobile, m_stMobile.c_str(), sizeof(tab.szMobile));
- thecore_memcpy(tab.parts, m_pointsInstant.parts, sizeof(tab.parts));
- // REMOVE_REAL_SKILL_LEVLES
- thecore_memcpy(tab.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
- // END_OF_REMOVE_REAL_SKILL_LEVLES
- tab.horse = GetHorseData();
- }
- void CHARACTER::SaveReal()
- {
- if (m_bSkipSave)
- return;
- if (!GetDesc())
- {
- sys_err("Character::Save : no descriptor when saving (name: %s)", GetName());
- return;
- }
- TPlayerTable table;
- CreatePlayerProto(table);
- db_clientdesc->DBPacket(HEADER_GD_PLAYER_SAVE, GetDesc()->GetHandle(), &table, sizeof(TPlayerTable));
- quest::PC * pkQuestPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
- if (!pkQuestPC)
- sys_err("CHARACTER::Save : null quest::PC pointer! (name %s)", GetName());
- else
- {
- pkQuestPC->Save();
- }
- marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
- if (pMarriage)
- pMarriage->Save();
- }
- void CHARACTER::FlushDelayedSaveItem()
- {
- // 저장 안된 소지품을 전부 저장시킨다.
- LPITEM item;
- for (int i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i)
- if ((item = GetInventoryItem(i)))
- ITEM_MANAGER::instance().FlushDelayedSave(item);
- }
- void CHARACTER::Disconnect(const char * c_pszReason)
- {
- assert(GetDesc() != NULL);
- sys_log(0, "DISCONNECT: %s (%s)", GetName(), c_pszReason ? c_pszReason : "unset" );
- if (GetShop())
- {
- GetShop()->RemoveGuest(this);
- SetShop(NULL);
- }
- if (GetArena() != NULL)
- {
- GetArena()->OnDisconnect(GetPlayerID());
- }
- if (GetParty() != NULL)
- {
- GetParty()->UpdateOfflineState(GetPlayerID());
- }
- marriage::CManager::instance().Logout(this);
- // P2P Logout
- TPacketGGLogout p;
- p.bHeader = HEADER_GG_LOGOUT;
- strlcpy(p.szName, GetName(), sizeof(p.szName));
- P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogout));
- char buf[51];
- snprintf(buf, sizeof(buf), "%s %d %d %ld %d",
- inet_ntoa(GetDesc()->GetAddr().sin_addr), GetGold(), g_bChannel, GetMapIndex(), GetAlignment());
- LogManager::instance().CharLog(this, 0, "LOGOUT", buf);
- if (LC_IsYMIR() || LC_IsKorea() || LC_IsBrazil())
- {
- long playTime = GetRealPoint(POINT_PLAYTIME) - m_dwLoginPlayTime;
- LogManager::instance().LoginLog(false, GetDesc()->GetAccountTable().id, GetPlayerID(), GetLevel(), GetJob(), playTime);
- if (LC_IsBrazil() != true)
- CPCBangManager::instance().Log(GetDesc()->GetHostName(), GetPlayerID(), playTime);
- }
- if (m_pWarMap)
- SetWarMap(NULL);
- if (m_pWeddingMap)
- {
- SetWeddingMap(NULL);
- }
- if (GetGuild())
- GetGuild()->LogoutMember(this);
- quest::CQuestManager::instance().LogoutPC(this);
- if (GetParty())
- GetParty()->Unlink(this);
- // 죽었을 때 접속끊으면 경험치 줄게 하기
- if (IsStun() || IsDead())
- {
- DeathPenalty(0);
- PointChange(POINT_HP, 50 - GetHP());
- }
- if (!CHARACTER_MANAGER::instance().FlushDelayedSave(this))
- {
- SaveReal();
- }
- FlushDelayedSaveItem();
- SaveAffect();
- m_bIsLoadedAffect = false;
- m_bSkipSave = true; // 이 이후에는 더이상 저장하면 안된다.
- quest::CQuestManager::instance().DisconnectPC(this);
- CloseSafebox();
- CloseMall();
- CPVPManager::instance().Disconnect(this);
- CTargetManager::instance().Logout(GetPlayerID());
- MessengerManager::instance().Logout(GetName());
- if (g_TeenDesc)
- {
- int offset = 0;
- char buf[245] = {0};
- buf[0] = HEADER_GT_LOGOUT;
- offset += 1;
- memset(buf+offset, 0x00, 2);
- offset += 2;
- TAccountTable &acc_table = GetDesc()->GetAccountTable();
- memcpy(buf+offset, &acc_table.id, 4);
- offset += 4;
- g_TeenDesc->Packet(buf, offset);
- }
- if (GetDesc())
- {
- GetDesc()->BindCharacter(NULL);
- // BindDesc(NULL);
- }
- M2_DESTROY_CHARACTER(this);
- }
- bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMotion/* = false */)
- {
- LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
- if (!sectree)
- {
- sys_log(0, "cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex);
- return false;
- }
- SetMapIndex(lMapIndex);
- bool bChangeTree = false;
- if (!GetSectree() || GetSectree() != sectree)
- bChangeTree = true;
- if (bChangeTree)
- {
- if (GetSectree())
- GetSectree()->RemoveEntity(this);
- ViewCleanup();
- }
- if (!IsNPC())
- {
- sys_log(0, "SHOW: %s %dx%dx%d", GetName(), x, y, z);
- if (GetStamina() < GetMaxStamina())
- StartAffectEvent();
- }
- else if (m_pkMobData)
- {
- m_pkMobInst->m_posLastAttacked.x = x;
- m_pkMobInst->m_posLastAttacked.y = y;
- m_pkMobInst->m_posLastAttacked.z = z;
- }
- if (bShowSpawnMotion)
- {
- SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
- m_afAffectFlag.Set(AFF_SPAWN);
- }
- SetXYZ(x, y, z);
- m_posDest.x = x;
- m_posDest.y = y;
- m_posDest.z = z;
- m_posStart.x = x;
- m_posStart.y = y;
- m_posStart.z = z;
- if (bChangeTree)
- {
- EncodeInsertPacket(this);
- sectree->InsertEntity(this);
- UpdateSectree();
- }
- else
- {
- ViewReencode();
- sys_log(0, " in same sectree");
- }
- REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
- SetValidComboInterval(0);
- return true;
- }
- // BGM_INFO
- struct BGMInfo
- {
- std::string name;
- float vol;
- };
- typedef std::map<unsigned, BGMInfo> BGMInfoMap;
- static BGMInfoMap gs_bgmInfoMap;
- static bool gs_bgmVolEnable = false;
- void CHARACTER_SetBGMVolumeEnable()
- {
- gs_bgmVolEnable = true;
- sys_log(0, "bgm_info.set_bgm_volume_enable");
- }
- void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol)
- {
- BGMInfo newInfo;
- newInfo.name = name;
- newInfo.vol = vol;
- gs_bgmInfoMap[mapIndex] = newInfo;
- sys_log(0, "bgm_info.add_info(%d, '%s', %f)", mapIndex, name, vol);
- }
- const BGMInfo& CHARACTER_GetBGMInfo(unsigned mapIndex)
- {
- BGMInfoMap::iterator f = gs_bgmInfoMap.find(mapIndex);
- if (gs_bgmInfoMap.end() == f)
- {
- static BGMInfo s_empty = {"", 0.0f};
- return s_empty;
- }
- return f->second;
- }
- bool CHARACTER_IsBGMVolumeEnable()
- {
- return gs_bgmVolEnable;
- }
- // END_OF_BGM_INFO
- void CHARACTER::MainCharacterPacket()
- {
- const unsigned mapIndex = GetMapIndex();
- const BGMInfo& bgmInfo = CHARACTER_GetBGMInfo(mapIndex);
- // SUPPORT_BGM
- if (!bgmInfo.name.empty())
- {
- if (CHARACTER_IsBGMVolumeEnable())
- {
- sys_log(1, "bgm_info.play_bgm_vol(%d, name='%s', vol=%f)", mapIndex, bgmInfo.name.c_str(), bgmInfo.vol);
- TPacketGCMainCharacter4_BGM_VOL mainChrPacket;
- mainChrPacket.header = HEADER_GC_MAIN_CHARACTER4_BGM_VOL;
- mainChrPacket.dwVID = m_vid;
- mainChrPacket.wRaceNum = GetRaceNum();
- mainChrPacket.lx = GetX();
- mainChrPacket.ly = GetY();
- mainChrPacket.lz = GetZ();
- mainChrPacket.empire = GetDesc()->GetEmpire();
- mainChrPacket.skill_group = GetSkillGroup();
- strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
- mainChrPacket.fBGMVol = bgmInfo.vol;
- strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
- GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter4_BGM_VOL));
- }
- else
- {
- sys_log(1, "bgm_info.play(%d, '%s')", mapIndex, bgmInfo.name.c_str());
- TPacketGCMainCharacter3_BGM mainChrPacket;
- mainChrPacket.header = HEADER_GC_MAIN_CHARACTER3_BGM;
- mainChrPacket.dwVID = m_vid;
- mainChrPacket.wRaceNum = GetRaceNum();
- mainChrPacket.lx = GetX();
- mainChrPacket.ly = GetY();
- mainChrPacket.lz = GetZ();
- mainChrPacket.empire = GetDesc()->GetEmpire();
- mainChrPacket.skill_group = GetSkillGroup();
- strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
- strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
- GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter3_BGM));
- }
- //if (m_stMobile.length())
- // ChatPacket(CHAT_TYPE_COMMAND, "sms");
- }
- // END_OF_SUPPORT_BGM
- else
- {
- sys_log(0, "bgm_info.play(%d, DEFAULT_BGM_NAME)", mapIndex);
- TPacketGCMainCharacter pack;
- pack.header = HEADER_GC_MAIN_CHARACTER;
- pack.dwVID = m_vid;
- pack.wRaceNum = GetRaceNum();
- pack.lx = GetX();
- pack.ly = GetY();
- pack.lz = GetZ();
- pack.empire = GetDesc()->GetEmpire();
- pack.skill_group = GetSkillGroup();
- strlcpy(pack.szName, GetName(), sizeof(pack.szName));
- GetDesc()->Packet(&pack, sizeof(TPacketGCMainCharacter));
- if (m_stMobile.length())
- ChatPacket(CHAT_TYPE_COMMAND, "sms");
- }
- }
- void CHARACTER::PointsPacket()
- {
- if (!GetDesc())
- return;
- TPacketGCPoints pack;
- pack.header = HEADER_GC_CHARACTER_POINTS;
- pack.points[POINT_LEVEL] = GetLevel();
- pack.points[POINT_EXP] = GetExp();
- pack.points[POINT_NEXT_EXP] = GetNextExp();
- pack.points[POINT_HP] = GetHP();
- pack.points[POINT_MAX_HP] = GetMaxHP();
- pack.points[POINT_SP] = GetSP();
- pack.points[POINT_MAX_SP] = GetMaxSP();
- pack.points[POINT_GOLD] = GetGold();
- pack.points[POINT_STAMINA] = GetStamina();
- pack.points[POINT_MAX_STAMINA] = GetMaxStamina();
- for (int i = POINT_ST; i < POINT_MAX_NUM; ++i)
- pack.points[i] = GetPoint(i);
- GetDesc()->Packet(&pack, sizeof(TPacketGCPoints));
- }
- bool CHARACTER::ChangeSex()
- {
- int src_race = GetRaceNum();
- switch (src_race)
- {
- case MAIN_RACE_WARRIOR_M:
- m_points.job = MAIN_RACE_WARRIOR_W;
- break;
- case MAIN_RACE_WARRIOR_W:
- m_points.job = MAIN_RACE_WARRIOR_M;
- break;
- case MAIN_RACE_ASSASSIN_M:
- m_points.job = MAIN_RACE_ASSASSIN_W;
- break;
- case MAIN_RACE_ASSASSIN_W:
- m_points.job = MAIN_RACE_ASSASSIN_M;
- break;
- case MAIN_RACE_SURA_M:
- m_points.job = MAIN_RACE_SURA_W;
- break;
- case MAIN_RACE_SURA_W:
- m_points.job = MAIN_RACE_SURA_M;
- break;
- case MAIN_RACE_SHAMAN_M:
- m_points.job = MAIN_RACE_SHAMAN_W;
- break;
- case MAIN_RACE_SHAMAN_W:
- m_points.job = MAIN_RACE_SHAMAN_M;
- break;
- default:
- sys_err("CHANGE_SEX: %s unknown race %d", GetName(), src_race);
- return false;
- }
- sys_log(0, "CHANGE_SEX: %s (%d -> %d)", GetName(), src_race, m_points.job);
- return true;
- }
- WORD CHARACTER::GetRaceNum() const
- {
- if (m_dwPolymorphRace)
- return m_dwPolymorphRace;
- if (m_pkMobData)
- return m_pkMobData->m_table.dwVnum;
- return m_points.job;
- }
- void CHARACTER::SetRace(BYTE race)
- {
- if (race >= MAIN_RACE_MAX_NUM)
- {
- sys_err("CHARACTER::SetRace(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
- return;
- }
- m_points.job = race;
- }
- BYTE CHARACTER::GetJob() const
- {
- unsigned race = m_points.job;
- unsigned job;
- if (RaceToJob(race, &job))
- return job;
- sys_err("CHARACTER::GetJob(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
- return JOB_WARRIOR;
- }
- void CHARACTER::SetLevel(int level)
- {
- m_points.level = level;
- if (IsPC())
- {
- if (level < PK_PROTECT_LEVEL)
- SetPKMode(PK_MODE_PROTECT);
- else if (GetGMLevel() != GM_PLAYER)
- SetPKMode(PK_MODE_PROTECT);
- else if (m_bPKMode == PK_MODE_PROTECT)
- SetPKMode(PK_MODE_PEACE);
- }
- }
- void CHARACTER::SetEmpire(BYTE bEmpire)
- {
- m_bEmpire = bEmpire;
- }
- void CHARACTER::SetPlayerProto(const TPlayerTable * t)
- {
- if (!GetDesc() || !*GetDesc()->GetHostName())
- sys_err("cannot get desc or hostname");
- else
- SetGMLevel();
- m_bCharType = CHAR_TYPE_PC;
- m_dwPlayerID = t->id;
- m_iAlignment = t->lAlignment;
- m_iRealAlignment = t->lAlignment;
- m_points.voice = t->voice;
- m_points.skill_group = t->skill_group;
- m_pointsInstant.bBasePart = t->part_base;
- SetPart(PART_HAIR, t->parts[PART_HAIR]);
- m_points.iRandomHP = t->sRandomHP;
- m_points.iRandomSP = t->sRandomSP;
- // REMOVE_REAL_SKILL_LEVLES
- if (m_pSkillLevels)
- M2_DELETE_ARRAY(m_pSkillLevels);
- m_pSkillLevels = M2_NEW TPlayerSkill[SKILL_MAX_NUM];
- thecore_memcpy(m_pSkillLevels, t->skills, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
- // END_OF_REMOVE_REAL_SKILL_LEVLES
- if (t->lMapIndex >= 10000)
- {
- m_posWarp.x = t->lExitX;
- m_posWarp.y = t->lExitY;
- m_lWarpMapIndex = t->lExitMapIndex;
- }
- SetRealPoint(POINT_PLAYTIME, t->playtime);
- m_dwLoginPlayTime = t->playtime;
- SetRealPoint(POINT_ST, t->st);
- SetRealPoint(POINT_HT, t->ht);
- SetRealPoint(POINT_DX, t->dx);
- SetRealPoint(POINT_IQ, t->iq);
- SetPoint(POINT_ST, t->st);
- SetPoint(POINT_HT, t->ht);
- SetPoint(POINT_DX, t->dx);
- SetPoint(POINT_IQ, t->iq);
- SetPoint(POINT_STAT, t->stat_point);
- SetPoint(POINT_SKILL, t->skill_point);
- SetPoint(POINT_SUB_SKILL, t->sub_skill_point);
- SetPoint(POINT_HORSE_SKILL, t->horse_skill_point);
- SetPoint(POINT_STAT_RESET_COUNT, t->stat_reset_count);
- SetPoint(POINT_LEVEL_STEP, t->level_step);
- SetRealPoint(POINT_LEVEL_STEP, t->level_step);
- SetRace(t->job);
- SetLevel(t->level);
- SetExp(t->exp);
- SetGold(t->gold);
- SetMapIndex(t->lMapIndex);
- SetXYZ(t->x, t->y, t->z);
- ComputePoints();
- SetHP(t->hp);
- SetSP(t->sp);
- SetStamina(t->stamina);
- //GM일때 보호모드
- if (!test_server)
- {
- if (GetGMLevel() > GM_LOW_WIZARD)
- {
- m_afAffectFlag.Set(AFF_YMIR);
- m_bPKMode = PK_MODE_PROTECT;
- }
- }
- if (GetLevel() < PK_PROTECT_LEVEL)
- m_bPKMode = PK_MODE_PROTECT;
- m_stMobile = t->szMobile;
- SetHorseData(t->horse);
- if (GetHorseLevel() > 0)
- UpdateHorseDataByLogoff(t->logoff_interval);
- thecore_memcpy(m_aiPremiumTimes, t->aiPremiumTimes, sizeof(t->aiPremiumTimes));
- m_dwLogOffInterval = t->logoff_interval;
- sys_log(0, "PLAYER_LOAD: %s PREMIUM %d %d, LOGGOFF_INTERVAL %u PTR: %p", t->name, m_aiPremiumTimes[0], m_aiPremiumTimes[1], t->logoff_interval, this);
- if (GetGMLevel() != GM_PLAYER)
- {
- LogManager::instance().CharLog(this, GetGMLevel(), "GM_LOGIN", "");
- sys_log(0, "GM_LOGIN(gmlevel=%d, name=%s(%d), pos=(%d, %d)", GetGMLevel(), GetName(), GetPlayerID(), GetX(), GetY());
- }
- #ifdef __PET_SYSTEM__
- // NOTE: 일단 캐릭터가 PC인 경우에만 PetSystem을 갖도록 함. 유럽 머신당 메모리 사용률때문에 NPC까지 하긴 좀..
- if (m_petSystem)
- {
- m_petSystem->Destroy();
- delete m_petSystem;
- }
- m_petSystem = M2_NEW CPetSystem(this);
- #endif
- }
- EVENTFUNC(kill_ore_load_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "kill_ore_load_even> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (ch == NULL) { // <Factor>
- return 0;
- }
- ch->m_pkMiningEvent = NULL;
- M2_DESTROY_CHARACTER(ch);
- return 0;
- }
- void CHARACTER::SetProto(const CMob * pkMob)
- {
- if (m_pkMobInst)
- M2_DELETE(m_pkMobInst);
- m_pkMobData = pkMob;
- m_pkMobInst = M2_NEW CMobInstance;
- m_bPKMode = PK_MODE_FREE;
- const TMobTable * t = &m_pkMobData->m_table;
- m_bCharType = t->bType;
- SetLevel(t->bLevel);
- SetEmpire(t->bEmpire);
- SetExp(t->dwExp);
- SetRealPoint(POINT_ST, t->bStr);
- SetRealPoint(POINT_DX, t->bDex);
- SetRealPoint(POINT_HT, t->bCon);
- SetRealPoint(POINT_IQ, t->bInt);
- ComputePoints();
- SetHP(GetMaxHP());
- SetSP(GetMaxSP());
- ////////////////////
- m_pointsInstant.dwAIFlag = t->dwAIFlag;
- SetImmuneFlag(t->dwImmuneFlag);
- AssignTriggers(t);
- ApplyMobAttribute(t);
- if (IsStone())
- {
- DetermineDropMetinStone();
- }
- if (IsWarp() || IsGoto())
- {
- StartWarpNPCEvent();
- }
- CHARACTER_MANAGER::instance().RegisterRaceNumMap(this);
- // XXX X-mas santa hardcoding
- if (GetRaceNum() == xmas::MOB_SANTA_VNUM)
- {
- SetPoint(POINT_ATT_GRADE_BONUS, 10);
- if (g_iUseLocale)
- SetPoint(POINT_DEF_GRADE_BONUS, 6);
- else
- SetPoint(POINT_DEF_GRADE_BONUS, 15);
- //산타용
- //m_dwPlayStartTime = get_dword_time() + 10 * 60 * 1000;
- //신선자 노해
- m_dwPlayStartTime = get_dword_time() + 30 * 1000;
- if (test_server)
- m_dwPlayStartTime = get_dword_time() + 30 * 1000;
- }
- // XXX CTF GuildWar hardcoding
- if (warmap::IsWarFlag(GetRaceNum()))
- {
- m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
- m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
- m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
- }
- if (warmap::IsWarFlagBase(GetRaceNum()))
- {
- m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
- m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
- m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
- }
- if (m_bCharType == CHAR_TYPE_HORSE ||
- GetRaceNum() == 20101 ||
- GetRaceNum() == 20102 ||
- GetRaceNum() == 20103 ||
- GetRaceNum() == 20104 ||
- GetRaceNum() == 20105 ||
- GetRaceNum() == 20106 ||
- GetRaceNum() == 20107 ||
- GetRaceNum() == 20108 ||
- GetRaceNum() == 20109
- )
- {
- m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
- m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
- m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
- }
- // MINING
- if (mining::IsVeinOfOre (GetRaceNum()))
- {
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- m_pkMiningEvent = event_create(kill_ore_load_event, info, PASSES_PER_SEC(number(7 * 60, 15 * 60)));
- }
- // END_OF_MINING
- }
- const TMobTable & CHARACTER::GetMobTable() const
- {
- return m_pkMobData->m_table;
- }
- bool CHARACTER::IsRaceFlag(DWORD dwBit) const
- {
- return m_pkMobData ? IS_SET(m_pkMobData->m_table.dwRaceFlag, dwBit) : 0;
- }
- DWORD CHARACTER::GetMobDamageMin() const
- {
- return m_pkMobData->m_table.dwDamageRange[0];
- }
- DWORD CHARACTER::GetMobDamageMax() const
- {
- return m_pkMobData->m_table.dwDamageRange[1];
- }
- float CHARACTER::GetMobDamageMultiply() const
- {
- float fDamMultiply = GetMobTable().fDamMultiply;
- if (IsBerserk())
- fDamMultiply = fDamMultiply * 2.0f; // BALANCE: 광폭화 시 두배
- return fDamMultiply;
- }
- DWORD CHARACTER::GetMobDropItemVnum() const
- {
- return m_pkMobData->m_table.dwDropItemVnum;
- }
- bool CHARACTER::IsSummonMonster() const
- {
- return GetSummonVnum() != 0;
- }
- DWORD CHARACTER::GetSummonVnum() const
- {
- return m_pkMobData ? m_pkMobData->m_table.dwSummonVnum : 0;
- }
- DWORD CHARACTER::GetPolymorphItemVnum() const
- {
- return m_pkMobData ? m_pkMobData->m_table.dwPolymorphItemVnum : 0;
- }
- DWORD CHARACTER::GetMonsterDrainSPPoint() const
- {
- return m_pkMobData ? m_pkMobData->m_table.dwDrainSP : 0;
- }
- BYTE CHARACTER::GetMobRank() const
- {
- if (!m_pkMobData)
- return MOB_RANK_KNIGHT; // PC일 경우 KNIGHT급
- return m_pkMobData->m_table.bRank;
- }
- BYTE CHARACTER::GetMobSize() const
- {
- if (!m_pkMobData)
- return MOBSIZE_MEDIUM;
- return m_pkMobData->m_table.bSize;
- }
- WORD CHARACTER::GetMobAttackRange() const
- {
- switch (GetMobBattleType())
- {
- case BATTLE_TYPE_RANGE:
- case BATTLE_TYPE_MAGIC:
- return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE);
- default:
- return m_pkMobData->m_table.wAttackRange;
- }
- }
- BYTE CHARACTER::GetMobBattleType() const
- {
- if (!m_pkMobData)
- return BATTLE_TYPE_MELEE;
- return (m_pkMobData->m_table.bBattleType);
- }
- void CHARACTER::ComputeBattlePoints()
- {
- if (IsPolymorphed())
- {
- DWORD dwMobVnum = GetPolymorphVnum();
- const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
- int iAtt = 0;
- int iDef = 0;
- if (pMob)
- {
- iAtt = GetLevel() * 2 + GetPolymorphPoint(POINT_ST) * 2;
- // lev + con
- iDef = GetLevel() + GetPolymorphPoint(POINT_HT) + pMob->m_table.wDef;
- }
- SetPoint(POINT_ATT_GRADE, iAtt);
- SetPoint(POINT_DEF_GRADE, iDef);
- SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
- SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
- }
- else if (IsPC())
- {
- SetPoint(POINT_ATT_GRADE, 0);
- SetPoint(POINT_DEF_GRADE, 0);
- SetPoint(POINT_CLIENT_DEF_GRADE, 0);
- SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
- SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
- //
- // 기본 ATK = 2lev + 2str, 직업에 마다 2str은 바뀔 수 있음
- //
- int iAtk = GetLevel() * 2;
- int iStatAtk = 0;
- switch (GetJob())
- {
- case JOB_WARRIOR:
- case JOB_SURA:
- iStatAtk = (2 * GetPoint(POINT_ST));
- break;
- case JOB_ASSASSIN:
- iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_DX)) / 3;
- break;
- case JOB_SHAMAN:
- iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_IQ)) / 3;
- break;
- default:
- sys_err("invalid job %d", GetJob());
- iStatAtk = (2 * GetPoint(POINT_ST));
- break;
- }
- // 말을 타고 있고, 스탯으로 인한 공격력이 ST*2 보다 낮으면 ST*2로 한다.
- // 스탯을 잘못 찍은 사람 공격력이 더 낮지 않게 하기 위해서다.
- if (GetMountVnum() && iStatAtk < 2 * GetPoint(POINT_ST))
- iStatAtk = (2 * GetPoint(POINT_ST));
- iAtk += iStatAtk;
- // 승마(말) : 검수라 데미지 감소
- if (GetMountVnum())
- {
- if (GetJob() == JOB_SURA && GetSkillGroup() == 1)
- {
- iAtk += (iAtk * GetHorseLevel()) / 60;
- }
- else
- {
- iAtk += (iAtk * GetHorseLevel()) / 30;
- }
- }
- //
- // ATK Setting
- //
- iAtk += GetPoint(POINT_ATT_GRADE_BONUS);
- PointChange(POINT_ATT_GRADE, iAtk);
- // DEF = LEV + CON + ARMOR
- int iShowDef = GetLevel() + GetPoint(POINT_HT); // For Ymir(천마)
- int iDef = GetLevel() + (int) (GetPoint(POINT_HT) / 1.25); // For Other
- int iArmor = 0;
- LPITEM pkItem;
- for (int i = 0; i < WEAR_MAX_NUM; ++i)
- if ((pkItem = GetWear(i)) && pkItem->GetType() == ITEM_ARMOR)
- {
- if (pkItem->GetSubType() == ARMOR_BODY || pkItem->GetSubType() == ARMOR_HEAD || pkItem->GetSubType() == ARMOR_FOOTS || pkItem->GetSubType() == ARMOR_SHIELD)
- {
- iArmor += pkItem->GetValue(1);
- iArmor += (2 * pkItem->GetValue(5));
- }
- }
- // 말 타고 있을 때 방어력이 말의 기준 방어력보다 낮으면 기준 방어력으로 설정
- if( true == IsHorseRiding() )
- {
- if (iArmor < GetHorseArmor())
- iArmor = GetHorseArmor();
- const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID());
- if (pHorseName != NULL && strlen(pHorseName))
- {
- iArmor += 20;
- }
- }
- iArmor += GetPoint(POINT_DEF_GRADE_BONUS);
- iArmor += GetPoint(POINT_PARTY_DEFENDER_BONUS);
- // INTERNATIONAL_VERSION
- if (LC_IsYMIR())
- {
- PointChange(POINT_DEF_GRADE, iShowDef + iArmor);
- }
- else
- {
- PointChange(POINT_DEF_GRADE, iDef + iArmor);
- PointChange(POINT_CLIENT_DEF_GRADE, (iShowDef + iArmor) - GetPoint(POINT_DEF_GRADE));
- }
- // END_OF_INTERNATIONAL_VERSION
- PointChange(POINT_MAGIC_ATT_GRADE, GetLevel() * 2 + GetPoint(POINT_IQ) * 2 + GetPoint(POINT_MAGIC_ATT_GRADE_BONUS));
- PointChange(POINT_MAGIC_DEF_GRADE, GetLevel() + (GetPoint(POINT_IQ) * 3 + GetPoint(POINT_HT)) / 3 + iArmor / 2 + GetPoint(POINT_MAGIC_DEF_GRADE_BONUS));
- }
- else
- {
- // 2lev + str * 2
- int iAtt = GetLevel() * 2 + GetPoint(POINT_ST) * 2;
- // lev + con
- int iDef = GetLevel() + GetPoint(POINT_HT) + GetMobTable().wDef;
- SetPoint(POINT_ATT_GRADE, iAtt);
- SetPoint(POINT_DEF_GRADE, iDef);
- SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
- SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
- }
- }
- void CHARACTER::ComputePoints()
- {
- long lStat = GetPoint(POINT_STAT);
- long lStatResetCount = GetPoint(POINT_STAT_RESET_COUNT);
- long lSkillActive = GetPoint(POINT_SKILL);
- long lSkillSub = GetPoint(POINT_SUB_SKILL);
- long lSkillHorse = GetPoint(POINT_HORSE_SKILL);
- long lLevelStep = GetPoint(POINT_LEVEL_STEP);
- long lAttackerBonus = GetPoint(POINT_PARTY_ATTACKER_BONUS);
- long lTankerBonus = GetPoint(POINT_PARTY_TANKER_BONUS);
- long lBufferBonus = GetPoint(POINT_PARTY_BUFFER_BONUS);
- long lSkillMasterBonus = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
- long lHasteBonus = GetPoint(POINT_PARTY_HASTE_BONUS);
- long lDefenderBonus = GetPoint(POINT_PARTY_DEFENDER_BONUS);
- long lHPRecovery = GetPoint(POINT_HP_RECOVERY);
- long lSPRecovery = GetPoint(POINT_SP_RECOVERY);
- memset(m_pointsInstant.points, 0, sizeof(m_pointsInstant.points));
- BuffOnAttr_ClearAll();
- m_SkillDamageBonus.clear();
- SetPoint(POINT_STAT, lStat);
- SetPoint(POINT_SKILL, lSkillActive);
- SetPoint(POINT_SUB_SKILL, lSkillSub);
- SetPoint(POINT_HORSE_SKILL, lSkillHorse);
- SetPoint(POINT_LEVEL_STEP, lLevelStep);
- SetPoint(POINT_STAT_RESET_COUNT, lStatResetCount);
- SetPoint(POINT_ST, GetRealPoint(POINT_ST));
- SetPoint(POINT_HT, GetRealPoint(POINT_HT));
- SetPoint(POINT_DX, GetRealPoint(POINT_DX));
- SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
- SetPart(PART_MAIN, GetOriginalPart(PART_MAIN));
- SetPart(PART_WEAPON, GetOriginalPart(PART_WEAPON));
- SetPart(PART_HEAD, GetOriginalPart(PART_HEAD));
- SetPart(PART_HAIR, GetOriginalPart(PART_HAIR));
- SetPoint(POINT_PARTY_ATTACKER_BONUS, lAttackerBonus);
- SetPoint(POINT_PARTY_TANKER_BONUS, lTankerBonus);
- SetPoint(POINT_PARTY_BUFFER_BONUS, lBufferBonus);
- SetPoint(POINT_PARTY_SKILL_MASTER_BONUS, lSkillMasterBonus);
- SetPoint(POINT_PARTY_HASTE_BONUS, lHasteBonus);
- SetPoint(POINT_PARTY_DEFENDER_BONUS, lDefenderBonus);
- SetPoint(POINT_HP_RECOVERY, lHPRecovery);
- SetPoint(POINT_SP_RECOVERY, lSPRecovery);
- // PC_BANG_ITEM_ADD
- SetPoint(POINT_PC_BANG_EXP_BONUS, 0);
- SetPoint(POINT_PC_BANG_DROP_BONUS, 0);
- // END_PC_BANG_ITEM_ADD
- int iMaxHP, iMaxSP;
- int iMaxStamina;
- if (IsPC())
- {
- // 최대 생명력/정신력
- iMaxHP = JobInitialPoints[GetJob()].max_hp + m_points.iRandomHP + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].hp_per_ht;
- iMaxSP = JobInitialPoints[GetJob()].max_sp + m_points.iRandomSP + GetPoint(POINT_IQ) * JobInitialPoints[GetJob()].sp_per_iq;
- iMaxStamina = JobInitialPoints[GetJob()].max_stamina + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].stamina_per_con;
- {
- CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_ADD_HP);
- if (NULL != pkSk)
- {
- pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_ADD_HP) / 100.0f);
- iMaxHP += static_cast<int>(pkSk->kPointPoly.Eval());
- }
- }
- // 기본 값들
- SetPoint(POINT_MOV_SPEED, 100);
- SetPoint(POINT_ATT_SPEED, 100);
- PointChange(POINT_ATT_SPEED, GetPoint(POINT_PARTY_HASTE_BONUS));
- SetPoint(POINT_CASTING_SPEED, 100);
- }
- else
- {
- iMaxHP = m_pkMobData->m_table.dwMaxHP;
- iMaxSP = 0;
- iMaxStamina = 0;
- SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
- SetPoint(POINT_MOV_SPEED, m_pkMobData->m_table.sMovingSpeed);
- SetPoint(POINT_CASTING_SPEED, m_pkMobData->m_table.sAttackSpeed);
- }
- if (IsPC())
- {
- // 말 타고 있을 때는 기본 스탯이 말의 기준 스탯보다 낮으면 높게 만든다.
- // 따라서 말의 기준 스탯이 무사 기준이므로, 수라/무당은 전체 스탯 합이
- // 대채적으로 더 올라가게 될 것이다.
- if (GetMountVnum())
- {
- if (GetHorseST() > GetPoint(POINT_ST))
- PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
- if (GetHorseDX() > GetPoint(POINT_DX))
- PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
- if (GetHorseHT() > GetPoint(POINT_HT))
- PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
- if (GetHorseIQ() > GetPoint(POINT_IQ))
- PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
- }
- }
- ComputeBattlePoints();
- // 기본 HP/SP 설정
- if (iMaxHP != GetMaxHP())
- {
- SetRealPoint(POINT_MAX_HP, iMaxHP); // 기본HP를 RealPoint에 저장해 놓는다.
- }
- PointChange(POINT_MAX_HP, 0);
- if (iMaxSP != GetMaxSP())
- {
- SetRealPoint(POINT_MAX_SP, iMaxSP); // 기본SP를 RealPoint에 저장해 놓는다.
- }
- PointChange(POINT_MAX_SP, 0);
- SetMaxStamina(iMaxStamina);
- m_pointsInstant.dwImmuneFlag = 0;
- for (int i = 0 ; i < WEAR_MAX_NUM; i++)
- {
- LPITEM pItem = GetWear(i);
- if (pItem)
- {
- pItem->ModifyPoints(true);
- SET_BIT(m_pointsInstant.dwImmuneFlag, GetWear(i)->GetImmuneFlag());
- }
- }
- // 용혼석 시스템
- // ComputePoints에서는 케릭터의 모든 속성값을 초기화하고,
- // 아이템, 버프 등에 관련된 모든 속성값을 재계산하기 때문에,
- // 용혼석 시스템도 ActiveDeck에 있는 모든 용혼석의 속성값을 다시 적용시켜야 한다.
- if (DragonSoul_IsDeckActivated())
- {
- for (int i = WEAR_MAX_NUM + DS_SLOT_MAX * DragonSoul_GetActiveDeck();
- i < WEAR_MAX_NUM + DS_SLOT_MAX * (DragonSoul_GetActiveDeck() + 1); i++)
- {
- LPITEM pItem = GetWear(i);
- if (pItem)
- {
- if (DSManager::instance().IsTimeLeftDragonSoul(pItem))
- pItem->ModifyPoints(true);
- }
- }
- }
- if (GetHP() > GetMaxHP())
- PointChange(POINT_HP, GetMaxHP() - GetHP());
- if (GetSP() > GetMaxSP())
- PointChange(POINT_SP, GetMaxSP() - GetSP());
- ComputeSkillPoints();
- RefreshAffect();
- CPetSystem* pPetSystem = GetPetSystem();
- if (NULL != pPetSystem)
- {
- pPetSystem->RefreshBuff();
- }
- for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
- {
- it->second->GiveAllAttributes();
- }
- UpdatePacket();
- }
- // m_dwPlayStartTime의 단위는 milisecond다. 데이터베이스에는 분단위로 기록하기
- // 때문에 플레이시간을 계산할 때 / 60000 으로 나눠서 하는데, 그 나머지 값이 남았
- // 을 때 여기에 dwTimeRemain으로 넣어서 제대로 계산되도록 해주어야 한다.
- void CHARACTER::ResetPlayTime(DWORD dwTimeRemain)
- {
- m_dwPlayStartTime = get_dword_time() - dwTimeRemain;
- }
- const int aiRecoveryPercents[10] = { 1, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
- EVENTFUNC(recovery_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "recovery_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (ch == NULL) { // <Factor>
- return 0;
- }
- if (!ch->IsPC())
- {
- //
- // 몬스터 회복
- //
- if (ch->IsAffectFlag(AFF_POISON))
- return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
- if (2493 == ch->GetMobTable().dwVnum)
- {
- int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct());
- regenPct += ch->GetMobTable().bRegenPercent;
- for (int i=1 ; i <= 4 ; ++i)
- {
- if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
- {
- DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
- size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
- size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
- regenPct += (val*cnt);
- break;
- }
- }
- ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * regenPct) / 100));
- }
- else if (!ch->IsDoor())
- {
- ch->MonsterLog("HP_REGEN +%d", MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
- ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
- }
- if (ch->GetHP() >= ch->GetMaxHP())
- {
- ch->m_pkRecoveryEvent = NULL;
- return 0;
- }
- if (2493 == ch->GetMobTable().dwVnum)
- {
- for (int i=1 ; i <= 4 ; ++i)
- {
- if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
- {
- DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
- size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
- size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
- return PASSES_PER_SEC(MAX(1, (ch->GetMobTable().bRegenCycle - (val*cnt))));
- }
- }
- }
- return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
- }
- else
- {
- //
- // PC 회복
- //
- ch->CheckTarget();
- //ch->UpdateSectree(); // 여기서 이걸 왜하지?
- ch->UpdateKillerMode();
- if (ch->IsAffectFlag(AFF_POISON) == true)
- {
- // 중독인 경우 자동회복 금지
- // 파법술인 경우 자동회복 금지
- return 3;
- }
- int iSec = (get_dword_time() - ch->GetLastMoveTime()) / 3000;
- // SP 회복 루틴.
- // 왜 이걸로 해서 함수로 빼놨는가 ?!
- ch->DistributeSP(ch);
- if (ch->GetMaxHP() <= ch->GetHP())
- return PASSES_PER_SEC(3);
- int iPercent = 0;
- int iAmount = 0;
- {
- iPercent = aiRecoveryPercents[MIN(9, iSec)];
- iAmount = 15 + (ch->GetMaxHP() * iPercent) / 100;
- }
- iAmount += (iAmount * ch->GetPoint(POINT_HP_REGEN)) / 100;
- sys_log(1, "RECOVERY_EVENT: %s %d HP_REGEN %d HP +%d", ch->GetName(), iPercent, ch->GetPoint(POINT_HP_REGEN), iAmount);
- ch->PointChange(POINT_HP, iAmount, false);
- return PASSES_PER_SEC(3);
- }
- }
- void CHARACTER::StartRecoveryEvent()
- {
- if (m_pkRecoveryEvent)
- return;
- if (IsDead() || IsStun())
- return;
- if (IsNPC() && GetHP() >= GetMaxHP()) // 몬스터는 체력이 다 차있으면 시작 안한다.
- return;
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- int iSec = IsPC() ? 3 : (MAX(1, GetMobTable().bRegenCycle));
- m_pkRecoveryEvent = event_create(recovery_event, info, PASSES_PER_SEC(iSec));
- }
- void CHARACTER::Standup()
- {
- struct packet_position pack_position;
- if (!IsPosition(POS_SITTING))
- return;
- SetPosition(POS_STANDING);
- sys_log(1, "STANDUP: %s", GetName());
- pack_position.header = HEADER_GC_CHARACTER_POSITION;
- pack_position.vid = GetVID();
- pack_position.position = POSITION_GENERAL;
- PacketAround(&pack_position, sizeof(pack_position));
- }
- void CHARACTER::Sitdown(int is_ground)
- {
- struct packet_position pack_position;
- if (IsPosition(POS_SITTING))
- return;
- SetPosition(POS_SITTING);
- sys_log(1, "SITDOWN: %s", GetName());
- pack_position.header = HEADER_GC_CHARACTER_POSITION;
- pack_position.vid = GetVID();
- pack_position.position = POSITION_SITTING_GROUND;
- PacketAround(&pack_position, sizeof(pack_position));
- }
- void CHARACTER::SetRotation(float fRot)
- {
- m_pointsInstant.fRot = fRot;
- }
- // x, y 방향으로 보고 선다.
- void CHARACTER::SetRotationToXY(long x, long y)
- {
- SetRotation(GetDegreeFromPositionXY(GetX(), GetY(), x, y));
- }
- bool CHARACTER::CannotMoveByAffect() const
- {
- return (IsAffectFlag(AFF_STUN));
- }
- bool CHARACTER::CanMove() const
- {
- if (CannotMoveByAffect())
- return false;
- if (GetMyShop()) // 상점 연 상태에서는 움직일 수 없음
- return false;
- // 0.2초 전이라면 움직일 수 없다.
- /*
- if (get_float_time() - m_fSyncTime < 0.2f)
- return false;
- */
- return true;
- }
- // 무조건 x, y 위치로 이동 시킨다.
- bool CHARACTER::Sync(long x, long y)
- {
- if (!GetSectree())
- return false;
- LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);
- if (!new_tree)
- {
- if (GetDesc())
- {
- sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName());
- GetDesc()->SetPhase(PHASE_CLOSE);
- }
- else
- {
- sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex());
- Dead();
- }
- return false;
- }
- SetRotationToXY(x, y);
- SetXYZ(x, y, 0);
- if (GetDungeon())
- {
- // 던젼용 이벤트 속성 변화
- int iLastEventAttr = m_iEventAttr;
- m_iEventAttr = new_tree->GetEventAttribute(x, y);
- if (m_iEventAttr != iLastEventAttr)
- {
- if (GetParty())
- {
- quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr);
- quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr);
- }
- else
- {
- quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr);
- quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr);
- }
- }
- }
- if (GetSectree() != new_tree)
- {
- if (!IsNPC())
- {
- SECTREEID id = new_tree->GetID();
- SECTREEID old_id = GetSectree()->GetID();
- sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d",
- GetName(),
- id.coord.x,
- id.coord.y,
- old_id.coord.x,
- old_id.coord.y);
- }
- new_tree->InsertEntity(this);
- }
- return true;
- }
- void CHARACTER::Stop()
- {
- if (!IsState(m_stateIdle))
- MonsterLog("[IDLE] 정지");
- GotoState(m_stateIdle);
- m_posDest.x = m_posStart.x = GetX();
- m_posDest.y = m_posStart.y = GetY();
- }
- bool CHARACTER::Goto(long x, long y)
- {
- // TODO 거리체크 필요
- // 같은 위치면 이동할 필요 없음 (자동 성공)
- if (GetX() == x && GetY() == y)
- return false;
- if (m_posDest.x == x && m_posDest.y == y)
- {
- if (!IsState(m_stateMove))
- {
- m_dwStateDuration = 4;
- GotoState(m_stateMove);
- }
- return false;
- }
- m_posDest.x = x;
- m_posDest.y = y;
- CalculateMoveDuration();
- m_dwStateDuration = 4;
- if (!IsState(m_stateMove))
- {
- MonsterLog("[MOVE] %s", GetVictim() ? "대상추적" : "그냥이동");
- if (GetVictim())
- {
- //MonsterChat(MONSTER_CHAT_CHASE);
- MonsterChat(MONSTER_CHAT_ATTACK);
- }
- }
- GotoState(m_stateMove);
- return true;
- }
- DWORD CHARACTER::GetMotionMode() const
- {
- DWORD dwMode = MOTION_MODE_GENERAL;
- if (IsPolymorphed())
- return dwMode;
- LPITEM pkItem;
- if ((pkItem = GetWear(WEAR_WEAPON)))
- {
- switch (pkItem->GetProto()->bSubType)
- {
- case WEAPON_SWORD:
- dwMode = MOTION_MODE_ONEHAND_SWORD;
- break;
- case WEAPON_TWO_HANDED:
- dwMode = MOTION_MODE_TWOHAND_SWORD;
- break;
- case WEAPON_DAGGER:
- dwMode = MOTION_MODE_DUALHAND_SWORD;
- break;
- case WEAPON_BOW:
- dwMode = MOTION_MODE_BOW;
- break;
- case WEAPON_BELL:
- dwMode = MOTION_MODE_BELL;
- break;
- case WEAPON_FAN:
- dwMode = MOTION_MODE_FAN;
- break;
- }
- }
- return dwMode;
- }
- float CHARACTER::GetMoveMotionSpeed() const
- {
- DWORD dwMode = GetMotionMode();
- const CMotion * pkMotion = NULL;
- if (!GetMountVnum())
- pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(dwMode, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
- else
- {
- pkMotion = CMotionManager::instance().GetMotion(GetMountVnum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
- if (!pkMotion)
- pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_HORSE, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
- }
- if (pkMotion)
- return -pkMotion->GetAccumVector().y / pkMotion->GetDuration();
- else
- {
- sys_err("cannot find motion (name %s race %d mode %d)", GetName(), GetRaceNum(), dwMode);
- return 300.0f;
- }
- }
- float CHARACTER::GetMoveSpeed() const
- {
- return GetMoveMotionSpeed() * 10000 / CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), 10000);
- }
- void CHARACTER::CalculateMoveDuration()
- {
- m_posStart.x = GetX();
- m_posStart.y = GetY();
- float fDist = DISTANCE_SQRT(m_posStart.x - m_posDest.x, m_posStart.y - m_posDest.y);
- float motionSpeed = GetMoveMotionSpeed();
- m_dwMoveDuration = CalculateDuration(GetLimitPoint(POINT_MOV_SPEED),
- (int) ((fDist / motionSpeed) * 1000.0f));
- if (IsNPC())
- sys_log(1, "%s: GOTO: distance %f, spd %u, duration %u, motion speed %f pos %d %d -> %d %d",
- GetName(), fDist, GetLimitPoint(POINT_MOV_SPEED), m_dwMoveDuration, motionSpeed,
- m_posStart.x, m_posStart.y, m_posDest.x, m_posDest.y);
- m_dwMoveStartTime = get_dword_time();
- }
- // x y 위치로 이동 한다. (이동할 수 있는 가 없는 가를 확인 하고 Sync 메소드로 실제 이동 한다)
- // 서버는 char의 x, y 값을 바로 바꾸지만,
- // 클라에서는 이전 위치에서 바꾼 x, y까지 interpolation한다.
- // 걷거나 뛰는 것은 char의 m_bNowWalking에 달려있다.
- // Warp를 의도한 것이라면 Show를 사용할 것.
- bool CHARACTER::Move(long x, long y)
- {
- // 같은 위치면 이동할 필요 없음 (자동 성공)
- if (GetX() == x && GetY() == y)
- return true;
- if (test_server)
- if (m_bDetailLog)
- sys_log(0, "%s position %u %u", GetName(), x, y);
- OnMove();
- return Sync(x, y);
- }
- void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot)
- {
- TPacketGCMove pack;
- if (bFunc == FUNC_WAIT)
- {
- x = m_posDest.x;
- y = m_posDest.y;
- dwDuration = m_dwMoveDuration;
- }
- EncodeMovePacket(pack, GetVID(), bFunc, bArg, x, y, dwDuration, dwTime, iRot == -1 ? (int) GetRotation() / 5 : iRot);
- PacketView(&pack, sizeof(TPacketGCMove), this);
- }
- int CHARACTER::GetRealPoint(BYTE type) const
- {
- return m_points.points[type];
- }
- void CHARACTER::SetRealPoint(BYTE type, int val)
- {
- m_points.points[type] = val;
- }
- int CHARACTER::GetPolymorphPoint(BYTE type) const
- {
- if (IsPolymorphed() && !IsPolyMaintainStat())
- {
- DWORD dwMobVnum = GetPolymorphVnum();
- const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
- int iPower = GetPolymorphPower();
- if (pMob)
- {
- switch (type)
- {
- case POINT_ST:
- if (GetJob() == JOB_SHAMAN || GetJob() == JOB_SURA && GetSkillGroup() == 2)
- return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_IQ);
- return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_ST);
- case POINT_HT:
- return pMob->m_table.bCon * iPower / 100 + GetPoint(POINT_HT);
- case POINT_IQ:
- return pMob->m_table.bInt * iPower / 100 + GetPoint(POINT_IQ);
- case POINT_DX:
- return pMob->m_table.bDex * iPower / 100 + GetPoint(POINT_DX);
- }
- }
- }
- return GetPoint(type);
- }
- int CHARACTER::GetPoint(BYTE type) const
- {
- if (type >= POINT_MAX_NUM)
- {
- sys_err("Point type overflow (type %u)", type);
- return 0;
- }
- int val = m_pointsInstant.points[type];
- int max_val = INT_MAX;
- switch (type)
- {
- case POINT_STEAL_HP:
- case POINT_STEAL_SP:
- max_val = 50;
- break;
- }
- if (val > max_val)
- sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
- return (val);
- }
- int CHARACTER::GetLimitPoint(BYTE type) const
- {
- if (type >= POINT_MAX_NUM)
- {
- sys_err("Point type overflow (type %u)", type);
- return 0;
- }
- int val = m_pointsInstant.points[type];
- int max_val = INT_MAX;
- int limit = INT_MAX;
- int min_limit = -INT_MAX;
- switch (type)
- {
- case POINT_ATT_SPEED:
- min_limit = 0;
- if (IsPC())
- limit = 170;
- else
- limit = 250;
- break;
- case POINT_MOV_SPEED:
- min_limit = 0;
- if (IsPC())
- limit = 200;
- else
- limit = 250;
- break;
- case POINT_STEAL_HP:
- case POINT_STEAL_SP:
- limit = 50;
- max_val = 50;
- break;
- case POINT_MALL_ATTBONUS:
- case POINT_MALL_DEFBONUS:
- limit = 20;
- max_val = 50;
- break;
- }
- if (val > max_val)
- sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
- if (val > limit)
- val = limit;
- if (val < min_limit)
- val = min_limit;
- return (val);
- }
- void CHARACTER::SetPoint(BYTE type, int val)
- {
- if (type >= POINT_MAX_NUM)
- {
- sys_err("Point type overflow (type %u)", type);
- return;
- }
- m_pointsInstant.points[type] = val;
- // 아직 이동이 다 안끝났다면 이동 시간 계산을 다시 해야 한다.
- if (type == POINT_MOV_SPEED && get_dword_time() < m_dwMoveStartTime + m_dwMoveDuration)
- {
- CalculateMoveDuration();
- }
- }
- INT CHARACTER::GetAllowedGold() const
- {
- if (GetLevel() <= 10)
- return 100000;
- else if (GetLevel() <= 20)
- return 500000;
- else
- return 50000000;
- }
- void CHARACTER::CheckMaximumPoints()
- {
- if (GetMaxHP() < GetHP())
- PointChange(POINT_HP, GetMaxHP() - GetHP());
- if (GetMaxSP() < GetSP())
- PointChange(POINT_SP, GetMaxSP() - GetSP());
- }
- void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast)
- {
- int val = 0;
- //sys_log(0, "PointChange %d %d | %d -> %d cHP %d mHP %d", type, amount, GetPoint(type), GetPoint(type)+amount, GetHP(), GetMaxHP());
- switch (type)
- {
- case POINT_NONE:
- return;
- case POINT_LEVEL:
- if ((GetLevel() + amount) > gPlayerMaxLevel)
- return;
- SetLevel(GetLevel() + amount);
- val = GetLevel();
- sys_log(0, "LEVELUP: %s %d NEXT EXP %d", GetName(), GetLevel(), GetNextExp());
- PointChange(POINT_NEXT_EXP, GetNextExp(), false);
- if (amount)
- {
- quest::CQuestManager::instance().LevelUp(GetPlayerID());
- LogManager::instance().LevelLog(this, val, GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000);
- if (GetGuild())
- {
- GetGuild()->LevelChange(GetPlayerID(), GetLevel());
- }
- if (GetParty())
- {
- GetParty()->RequestSetMemberLevel(GetPlayerID(), GetLevel());
- }
- }
- break;
- case POINT_NEXT_EXP:
- val = GetNextExp();
- bAmount = false; // 무조건 bAmount는 false 여야 한다.
- break;
- case POINT_EXP:
- {
- DWORD exp = GetExp();
- DWORD next_exp = GetNextExp();
- // 청소년보호
- if (LC_IsNewCIBN())
- {
- if (IsOverTime(OT_NONE))
- {
- dev_log(LOG_DEB0, "<EXP_LOG> %s = NONE", GetName());
- }
- else if (IsOverTime(OT_3HOUR))
- {
- amount = (amount / 2);
- dev_log(LOG_DEB0, "<EXP_LOG> %s = 3HOUR", GetName());
- }
- else if (IsOverTime(OT_5HOUR))
- {
- amount = 0;
- dev_log(LOG_DEB0, "<EXP_LOG> %s = 5HOUR", GetName());
- }
- }
- // exp가 0 이하로 가지 않도록 한다
- if (amount < 0 && exp < -amount)
- {
- sys_log(1, "%s AMOUNT < 0 %d, CUR EXP: %d", GetName(), -amount, exp);
- amount = -exp;
- SetExp(exp + amount);
- val = GetExp();
- }
- else
- {
- if (gPlayerMaxLevel <= GetLevel())
- return;
- if (test_server)
- ChatPacket(CHAT_TYPE_INFO, "You have gained %d exp.", amount);
- DWORD iExpBalance = 0;
- // 레벨 업!
- if (exp + amount >= next_exp)
- {
- iExpBalance = (exp + amount) - next_exp;
- amount = next_exp - exp;
- SetExp(0);
- exp = next_exp;
- }
- else
- {
- SetExp(exp + amount);
- exp = GetExp();
- }
- DWORD q = DWORD(next_exp / 4.0f);
- int iLevStep = GetRealPoint(POINT_LEVEL_STEP);
- // iLevStep이 4 이상이면 레벨이 올랐어야 하므로 여기에 올 수 없는 값이다.
- if (iLevStep >= 4)
- {
- sys_err("%s LEVEL_STEP bigger than 4! (%d)", GetName(), iLevStep);
- iLevStep = 4;
- }
- if (exp >= next_exp && iLevStep < 4)
- {
- for (int i = 0; i < 4 - iLevStep; ++i)
- PointChange(POINT_LEVEL_STEP, 1, false, true);
- }
- else if (exp >= q * 3 && iLevStep < 3)
- {
- for (int i = 0; i < 3 - iLevStep; ++i)
- PointChange(POINT_LEVEL_STEP, 1, false, true);
- }
- else if (exp >= q * 2 && iLevStep < 2)
- {
- for (int i = 0; i < 2 - iLevStep; ++i)
- PointChange(POINT_LEVEL_STEP, 1, false, true);
- }
- else if (exp >= q && iLevStep < 1)
- PointChange(POINT_LEVEL_STEP, 1);
- if (iExpBalance)
- {
- PointChange(POINT_EXP, iExpBalance);
- }
- val = GetExp();
- }
- }
- break;
- case POINT_LEVEL_STEP:
- if (amount > 0)
- {
- val = GetPoint(POINT_LEVEL_STEP) + amount;
- switch (val)
- {
- case 1:
- case 2:
- case 3:
- //if (GetLevel() < 100) PointChange(POINT_STAT, 1);
- if (GetLevel() < 91) PointChange(POINT_STAT, 1);
- break;
- case 4:
- {
- int iHP = number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end);
- int iSP = number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end);
- m_points.iRandomHP += iHP;
- m_points.iRandomSP += iSP;
- if (GetSkillGroup())
- {
- if (GetLevel() >= 5)
- PointChange(POINT_SKILL, 1);
- if (GetLevel() >= 9)
- PointChange(POINT_SUB_SKILL, 1);
- }
- PointChange(POINT_MAX_HP, iHP);
- PointChange(POINT_MAX_SP, iSP);
- PointChange(POINT_LEVEL, 1, false, true);
- val = 0;
- }
- break;
- }
- if (GetLevel() <= 10)
- AutoGiveItem(27001, 2);
- else if (GetLevel() <= 30)
- AutoGiveItem(27002, 2);
- else
- {
- AutoGiveItem(27002, 2);
- // AutoGiveItem(27003, 2);
- }
- PointChange(POINT_HP, GetMaxHP() - GetHP());
- PointChange(POINT_SP, GetMaxSP() - GetSP());
- PointChange(POINT_STAMINA, GetMaxStamina() - GetStamina());
- SetPoint(POINT_LEVEL_STEP, val);
- SetRealPoint(POINT_LEVEL_STEP, val);
- Save();
- }
- else
- val = GetPoint(POINT_LEVEL_STEP);
- break;
- case POINT_HP:
- {
- if (IsDead() || IsStun())
- return;
- int prev_hp = GetHP();
- amount = MIN(GetMaxHP() - GetHP(), amount);
- SetHP(GetHP() + amount);
- val = GetHP();
- BroadcastTargetPacket();
- if (GetParty() && IsPC() && val != prev_hp)
- GetParty()->SendPartyInfoOneToAll(this);
- }
- break;
- case POINT_SP:
- {
- if (IsDead() || IsStun())
- return;
- amount = MIN(GetMaxSP() - GetSP(), amount);
- SetSP(GetSP() + amount);
- val = GetSP();
- }
- break;
- case POINT_STAMINA:
- {
- if (IsDead() || IsStun())
- return;
- int prev_val = GetStamina();
- amount = MIN(GetMaxStamina() - GetStamina(), amount);
- SetStamina(GetStamina() + amount);
- val = GetStamina();
- if (val == 0)
- {
- // Stamina가 없으니 걷자!
- SetNowWalking(true);
- }
- else if (prev_val == 0)
- {
- // 없던 스테미나가 생겼으니 이전 모드 복귀
- ResetWalking();
- }
- if (amount < 0 && val != 0) // 감소는 보내지않는다.
- return;
- }
- break;
- case POINT_MAX_HP:
- {
- SetPoint(type, GetPoint(type) + amount);
- //SetMaxHP(GetMaxHP() + amount);
- // 최대 생명력 = (기본 최대 생명력 + 추가) * 최대생명력%
- int hp = GetRealPoint(POINT_MAX_HP);
- int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100);
- add_hp += GetPoint(POINT_MAX_HP);
- add_hp += GetPoint(POINT_PARTY_TANKER_BONUS);
- SetMaxHP(hp + add_hp);
- val = GetMaxHP();
- }
- break;
- case POINT_MAX_SP:
- {
- SetPoint(type, GetPoint(type) + amount);
- //SetMaxSP(GetMaxSP() + amount);
- // 최대 정신력 = (기본 최대 정신력 + 추가) * 최대정신력%
- int sp = GetRealPoint(POINT_MAX_SP);
- int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100);
- add_sp += GetPoint(POINT_MAX_SP);
- add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
- SetMaxSP(sp + add_sp);
- val = GetMaxSP();
- }
- break;
- case POINT_MAX_HP_PCT:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- PointChange(POINT_MAX_HP, 0);
- break;
- case POINT_MAX_SP_PCT:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- PointChange(POINT_MAX_SP, 0);
- break;
- case POINT_MAX_STAMINA:
- SetMaxStamina(GetMaxStamina() + amount);
- val = GetMaxStamina();
- break;
- case POINT_GOLD:
- {
- const int64_t nTotalMoney = static_cast<int64_t>(GetGold()) + static_cast<int64_t>(amount);
- if (GOLD_MAX <= nTotalMoney)
- {
- sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName());
- LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", "");
- return;
- }
- // 청소년보호
- if (LC_IsNewCIBN() && amount > 0)
- {
- if (IsOverTime(OT_NONE))
- {
- dev_log(LOG_DEB0, "<GOLD_LOG> %s = NONE", GetName());
- }
- else if (IsOverTime(OT_3HOUR))
- {
- amount = (amount / 2);
- dev_log(LOG_DEB0, "<GOLD_LOG> %s = 3HOUR", GetName());
- }
- else if (IsOverTime(OT_5HOUR))
- {
- amount = 0;
- dev_log(LOG_DEB0, "<GOLD_LOG> %s = 5HOUR", GetName());
- }
- }
- SetGold(GetGold() + amount);
- val = GetGold();
- }
- break;
- case POINT_SKILL:
- case POINT_STAT:
- case POINT_SUB_SKILL:
- case POINT_STAT_RESET_COUNT:
- case POINT_HORSE_SKILL:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- SetRealPoint(type, val);
- break;
- case POINT_DEF_GRADE:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- PointChange(POINT_CLIENT_DEF_GRADE, amount);
- break;
- case POINT_CLIENT_DEF_GRADE:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- break;
- case POINT_ST:
- case POINT_HT:
- case POINT_DX:
- case POINT_IQ:
- case POINT_HP_REGEN:
- case POINT_SP_REGEN:
- case POINT_ATT_SPEED:
- case POINT_ATT_GRADE:
- case POINT_MOV_SPEED:
- case POINT_CASTING_SPEED:
- case POINT_MAGIC_ATT_GRADE:
- case POINT_MAGIC_DEF_GRADE:
- case POINT_BOW_DISTANCE:
- case POINT_HP_RECOVERY:
- case POINT_SP_RECOVERY:
- case POINT_ATTBONUS_HUMAN: // 42 인간에게 강함
- case POINT_ATTBONUS_ANIMAL: // 43 동물에게 데미지 % 증가
- case POINT_ATTBONUS_ORC: // 44 웅귀에게 데미지 % 증가
- case POINT_ATTBONUS_MILGYO: // 45 밀교에게 데미지 % 증가
- case POINT_ATTBONUS_UNDEAD: // 46 시체에게 데미지 % 증가
- case POINT_ATTBONUS_DEVIL: // 47 마귀(악마)에게 데미지 % 증가
- case POINT_ATTBONUS_MONSTER:
- case POINT_ATTBONUS_SURA:
- case POINT_ATTBONUS_ASSASSIN:
- case POINT_ATTBONUS_WARRIOR:
- case POINT_ATTBONUS_SHAMAN:
- case POINT_POISON_PCT:
- case POINT_STUN_PCT:
- case POINT_SLOW_PCT:
- case POINT_BLOCK:
- case POINT_DODGE:
- case POINT_CRITICAL_PCT:
- case POINT_RESIST_CRITICAL:
- case POINT_PENETRATE_PCT:
- case POINT_RESIST_PENETRATE:
- case POINT_CURSE_PCT:
- case POINT_STEAL_HP: // 48 생명력 흡수
- case POINT_STEAL_SP: // 49 정신력 흡수
- case POINT_MANA_BURN_PCT: // 50 마나 번
- case POINT_DAMAGE_SP_RECOVER: // 51 공격당할 시 정신력 회복 확률
- case POINT_RESIST_NORMAL_DAMAGE:
- case POINT_RESIST_SWORD:
- case POINT_RESIST_TWOHAND:
- case POINT_RESIST_DAGGER:
- case POINT_RESIST_BELL:
- case POINT_RESIST_FAN:
- case POINT_RESIST_BOW:
- case POINT_RESIST_FIRE:
- case POINT_RESIST_ELEC:
- case POINT_RESIST_MAGIC:
- case POINT_RESIST_WIND:
- case POINT_RESIST_ICE:
- case POINT_RESIST_EARTH:
- case POINT_RESIST_DARK:
- case POINT_REFLECT_MELEE: // 67 공격 반사
- case POINT_REFLECT_CURSE: // 68 저주 반사
- case POINT_POISON_REDUCE: // 69 독데미지 감소
- case POINT_KILL_SP_RECOVER: // 70 적 소멸시 MP 회복
- case POINT_KILL_HP_RECOVERY: // 75
- case POINT_HIT_HP_RECOVERY:
- case POINT_HIT_SP_RECOVERY:
- case POINT_MANASHIELD:
- case POINT_ATT_BONUS:
- case POINT_DEF_BONUS:
- case POINT_SKILL_DAMAGE_BONUS:
- case POINT_NORMAL_HIT_DAMAGE_BONUS:
- // DEPEND_BONUS_ATTRIBUTES
- case POINT_SKILL_DEFEND_BONUS:
- case POINT_NORMAL_HIT_DEFEND_BONUS:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- break;
- // END_OF_DEPEND_BONUS_ATTRIBUTES
- case POINT_PARTY_ATTACKER_BONUS:
- case POINT_PARTY_TANKER_BONUS:
- case POINT_PARTY_BUFFER_BONUS:
- case POINT_PARTY_SKILL_MASTER_BONUS:
- case POINT_PARTY_HASTE_BONUS:
- case POINT_PARTY_DEFENDER_BONUS:
- case POINT_RESIST_WARRIOR :
- case POINT_RESIST_ASSASSIN :
- case POINT_RESIST_SURA :
- case POINT_RESIST_SHAMAN :
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- break;
- case POINT_MALL_ATTBONUS:
- case POINT_MALL_DEFBONUS:
- case POINT_MALL_EXPBONUS:
- case POINT_MALL_ITEMBONUS:
- case POINT_MALL_GOLDBONUS:
- case POINT_MELEE_MAGIC_ATT_BONUS_PER:
- if (GetPoint(type) + amount > 100)
- {
- sys_err("MALL_BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
- amount = 100 - GetPoint(type);
- }
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- break;
- // PC_BANG_ITEM_ADD
- case POINT_PC_BANG_EXP_BONUS :
- case POINT_PC_BANG_DROP_BONUS :
- case POINT_RAMADAN_CANDY_BONUS_EXP:
- SetPoint(type, amount);
- val = GetPoint(type);
- break;
- // END_PC_BANG_ITEM_ADD
- case POINT_EXP_DOUBLE_BONUS: // 71
- case POINT_GOLD_DOUBLE_BONUS: // 72
- case POINT_ITEM_DROP_BONUS: // 73
- case POINT_POTION_BONUS: // 74
- if (GetPoint(type) + amount > 100)
- {
- sys_err("BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
- amount = 100 - GetPoint(type);
- }
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- break;
- case POINT_IMMUNE_STUN: // 76
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- if (val)
- {
- SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
- }
- else
- {
- REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
- }
- break;
- case POINT_IMMUNE_SLOW: // 77
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- if (val)
- {
- SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
- }
- else
- {
- REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
- }
- break;
- case POINT_IMMUNE_FALL: // 78
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- if (val)
- {
- SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
- }
- else
- {
- REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
- }
- break;
- case POINT_ATT_GRADE_BONUS:
- SetPoint(type, GetPoint(type) + amount);
- PointChange(POINT_ATT_GRADE, amount);
- val = GetPoint(type);
- break;
- case POINT_DEF_GRADE_BONUS:
- SetPoint(type, GetPoint(type) + amount);
- PointChange(POINT_DEF_GRADE, amount);
- val = GetPoint(type);
- break;
- case POINT_MAGIC_ATT_GRADE_BONUS:
- SetPoint(type, GetPoint(type) + amount);
- PointChange(POINT_MAGIC_ATT_GRADE, amount);
- val = GetPoint(type);
- break;
- case POINT_MAGIC_DEF_GRADE_BONUS:
- SetPoint(type, GetPoint(type) + amount);
- PointChange(POINT_MAGIC_DEF_GRADE, amount);
- val = GetPoint(type);
- break;
- case POINT_VOICE:
- case POINT_EMPIRE_POINT:
- //sys_err("CHARACTER::PointChange: %s: point cannot be changed. use SetPoint instead (type: %d)", GetName(), type);
- val = GetRealPoint(type);
- break;
- case POINT_POLYMORPH:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- SetPolymorph(val);
- break;
- case POINT_MOUNT:
- SetPoint(type, GetPoint(type) + amount);
- val = GetPoint(type);
- MountVnum(val);
- break;
- case POINT_ENERGY:
- case POINT_COSTUME_ATTR_BONUS:
- {
- int old_val = GetPoint(type);
- SetPoint(type, old_val + amount);
- val = GetPoint(type);
- BuffOnAttr_ValueChange(type, old_val, val);
- }
- break;
- default:
- sys_err("CHARACTER::PointChange: %s: unknown point change type %d", GetName(), type);
- return;
- }
- switch (type)
- {
- case POINT_LEVEL:
- case POINT_ST:
- case POINT_DX:
- case POINT_IQ:
- case POINT_HT:
- ComputeBattlePoints();
- break;
- case POINT_MAX_HP:
- case POINT_MAX_SP:
- case POINT_MAX_STAMINA:
- break;
- }
- if (type == POINT_HP && amount == 0)
- return;
- if (GetDesc())
- {
- struct packet_point_change pack;
- pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
- pack.dwVID = m_vid;
- pack.type = type;
- pack.value = val;
- if (bAmount)
- pack.amount = amount;
- else
- pack.amount = 0;
- if (!bBroadcast)
- GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
- else
- PacketAround(&pack, sizeof(pack));
- }
- }
- void CHARACTER::ApplyPoint(BYTE bApplyType, int iVal)
- {
- switch (bApplyType)
- {
- case APPLY_NONE: // 0
- break;;
- case APPLY_CON:
- PointChange(POINT_HT, iVal);
- PointChange(POINT_MAX_HP, (iVal * JobInitialPoints[GetJob()].hp_per_ht));
- PointChange(POINT_MAX_STAMINA, (iVal * JobInitialPoints[GetJob()].stamina_per_con));
- break;
- case APPLY_INT:
- PointChange(POINT_IQ, iVal);
- PointChange(POINT_MAX_SP, (iVal * JobInitialPoints[GetJob()].sp_per_iq));
- break;
- case APPLY_SKILL:
- // SKILL_DAMAGE_BONUS
- {
- // 최상위 비트 기준으로 8비트 vnum, 9비트 add, 15비트 change
- // 00000000 00000000 00000000 00000000
- // ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
- // vnum ^ add change
- BYTE bSkillVnum = (BYTE) (((DWORD)iVal) >> 24);
- int iAdd = iVal & 0x00800000;
- int iChange = iVal & 0x007fffff;
- sys_log(1, "APPLY_SKILL skill %d add? %d change %d", bSkillVnum, iAdd ? 1 : 0, iChange);
- if (0 == iAdd)
- iChange = -iChange;
- boost::unordered_map<BYTE, int>::iterator iter = m_SkillDamageBonus.find(bSkillVnum);
- if (iter == m_SkillDamageBonus.end())
- m_SkillDamageBonus.insert(std::make_pair(bSkillVnum, iChange));
- else
- iter->second += iChange;
- }
- // END_OF_SKILL_DAMAGE_BONUS
- break;
- case APPLY_STR:
- case APPLY_DEX:
- case APPLY_MAX_HP:
- case APPLY_MAX_SP:
- case APPLY_MAX_HP_PCT:
- case APPLY_MAX_SP_PCT:
- case APPLY_ATT_SPEED:
- case APPLY_MOV_SPEED:
- case APPLY_CAST_SPEED:
- case APPLY_HP_REGEN:
- case APPLY_SP_REGEN:
- case APPLY_POISON_PCT:
- case APPLY_STUN_PCT:
- case APPLY_SLOW_PCT:
- case APPLY_CRITICAL_PCT:
- case APPLY_PENETRATE_PCT:
- case APPLY_ATTBONUS_HUMAN:
- case APPLY_ATTBONUS_ANIMAL:
- case APPLY_ATTBONUS_ORC:
- case APPLY_ATTBONUS_MILGYO:
- case APPLY_ATTBONUS_UNDEAD:
- case APPLY_ATTBONUS_DEVIL:
- case APPLY_ATTBONUS_WARRIOR: // 59
- case APPLY_ATTBONUS_ASSASSIN: // 60
- case APPLY_ATTBONUS_SURA: // 61
- case APPLY_ATTBONUS_SHAMAN: // 62
- case APPLY_ATTBONUS_MONSTER: // 63
- case APPLY_STEAL_HP:
- case APPLY_STEAL_SP:
- case APPLY_MANA_BURN_PCT:
- case APPLY_DAMAGE_SP_RECOVER:
- case APPLY_BLOCK:
- case APPLY_DODGE:
- case APPLY_RESIST_SWORD:
- case APPLY_RESIST_TWOHAND:
- case APPLY_RESIST_DAGGER:
- case APPLY_RESIST_BELL:
- case APPLY_RESIST_FAN:
- case APPLY_RESIST_BOW:
- case APPLY_RESIST_FIRE:
- case APPLY_RESIST_ELEC:
- case APPLY_RESIST_MAGIC:
- case APPLY_RESIST_WIND:
- case APPLY_RESIST_ICE:
- case APPLY_RESIST_EARTH:
- case APPLY_RESIST_DARK:
- case APPLY_REFLECT_MELEE:
- case APPLY_REFLECT_CURSE:
- case APPLY_ANTI_CRITICAL_PCT:
- case APPLY_ANTI_PENETRATE_PCT:
- case APPLY_POISON_REDUCE:
- case APPLY_KILL_SP_RECOVER:
- case APPLY_EXP_DOUBLE_BONUS:
- case APPLY_GOLD_DOUBLE_BONUS:
- case APPLY_ITEM_DROP_BONUS:
- case APPLY_POTION_BONUS:
- case APPLY_KILL_HP_RECOVER:
- case APPLY_IMMUNE_STUN:
- case APPLY_IMMUNE_SLOW:
- case APPLY_IMMUNE_FALL:
- case APPLY_BOW_DISTANCE:
- case APPLY_ATT_GRADE_BONUS:
- case APPLY_DEF_GRADE_BONUS:
- case APPLY_MAGIC_ATT_GRADE:
- case APPLY_MAGIC_DEF_GRADE:
- case APPLY_CURSE_PCT:
- case APPLY_MAX_STAMINA:
- case APPLY_MALL_ATTBONUS:
- case APPLY_MALL_DEFBONUS:
- case APPLY_MALL_EXPBONUS:
- case APPLY_MALL_ITEMBONUS:
- case APPLY_MALL_GOLDBONUS:
- case APPLY_SKILL_DAMAGE_BONUS:
- case APPLY_NORMAL_HIT_DAMAGE_BONUS:
- // DEPEND_BONUS_ATTRIBUTES
- case APPLY_SKILL_DEFEND_BONUS:
- case APPLY_NORMAL_HIT_DEFEND_BONUS:
- // END_OF_DEPEND_BONUS_ATTRIBUTES
- case APPLY_PC_BANG_EXP_BONUS :
- case APPLY_PC_BANG_DROP_BONUS :
- case APPLY_RESIST_WARRIOR :
- case APPLY_RESIST_ASSASSIN :
- case APPLY_RESIST_SURA :
- case APPLY_RESIST_SHAMAN :
- case APPLY_ENERGY: // 82 기력
- case APPLY_DEF_GRADE: // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
- case APPLY_COSTUME_ATTR_BONUS: // 84 코스튬 아이템에 붙은 속성치 보너스
- case APPLY_MAGIC_ATTBONUS_PER: // 85 마법 공격력 +x%
- case APPLY_MELEE_MAGIC_ATTBONUS_PER: // 86 마법 + 밀리 공격력 +x%
- PointChange(aApplyInfo[bApplyType].bPointType, iVal);
- break;
- default:
- sys_err("Unknown apply type %d name %s", bApplyType, GetName());
- break;
- }
- }
- void CHARACTER::MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet)
- {
- packet->header = HEADER_GC_MOTION;
- packet->vid = m_vid;
- packet->motion = motion;
- if (victim)
- packet->victim_vid = victim->GetVID();
- else
- packet->victim_vid = 0;
- }
- void CHARACTER::Motion(BYTE motion, LPCHARACTER victim)
- {
- struct packet_motion pack_motion;
- MotionPacketEncode(motion, victim, &pack_motion);
- PacketAround(&pack_motion, sizeof(struct packet_motion));
- }
- EVENTFUNC(save_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "save_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (ch == NULL) { // <Factor>
- return 0;
- }
- sys_log(1, "SAVE_EVENT: %s", ch->GetName());
- ch->Save();
- ch->FlushDelayedSaveItem();
- return (save_event_second_cycle);
- }
- void CHARACTER::StartSaveEvent()
- {
- if (m_pkSaveEvent)
- return;
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle);
- }
- void CHARACTER::MonsterLog(const char* format, ...)
- {
- if (!test_server)
- return;
- if (IsPC())
- return;
- char chatbuf[CHAT_MAX_LEN + 1];
- int len = snprintf(chatbuf, sizeof(chatbuf), "%u)", (DWORD)GetVID());
- if (len < 0 || len >= (int) sizeof(chatbuf))
- len = sizeof(chatbuf) - 1;
- va_list args;
- va_start(args, format);
- int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args);
- if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len)
- len += (sizeof(chatbuf) - len) - 1;
- else
- len += len2;
- // \0 문자 포함
- ++len;
- va_end(args);
- TPacketGCChat pack_chat;
- pack_chat.header = HEADER_GC_CHAT;
- pack_chat.size = sizeof(TPacketGCChat) + len;
- pack_chat.type = CHAT_TYPE_TALKING;
- pack_chat.id = (DWORD)GetVID();
- pack_chat.bEmpire = 0;
- TEMP_BUFFER buf;
- buf.write(&pack_chat, sizeof(TPacketGCChat));
- buf.write(chatbuf, len);
- CHARACTER_MANAGER::instance().PacketMonsterLog(this, buf.read_peek(), buf.size());
- }
- void CHARACTER::ChatPacket(BYTE type, const char * format, ...)
- {
- LPDESC d = GetDesc();
- if (!d || !format)
- return;
- char chatbuf[CHAT_MAX_LEN + 1];
- va_list args;
- va_start(args, format);
- int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args);
- va_end(args);
- struct packet_chat pack_chat;
- pack_chat.header = HEADER_GC_CHAT;
- pack_chat.size = sizeof(struct packet_chat) + len;
- pack_chat.type = type;
- pack_chat.id = 0;
- pack_chat.bEmpire = d->GetEmpire();
- TEMP_BUFFER buf;
- buf.write(&pack_chat, sizeof(struct packet_chat));
- buf.write(chatbuf, len);
- d->Packet(buf.read_peek(), buf.size());
- if (type == CHAT_TYPE_COMMAND && test_server)
- sys_log(0, "SEND_COMMAND %s %s", GetName(), chatbuf);
- }
- // MINING
- void CHARACTER::mining_take()
- {
- m_pkMiningEvent = NULL;
- }
- void CHARACTER::mining_cancel()
- {
- if (m_pkMiningEvent)
- {
- sys_log(0, "XXX MINING CANCEL");
- event_cancel(&m_pkMiningEvent);
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채광을 중단하였습니다."));
- }
- }
- void CHARACTER::mining(LPCHARACTER chLoad)
- {
- if (m_pkMiningEvent)
- {
- mining_cancel();
- return;
- }
- if (!chLoad)
- return;
- if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0)
- return;
- LPITEM pick = GetWear(WEAR_WEAPON);
- if (!pick || pick->GetType() != ITEM_PICK)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("곡괭이를 장착하세요."));
- return;
- }
- int count = number(5, 15); // 동작 횟수, 한 동작당 2초
- // 채광 동작을 보여줌
- TPacketGCDigMotion p;
- p.header = HEADER_GC_DIG_MOTION;
- p.vid = GetVID();
- p.target_vid = chLoad->GetVID();
- p.count = count;
- PacketAround(&p, sizeof(p));
- m_pkMiningEvent = mining::CreateMiningEvent(this, chLoad, count);
- }
- // END_OF_MINING
- void CHARACTER::fishing()
- {
- if (m_pkFishingEvent)
- {
- fishing_take();
- return;
- }
- // 못감 속성에서 낚시를 시도한다?
- {
- LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex());
- int x = GetX();
- int y = GetY();
- LPSECTREE tree = pkSectreeMap->Find(x, y);
- DWORD dwAttr = tree->GetAttribute(x, y);
- if (IS_SET(dwAttr, ATTR_BLOCK))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시를 할 수 있는 곳이 아닙니다"));
- return;
- }
- }
- LPITEM rod = GetWear(WEAR_WEAPON);
- // 낚시대 장착
- if (!rod || rod->GetType() != ITEM_ROD)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대를 장착 하세요."));
- return;
- }
- if (0 == rod->GetSocket(2))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("미끼를 끼고 던져 주세요."));
- return;
- }
- float fx, fy;
- GetDeltaByDegree(GetRotation(), 400.0f, &fx, &fy);
- m_pkFishingEvent = fishing::CreateFishingEvent(this);
- }
- void CHARACTER::fishing_take()
- {
- LPITEM rod = GetWear(WEAR_WEAPON);
- if (rod && rod->GetType() == ITEM_ROD)
- {
- using fishing::fishing_event_info;
- if (m_pkFishingEvent)
- {
- struct fishing_event_info* info = dynamic_cast<struct fishing_event_info*>(m_pkFishingEvent->info);
- if (info)
- fishing::Take(info, this);
- }
- }
- else
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대가 아닌 물건으로 낚시를 할 수 없습니다!"));
- }
- event_cancel(&m_pkFishingEvent);
- }
- bool CHARACTER::StartStateMachine(int iNextPulse)
- {
- if (CHARACTER_MANAGER::instance().AddToStateList(this))
- {
- m_dwNextStatePulse = thecore_heart->pulse + iNextPulse;
- return true;
- }
- return false;
- }
- void CHARACTER::StopStateMachine()
- {
- CHARACTER_MANAGER::instance().RemoveFromStateList(this);
- }
- void CHARACTER::UpdateStateMachine(DWORD dwPulse)
- {
- if (dwPulse < m_dwNextStatePulse)
- return;
- if (IsDead())
- return;
- Update();
- m_dwNextStatePulse = dwPulse + m_dwStateDuration;
- }
- void CHARACTER::SetNextStatePulse(int iNextPulse)
- {
- CHARACTER_MANAGER::instance().AddToStateList(this);
- m_dwNextStatePulse = iNextPulse;
- if (iNextPulse < 10)
- MonsterLog("다음상태로어서가자");
- }
- // 캐릭터 인스턴스 업데이트 함수.
- void CHARACTER::UpdateCharacter(DWORD dwPulse)
- {
- CFSM::Update();
- }
- void CHARACTER::SetShop(LPSHOP pkShop)
- {
- if ((m_pkShop = pkShop))
- SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
- else
- {
- REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
- SetShopOwner(NULL);
- }
- }
- void CHARACTER::SetExchange(CExchange * pkExchange)
- {
- m_pkExchange = pkExchange;
- }
- void CHARACTER::SetPart(BYTE bPartPos, WORD wVal)
- {
- assert(bPartPos < PART_MAX_NUM);
- m_pointsInstant.parts[bPartPos] = wVal;
- }
- WORD CHARACTER::GetPart(BYTE bPartPos) const
- {
- assert(bPartPos < PART_MAX_NUM);
- return m_pointsInstant.parts[bPartPos];
- }
- WORD CHARACTER::GetOriginalPart(BYTE bPartPos) const
- {
- switch (bPartPos)
- {
- case PART_MAIN:
- if (!IsPC()) // PC가 아닌 경우 현재 파트를 그대로 리턴
- return GetPart(PART_MAIN);
- else
- return m_pointsInstant.bBasePart;
- case PART_HAIR:
- return GetPart(PART_HAIR);
- default:
- return 0;
- }
- }
- BYTE CHARACTER::GetCharType() const
- {
- return m_bCharType;
- }
- bool CHARACTER::SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList)
- {
- // TRENT_MONSTER
- if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
- return false;
- // END_OF_TRENT_MONSTER
- if (ch == this)
- {
- sys_err("SetSyncOwner owner == this (%p)", this);
- return false;
- }
- if (!ch)
- {
- if (bRemoveFromList && m_pkChrSyncOwner)
- {
- m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
- }
- if (m_pkChrSyncOwner)
- sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
- // 리스트에서 제거하지 않더라도 포인터는 NULL로 셋팅되어야 한다.
- m_pkChrSyncOwner = NULL;
- }
- else
- {
- if (!IsSyncOwner(ch))
- return false;
- // 거리가 200 이상이면 SyncOwner가 될 수 없다.
- if (DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY()) > 250)
- {
- sys_log(1, "SetSyncOwner distance over than 250 %s %s", GetName(), ch->GetName());
- // SyncOwner일 경우 Owner로 표시한다.
- if (m_pkChrSyncOwner == ch)
- return true;
- return false;
- }
- if (m_pkChrSyncOwner != ch)
- {
- if (m_pkChrSyncOwner)
- {
- sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
- m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
- }
- m_pkChrSyncOwner = ch;
- m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.push_back(this);
- // SyncOwner가 바뀌면 LastSyncTime을 초기화한다.
- static const timeval zero_tv = {0, 0};
- SetLastSyncTime(zero_tv);
- sys_log(1, "SetSyncOwner set %s %p to %s", GetName(), this, ch->GetName());
- }
- m_fSyncTime = get_float_time();
- }
- // TODO: Sync Owner가 같더라도 계속 패킷을 보내고 있으므로,
- // 동기화 된 시간이 3초 이상 지났을 때 풀어주는 패킷을
- // 보내는 방식으로 하면 패킷을 줄일 수 있다.
- TPacketGCOwnership pack;
- pack.bHeader = HEADER_GC_OWNERSHIP;
- pack.dwOwnerVID = ch ? ch->GetVID() : 0;
- pack.dwVictimVID = GetVID();
- PacketAround(&pack, sizeof(TPacketGCOwnership));
- return true;
- }
- struct FuncClearSync
- {
- void operator () (LPCHARACTER ch)
- {
- assert(ch != NULL);
- ch->SetSyncOwner(NULL, false); // false 플래그로 해야 for_each 가 제대로 돈다.
- }
- };
- void CHARACTER::ClearSync()
- {
- SetSyncOwner(NULL);
- // 아래 for_each에서 나를 m_pkChrSyncOwner로 가진 자들의 포인터를 NULL로 한다.
- std::for_each(m_kLst_pkChrSyncOwned.begin(), m_kLst_pkChrSyncOwned.end(), FuncClearSync());
- m_kLst_pkChrSyncOwned.clear();
- }
- bool CHARACTER::IsSyncOwner(LPCHARACTER ch) const
- {
- if (m_pkChrSyncOwner == ch)
- return true;
- // 마지막으로 동기화 된 시간이 3초 이상 지났다면 소유권이 아무에게도
- // 없다. 따라서 아무나 SyncOwner이므로 true 리턴
- if (get_float_time() - m_fSyncTime >= 3.0f)
- return true;
- return false;
- }
- void CHARACTER::SetParty(LPPARTY pkParty)
- {
- if (pkParty == m_pkParty)
- return;
- if (pkParty && m_pkParty)
- sys_err("%s is trying to reassigning party (current %p, new party %p)", GetName(), get_pointer(m_pkParty), get_pointer(pkParty));
- sys_log(1, "PARTY set to %p", get_pointer(pkParty));
- //if (m_pkDungeon && IsPC())
- //SetDungeon(NULL);
- if (m_pkDungeon && IsPC() && !pkParty) // Fix
- SetDungeon(NULL);
- m_pkParty = pkParty;
- if (IsPC())
- {
- if (m_pkParty)
- SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
- else
- REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
- UpdatePacket();
- }
- }
- // PARTY_JOIN_BUG_FIX
- /// 파티 가입 event 정보
- EVENTINFO(TPartyJoinEventInfo)
- {
- DWORD dwGuestPID; ///< 파티에 참여할 캐릭터의 PID
- DWORD dwLeaderPID; ///< 파티 리더의 PID
- TPartyJoinEventInfo()
- : dwGuestPID( 0 )
- , dwLeaderPID( 0 )
- {
- }
- } ;
- EVENTFUNC(party_request_event)
- {
- TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>( event->info );
- if ( info == NULL )
- {
- sys_err( "party_request_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID);
- if (ch)
- {
- sys_log(0, "PartyRequestEvent %s", ch->GetName());
- ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
- ch->SetPartyRequestEvent(NULL);
- }
- return 0;
- }
- bool CHARACTER::RequestToParty(LPCHARACTER leader)
- {
- if (leader->GetParty())
- leader = leader->GetParty()->GetLeaderCharacter();
- if (!leader)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("파티장이 접속 상태가 아니라서 요청을 할 수 없습니다."));
- return false;
- }
- if (m_pkPartyRequestEvent)
- return false;
- if (!IsPC() || !leader->IsPC())
- return false;
- if (leader->IsBlockMode(BLOCK_PARTY_REQUEST))
- return false;
- PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this);
- switch (errcode)
- {
- case PERR_NONE:
- break;
- case PERR_SERVER:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
- return false;
- case PERR_DIFFEMPIRE:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
- return false;
- case PERR_DUNGEON:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
- return false;
- case PERR_OBSERVER:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
- return false;
- case PERR_LVBOUNDARY:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
- return false;
- case PERR_LOWLEVEL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
- return false;
- case PERR_HILEVEL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
- return false;
- case PERR_ALREADYJOIN:
- return false;
- case PERR_PARTYISFULL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
- return false;
- default:
- sys_err("Do not process party join error(%d)", errcode);
- return false;
- }
- TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
- info->dwGuestPID = GetPlayerID();
- info->dwLeaderPID = leader->GetPlayerID();
- SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10)));
- leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID());
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님에게 파티가입 신청을 했습니다."), leader->GetName());
- return true;
- }
- void CHARACTER::DenyToParty(LPCHARACTER member)
- {
- sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
- if (!member->m_pkPartyRequestEvent)
- return;
- TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
- if (!info)
- {
- sys_err( "CHARACTER::DenyToParty> <Factor> Null pointer" );
- return;
- }
- if (info->dwGuestPID != member->GetPlayerID())
- return;
- if (info->dwLeaderPID != GetPlayerID())
- return;
- event_cancel(&member->m_pkPartyRequestEvent);
- member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
- }
- void CHARACTER::AcceptToParty(LPCHARACTER member)
- {
- sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
- if (!member->m_pkPartyRequestEvent)
- return;
- TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
- if (!info)
- {
- sys_err( "CHARACTER::AcceptToParty> <Factor> Null pointer" );
- return;
- }
- if (info->dwGuestPID != member->GetPlayerID())
- return;
- if (info->dwLeaderPID != GetPlayerID())
- return;
- event_cancel(&member->m_pkPartyRequestEvent);
- if (!GetParty())
- member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 파티에 속해있지 않습니다."));
- else
- {
- if (GetPlayerID() != GetParty()->GetLeaderPID())
- return;
- PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member);
- switch (errcode)
- {
- case PERR_NONE: member->PartyJoin(this); return;
- case PERR_SERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다.")); break;
- case PERR_DUNGEON: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다.")); break;
- case PERR_OBSERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다.")); break;
- case PERR_LVBOUNDARY: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다.")); break;
- case PERR_LOWLEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다.")); break;
- case PERR_HILEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다.")); break;
- case PERR_ALREADYJOIN: break;
- case PERR_PARTYISFULL: {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
- member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
- break;
- }
- default: sys_err("Do not process party join error(%d)", errcode);
- }
- }
- member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
- }
- /**
- * 파티 초대 event callback 함수.
- * event 가 발동하면 초대 거절로 처리한다.
- */
- EVENTFUNC(party_invite_event)
- {
- TPartyJoinEventInfo * pInfo = dynamic_cast<TPartyJoinEventInfo *>( event->info );
- if ( pInfo == NULL )
- {
- sys_err( "party_invite_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID);
- if (pchInviter)
- {
- sys_log(1, "PartyInviteEvent %s", pchInviter->GetName());
- pchInviter->PartyInviteDeny(pInfo->dwGuestPID);
- }
- return 0;
- }
- void CHARACTER::PartyInvite(LPCHARACTER pchInvitee)
- {
- if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
- return;
- }
- else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s 님이 파티 거부 상태입니다."), pchInvitee->GetName());
- return;
- }
- PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee);
- switch (errcode)
- {
- case PERR_NONE:
- break;
- case PERR_SERVER:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
- return;
- case PERR_DIFFEMPIRE:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
- return;
- case PERR_DUNGEON:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
- return;
- case PERR_OBSERVER:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
- return;
- case PERR_LVBOUNDARY:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
- return;
- case PERR_LOWLEVEL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
- return;
- case PERR_HILEVEL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
- return;
- case PERR_ALREADYJOIN:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 이미 %s님은 파티에 속해 있습니다."), pchInvitee->GetName());
- return;
- case PERR_PARTYISFULL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
- return;
- default:
- sys_err("Do not process party join error(%d)", errcode);
- return;
- }
- if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID()))
- return;
- //
- // EventMap 에 이벤트 추가
- //
- TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
- info->dwGuestPID = pchInvitee->GetPlayerID();
- info->dwLeaderPID = GetPlayerID();
- m_PartyInviteEventMap.insert(EventMap::value_type(pchInvitee->GetPlayerID(), event_create(party_invite_event, info, PASSES_PER_SEC(10))));
- //
- // 초대 받는 character 에게 초대 패킷 전송
- //
- TPacketGCPartyInvite p;
- p.header = HEADER_GC_PARTY_INVITE;
- p.leader_vid = GetVID();
- pchInvitee->GetDesc()->Packet(&p, sizeof(p));
- }
- void CHARACTER::PartyInviteAccept(LPCHARACTER pchInvitee)
- {
- EventMap::iterator itFind = m_PartyInviteEventMap.find(pchInvitee->GetPlayerID());
- if (itFind == m_PartyInviteEventMap.end())
- {
- sys_log(1, "PartyInviteAccept from not invited character(%s)", pchInvitee->GetName());
- return;
- }
- event_cancel(&itFind->second);
- m_PartyInviteEventMap.erase(itFind);
- if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
- return;
- }
- PartyJoinErrCode errcode = IsPartyJoinableMutableCondition(this, pchInvitee);
- switch (errcode)
- {
- case PERR_NONE:
- break;
- case PERR_SERVER:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
- return;
- case PERR_DUNGEON:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대에 응할 수 없습니다."));
- return;
- case PERR_OBSERVER:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
- return;
- case PERR_LVBOUNDARY:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
- return;
- case PERR_LOWLEVEL:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
- return;
- case PERR_HILEVEL:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
- return;
- case PERR_ALREADYJOIN:
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티 초대에 응할 수 없습니다."));
- return;
- case PERR_PARTYISFULL:
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
- pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
- return;
- default:
- sys_err("ignore party join error(%d)", errcode);
- return;
- }
- //
- // 파티 가입 처리
- //
- if (GetParty())
- pchInvitee->PartyJoin(this);
- else
- {
- LPPARTY pParty = CPartyManager::instance().CreateParty(this);
- pParty->Join(pchInvitee->GetPlayerID());
- pParty->Link(pchInvitee);
- pParty->SendPartyInfoAllToOne(this);
- }
- }
- void CHARACTER::PartyInviteDeny(DWORD dwPID)
- {
- EventMap::iterator itFind = m_PartyInviteEventMap.find(dwPID);
- if (itFind == m_PartyInviteEventMap.end())
- {
- sys_log(1, "PartyInviteDeny to not exist event(inviter PID: %d, invitee PID: %d)", GetPlayerID(), dwPID);
- return;
- }
- event_cancel(&itFind->second);
- m_PartyInviteEventMap.erase(itFind);
- LPCHARACTER pchInvitee = CHARACTER_MANAGER::instance().FindByPID(dwPID);
- if (pchInvitee)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티 초대를 거절하셨습니다."), pchInvitee->GetName());
- }
- void CHARACTER::PartyJoin(LPCHARACTER pLeader)
- {
- pLeader->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티에 참가하셨습니다."), GetName());
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님의 파티에 참가하셨습니다."), pLeader->GetName());
- pLeader->GetParty()->Join(GetPlayerID());
- pLeader->GetParty()->Link(this);
- }
- CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
- {
- if (pchLeader->GetEmpire() != pchGuest->GetEmpire())
- return PERR_DIFFEMPIRE;
- return IsPartyJoinableMutableCondition(pchLeader, pchGuest);
- }
- static bool __party_can_join_by_level(LPCHARACTER leader, LPCHARACTER quest)
- {
- int level_limit = 30;
- if (LC_IsCanada())
- level_limit = 15;
- else if (LC_IsBrazil() == true)
- {
- level_limit = 10;
- }
- else
- level_limit = 30;
- return (abs(leader->GetLevel() - quest->GetLevel()) <= level_limit);
- }
- CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
- {
- if (!CPartyManager::instance().IsEnablePCParty())
- return PERR_SERVER;
- else if (pchLeader->GetDungeon())
- return PERR_DUNGEON;
- else if (pchGuest->IsObserverMode())
- return PERR_OBSERVER;
- else if (false == __party_can_join_by_level(pchLeader, pchGuest))
- return PERR_LVBOUNDARY;
- else if (pchGuest->GetParty())
- return PERR_ALREADYJOIN;
- else if (pchLeader->GetParty())
- {
- if (pchLeader->GetParty()->GetMemberCount() == PARTY_MAX_MEMBER)
- return PERR_PARTYISFULL;
- }
- return PERR_NONE;
- }
- // END_OF_PARTY_JOIN_BUG_FIX
- void CHARACTER::SetDungeon(LPDUNGEON pkDungeon)
- {
- if (pkDungeon && m_pkDungeon)
- sys_err("%s is trying to reassigning dungeon (current %p, new party %p)", GetName(), get_pointer(m_pkDungeon), get_pointer(pkDungeon));
- if (m_pkDungeon == pkDungeon) {
- return;
- }
- if (m_pkDungeon)
- {
- if (IsPC())
- {
- if (GetParty())
- m_pkDungeon->DecPartyMember(GetParty(), this);
- else
- m_pkDungeon->DecMember(this);
- }
- else if (IsMonster() || IsStone())
- {
- m_pkDungeon->DecMonster();
- }
- }
- m_pkDungeon = pkDungeon;
- if (pkDungeon)
- {
- sys_log(0, "%s DUNGEON set to %p, PARTY is %p", GetName(), get_pointer(pkDungeon), get_pointer(m_pkParty));
- if (IsPC())
- {
- if (GetParty())
- m_pkDungeon->IncPartyMember(GetParty(), this);
- else
- m_pkDungeon->IncMember(this);
- }
- else if (IsMonster() || IsStone())
- {
- m_pkDungeon->IncMonster();
- }
- }
- }
- void CHARACTER::SetWarMap(CWarMap * pWarMap)
- {
- if (m_pWarMap)
- m_pWarMap->DecMember(this);
- m_pWarMap = pWarMap;
- if (m_pWarMap)
- m_pWarMap->IncMember(this);
- }
- void CHARACTER::SetWeddingMap(marriage::WeddingMap* pMap)
- {
- if (m_pWeddingMap)
- m_pWeddingMap->DecMember(this);
- m_pWeddingMap = pMap;
- if (m_pWeddingMap)
- m_pWeddingMap->IncMember(this);
- }
- void CHARACTER::SetRegen(LPREGEN pkRegen)
- {
- m_pkRegen = pkRegen;
- if (pkRegen != NULL) {
- regen_id_ = pkRegen->id;
- }
- m_fRegenAngle = GetRotation();
- m_posRegen = GetXYZ();
- }
- bool CHARACTER::OnIdle()
- {
- return false;
- }
- void CHARACTER::OnMove(bool bIsAttack)
- {
- m_dwLastMoveTime = get_dword_time();
- if (bIsAttack)
- {
- m_dwLastAttackTime = m_dwLastMoveTime;
- if (IsAffectFlag(AFF_REVIVE_INVISIBLE))
- RemoveAffect(AFFECT_REVIVE_INVISIBLE);
- if (IsAffectFlag(AFF_EUNHYUNG))
- {
- RemoveAffect(SKILL_EUNHYUNG);
- SetAffectedEunhyung();
- }
- else
- {
- ClearAffectedEunhyung();
- }
- /*if (IsAffectFlag(AFF_JEONSIN))
- RemoveAffect(SKILL_JEONSINBANGEO);*/
- }
- /*if (IsAffectFlag(AFF_GUNGON))
- RemoveAffect(SKILL_GUNGON);*/
- // MINING
- mining_cancel();
- // END_OF_MINING
- }
- void CHARACTER::OnClick(LPCHARACTER pkChrCauser)
- {
- if (!pkChrCauser)
- {
- sys_err("OnClick %s by NULL", GetName());
- return;
- }
- DWORD vid = GetVID();
- sys_log(0, "OnClick %s[vnum %d ServerUniqueID %d, pid %d] by %s", GetName(), GetRaceNum(), vid, GetPlayerID(), pkChrCauser->GetName());
- // 상점을 연상태로 퀘스트를 진행할 수 없다.
- {
- // 단, 자신은 자신의 상점을 클릭할 수 있다.
- if (pkChrCauser->GetMyShop() && pkChrCauser != this)
- {
- sys_err("OnClick Fail (%s->%s) - pc has shop", pkChrCauser->GetName(), GetName());
- return;
- }
- }
- // 교환중일때 퀘스트를 진행할 수 없다.
- {
- if (pkChrCauser->GetExchange())
- {
- sys_err("OnClick Fail (%s->%s) - pc is exchanging", pkChrCauser->GetName(), GetName());
- return;
- }
- }
- if (IsPC())
- {
- // 타겟으로 설정된 경우는 PC에 의한 클릭도 퀘스트로 처리하도록 합니다.
- if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID()))
- {
- // 2005.03.17.myevan.타겟이 아닌 경우는 개인 상점 처리 기능을 작동시킨다.
- if (GetMyShop())
- {
- if (pkChrCauser->IsDead() == true) return;
- //PREVENT_TRADE_WINDOW
- if (pkChrCauser == this) // 자기는 가능
- {
- if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen())
- {
- pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중(창고,교환,상점)에는 개인상점을 사용할 수 없습니다."));
- return;
- }
- }
- else // 다른 사람이 클릭했을때
- {
- // 클릭한 사람이 교환/창고/개인상점/상점이용중이라면 불가
- if ((pkChrCauser->GetExchange() || pkChrCauser->IsOpenSafebox() || pkChrCauser->GetMyShop() || pkChrCauser->GetShopOwner()) || pkChrCauser->IsCubeOpen() )
- {
- pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중(창고,교환,상점)에는 개인상점을 사용할 수 없습니다."));
- return;
- }
- // 클릭한 대상이 교환/창고/상점이용중이라면 불가
- //if ((GetExchange() || IsOpenSafebox() || GetShopOwner()))
- if ((GetExchange() || IsOpenSafebox() || IsCubeOpen()))
- {
- pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 다른 거래를 하고 있는 중입니다."));
- return;
- }
- }
- //END_PREVENT_TRADE_WINDOW
- if (pkChrCauser->GetShop())
- {
- pkChrCauser->GetShop()->RemoveGuest(pkChrCauser);
- pkChrCauser->SetShop(NULL);
- }
- GetMyShop()->AddGuest(pkChrCauser, GetVID(), false);
- pkChrCauser->SetShopOwner(this);
- return;
- }
- if (test_server)
- sys_err("%s.OnClickFailure(%s) - target is PC", pkChrCauser->GetName(), GetName());
- return;
- }
- }
- // 청소년은 퀘스트 못함
- if (LC_IsNewCIBN())
- {
- if (pkChrCauser->IsOverTime(OT_3HOUR))
- {
- sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 3);
- return;
- }
- else if (pkChrCauser->IsOverTime(OT_5HOUR))
- {
- sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 5);
- return;
- }
- }
- pkChrCauser->SetQuestNPCID(GetVID());
- if (quest::CQuestManager::instance().Click(pkChrCauser->GetPlayerID(), this))
- {
- return;
- }
- // NPC 전용 기능 수행 : 상점 열기 등
- if (!IsPC())
- {
- if (!m_triggerOnClick.pFunc)
- {
- // NPC 트리거 시스템 로그 보기
- //sys_err("%s.OnClickFailure(%s) : triggerOnClick.pFunc is EMPTY(pid=%d)",
- // pkChrCauser->GetName(),
- // GetName(),
- // pkChrCauser->GetPlayerID());
- return;
- }
- m_triggerOnClick.pFunc(this, pkChrCauser);
- }
- }
- BYTE CHARACTER::GetGMLevel() const
- {
- if (test_server)
- return GM_IMPLEMENTOR;
- return m_pointsInstant.gm_level;
- }
- void CHARACTER::SetGMLevel()
- {
- if (GetDesc())
- {
- m_pointsInstant.gm_level = gm_get_level(GetName(), GetDesc()->GetHostName(), GetDesc()->GetAccountTable().login);
- }
- else
- {
- m_pointsInstant.gm_level = GM_PLAYER;
- }
- }
- BOOL CHARACTER::IsGM() const
- {
- if (m_pointsInstant.gm_level != GM_PLAYER)
- return true;
- if (test_server)
- return true;
- return false;
- }
- void CHARACTER::SetStone(LPCHARACTER pkChrStone)
- {
- m_pkChrStone = pkChrStone;
- if (m_pkChrStone)
- {
- if (pkChrStone->m_set_pkChrSpawnedBy.find(this) == pkChrStone->m_set_pkChrSpawnedBy.end())
- pkChrStone->m_set_pkChrSpawnedBy.insert(this);
- }
- }
- struct FuncDeadSpawnedByStone
- {
- void operator () (LPCHARACTER ch)
- {
- ch->Dead(NULL);
- ch->SetStone(NULL);
- }
- };
- void CHARACTER::ClearStone()
- {
- if (!m_set_pkChrSpawnedBy.empty())
- {
- // 내가 스폰시킨 몬스터들을 모두 죽인다.
- FuncDeadSpawnedByStone f;
- std::for_each(m_set_pkChrSpawnedBy.begin(), m_set_pkChrSpawnedBy.end(), f);
- m_set_pkChrSpawnedBy.clear();
- }
- if (!m_pkChrStone)
- return;
- m_pkChrStone->m_set_pkChrSpawnedBy.erase(this);
- m_pkChrStone = NULL;
- }
- void CHARACTER::ClearTarget()
- {
- if (m_pkChrTarget)
- {
- m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
- m_pkChrTarget = NULL;
- }
- TPacketGCTarget p;
- p.header = HEADER_GC_TARGET;
- p.dwVID = 0;
- p.bHPPercent = 0;
- CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
- while (it != m_set_pkChrTargetedBy.end())
- {
- LPCHARACTER pkChr = *(it++);
- pkChr->m_pkChrTarget = NULL;
- if (!pkChr->GetDesc())
- {
- sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
- abort();
- }
- pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
- }
- m_set_pkChrTargetedBy.clear();
- }
- void CHARACTER::SetTarget(LPCHARACTER pkChrTarget)
- {
- if (m_pkChrTarget == pkChrTarget)
- return;
- // CASTLE
- if (IS_CASTLE_MAP(GetMapIndex()) && !IsGM())
- return;
- // CASTLE
- if (m_pkChrTarget)
- m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
- m_pkChrTarget = pkChrTarget;
- TPacketGCTarget p;
- p.header = HEADER_GC_TARGET;
- if (m_pkChrTarget)
- {
- m_pkChrTarget->m_set_pkChrTargetedBy.insert(this);
- p.dwVID = m_pkChrTarget->GetVID();
- if (m_pkChrTarget->IsPC() && !m_pkChrTarget->IsPolymorphed() || m_pkChrTarget->GetMaxHP() <= 0)
- p.bHPPercent = 0;
- else
- {
- if (m_pkChrTarget->GetRaceNum() == 20101 ||
- m_pkChrTarget->GetRaceNum() == 20102 ||
- m_pkChrTarget->GetRaceNum() == 20103 ||
- m_pkChrTarget->GetRaceNum() == 20104 ||
- m_pkChrTarget->GetRaceNum() == 20105 ||
- m_pkChrTarget->GetRaceNum() == 20106 ||
- m_pkChrTarget->GetRaceNum() == 20107 ||
- m_pkChrTarget->GetRaceNum() == 20108 ||
- m_pkChrTarget->GetRaceNum() == 20109)
- {
- LPCHARACTER owner = m_pkChrTarget->GetVictim();
- if (owner)
- {
- int iHorseHealth = owner->GetHorseHealth();
- int iHorseMaxHealth = owner->GetHorseMaxHealth();
- if (iHorseMaxHealth)
- p.bHPPercent = MINMAX(0, iHorseHealth * 100 / iHorseMaxHealth, 100);
- else
- p.bHPPercent = 100;
- }
- else
- p.bHPPercent = 100;
- }
- else
- p.bHPPercent = MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100);
- }
- }
- else
- {
- p.dwVID = 0;
- p.bHPPercent = 0;
- }
- GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
- }
- void CHARACTER::BroadcastTargetPacket()
- {
- if (m_set_pkChrTargetedBy.empty())
- return;
- TPacketGCTarget p;
- p.header = HEADER_GC_TARGET;
- p.dwVID = GetVID();
- if (IsPC())
- p.bHPPercent = 0;
- else
- p.bHPPercent = MINMAX(0, (GetHP() * 100) / GetMaxHP(), 100);
- CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
- while (it != m_set_pkChrTargetedBy.end())
- {
- LPCHARACTER pkChr = *it++;
- if (!pkChr->GetDesc())
- {
- sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
- abort();
- }
- pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
- }
- }
- void CHARACTER::CheckTarget()
- {
- if (!m_pkChrTarget)
- return;
- if (DISTANCE_APPROX(GetX() - m_pkChrTarget->GetX(), GetY() - m_pkChrTarget->GetY()) >= 4800)
- SetTarget(NULL);
- }
- void CHARACTER::SetWarpLocation(long lMapIndex, long x, long y)
- {
- m_posWarp.x = x * 100;
- m_posWarp.y = y * 100;
- m_lWarpMapIndex = lMapIndex;
- }
- void CHARACTER::SaveExitLocation()
- {
- m_posExit = GetXYZ();
- m_lExitMapIndex = GetMapIndex();
- }
- void CHARACTER::ExitToSavedLocation()
- {
- sys_log (0, "ExitToSavedLocation");
- WarpSet(m_posWarp.x, m_posWarp.y, m_lWarpMapIndex);
- m_posExit.x = m_posExit.y = m_posExit.z = 0;
- m_lExitMapIndex = 0;
- }
- // fixme
- // 지금까진 privateMapIndex 가 현재 맵 인덱스와 같은지 체크 하는 것을 외부에서 하고,
- // 다르면 warpset을 불렀는데
- // 이를 warpset 안으로 넣자.
- bool CHARACTER::WarpSet(long x, long y, long lPrivateMapIndex)
- {
- if (!IsPC())
- return false;
- long lAddr;
- long lMapIndex;
- WORD wPort;
- if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort))
- {
- sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex, x, y, GetName());
- return false;
- }
- //Send Supplementary Data Block if new map requires security packages in loading this map
- {
- long lCurAddr;
- long lCurMapIndex = 0;
- WORD wCurPort;
- CMapLocation::instance().Get(GetX(), GetY(), lCurMapIndex, lCurAddr, wCurPort);
- //do not send SDB files if char is in the same map
- if( lCurMapIndex != lMapIndex )
- {
- const TMapRegion * rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex);
- {
- DESC_MANAGER::instance().SendClientPackageSDBToLoadMap( GetDesc(), rMapRgn->strMapName.c_str() );
- }
- }
- }
- if (lPrivateMapIndex >= 10000)
- {
- if (lPrivateMapIndex / 10000 != lMapIndex)
- {
- sys_err("Invalid map inedx %d, must be child of %d", lPrivateMapIndex, lMapIndex);
- return false;
- }
- lMapIndex = lPrivateMapIndex;
- }
- Stop();
- Save();
- if (GetSectree())
- {
- GetSectree()->RemoveEntity(this);
- ViewCleanup();
- EncodeRemovePacket(this);
- }
- m_lWarpMapIndex = lMapIndex;
- m_posWarp.x = x;
- m_posWarp.y = y;
- sys_log(0, "WarpSet %s %d %d current map %d target map %d", GetName(), x, y, GetMapIndex(), lMapIndex);
- TPacketGCWarp p;
- p.bHeader = HEADER_GC_WARP;
- p.lX = x;
- p.lY = y;
- p.lAddr = lAddr;
- #ifdef ENABLE_PROXY_IP
- if (!g_stProxyIP.empty())
- p.lAddr = inet_addr(g_stProxyIP.c_str());
- #endif
- p.wPort = wPort;
- GetDesc()->Packet(&p, sizeof(TPacketGCWarp));
- //if (!LC_IsNewCIBN())
- {
- char buf[256];
- snprintf(buf, sizeof(buf), "%s MapIdx %ld DestMapIdx%ld DestX%ld DestY%ld Empire%d", GetName(), GetMapIndex(), lPrivateMapIndex, x, y, GetEmpire());
- LogManager::instance().CharLog(this, 0, "WARP", buf);
- }
- return true;
- }
- void CHARACTER::WarpEnd()
- {
- if (test_server)
- sys_log(0, "WarpEnd %s", GetName());
- if (m_posWarp.x == 0 && m_posWarp.y == 0)
- return;
- int index = m_lWarpMapIndex;
- if (index > 10000)
- index /= 10000;
- if (!map_allow_find(index))
- {
- // 이 곳으로 워프할 수 없으므로 워프하기 전 좌표로 되돌리자.
- sys_err("location %d %d not allowed to login this server", m_posWarp.x, m_posWarp.y);
- GetDesc()->SetPhase(PHASE_CLOSE);
- return;
- }
- sys_log(0, "WarpEnd %s %d %u %u", GetName(), m_lWarpMapIndex, m_posWarp.x, m_posWarp.y);
- Show(m_lWarpMapIndex, m_posWarp.x, m_posWarp.y, 0);
- Stop();
- m_lWarpMapIndex = 0;
- m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
- {
- // P2P Login
- TPacketGGLogin p;
- p.bHeader = HEADER_GG_LOGIN;
- strlcpy(p.szName, GetName(), sizeof(p.szName));
- p.dwPID = GetPlayerID();
- p.bEmpire = GetEmpire();
- p.lMapIndex = SECTREE_MANAGER::instance().GetMapIndex(GetX(), GetY());
- p.bChannel = g_bChannel;
- P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogin));
- }
- }
- bool CHARACTER::Return()
- {
- if (!IsNPC())
- return false;
- int x, y;
- /*
- float fDist = DISTANCE_SQRT(m_pkMobData->m_posLastAttacked.x - GetX(), m_pkMobData->m_posLastAttacked.y - GetY());
- float fx, fy;
- GetDeltaByDegree(GetRotation(), fDist, &fx, &fy);
- x = GetX() + (int) fx;
- y = GetY() + (int) fy;
- */
- SetVictim(NULL);
- x = m_pkMobInst->m_posLastAttacked.x;
- y = m_pkMobInst->m_posLastAttacked.y;
- SetRotationToXY(x, y);
- if (!Goto(x, y))
- return false;
- SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
- if (test_server)
- sys_log(0, "%s %p 포기하고 돌아가자! %d %d", GetName(), this, x, y);
- if (GetParty())
- GetParty()->SendMessage(this, PM_RETURN, x, y);
- return true;
- }
- bool CHARACTER::Follow(LPCHARACTER pkChr, float fMinDistance)
- {
- if (IsPC())
- {
- sys_err("CHARACTER::Follow : PC cannot use this method", GetName());
- return false;
- }
- // TRENT_MONSTER
- if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
- {
- if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
- {
- // If i'm in a party. I must obey party leader's AI.
- if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
- {
- if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
- {
- // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
- if (m_pkMobData->m_table.wAttackRange < DISTANCE_APPROX(pkChr->GetX() - GetX(), pkChr->GetY() - GetY()))
- if (Return())
- return true;
- }
- }
- }
- return false;
- }
- // END_OF_TRENT_MONSTER
- long x = pkChr->GetX();
- long y = pkChr->GetY();
- if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
- {
- // If i'm in a party. I must obey party leader's AI.
- if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
- {
- if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
- {
- // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
- if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
- if (Return())
- return true;
- }
- }
- }
- if (IsGuardNPC())
- {
- if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
- if (Return())
- return true;
- }
- if (pkChr->IsState(pkChr->m_stateMove) &&
- GetMobBattleType() != BATTLE_TYPE_RANGE &&
- GetMobBattleType() != BATTLE_TYPE_MAGIC &&
- false == IsPet())
- {
- // 대상이 이동중이면 예측 이동을 한다
- // 나와 상대방의 속도차와 거리로부터 만날 시간을 예상한 후
- // 상대방이 그 시간까지 직선으로 이동한다고 가정하여 거기로 이동한다.
- float rot = pkChr->GetRotation();
- float rot_delta = GetDegreeDelta(rot, GetDegreeFromPositionXY(GetX(), GetY(), pkChr->GetX(), pkChr->GetY()));
- float yourSpeed = pkChr->GetMoveSpeed();
- float mySpeed = GetMoveSpeed();
- float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
- float fFollowSpeed = mySpeed - yourSpeed * cos(rot_delta * M_PI / 180);
- if (fFollowSpeed >= 0.1f)
- {
- float fMeetTime = fDist / fFollowSpeed;
- float fYourMoveEstimateX, fYourMoveEstimateY;
- if( fMeetTime * yourSpeed <= 100000.0f )
- {
- GetDeltaByDegree(pkChr->GetRotation(), fMeetTime * yourSpeed, &fYourMoveEstimateX, &fYourMoveEstimateY);
- x += (long) fYourMoveEstimateX;
- y += (long) fYourMoveEstimateY;
- float fDistNew = sqrt(((double)x - GetX())*(x-GetX())+((double)y - GetY())*(y-GetY()));
- if (fDist < fDistNew)
- {
- x = (long)(GetX() + (x - GetX()) * fDist / fDistNew);
- y = (long)(GetY() + (y - GetY()) * fDist / fDistNew);
- }
- }
- }
- }
- // 가려는 위치를 바라봐야 한다.
- SetRotationToXY(x, y);
- float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
- if (fDist <= fMinDistance)
- return false;
- float fx, fy;
- if (IsChangeAttackPosition(pkChr) && GetMobRank() < MOB_RANK_BOSS)
- {
- // 상대방 주변 랜덤한 곳으로 이동
- SetChangeAttackPositionTime();
- int retry = 16;
- int dx, dy;
- int rot = (int) GetDegreeFromPositionXY(x, y, GetX(), GetY());
- while (--retry)
- {
- if (fDist < 500.0f)
- GetDeltaByDegree((rot + number(-90, 90) + number(-90, 90)) % 360, fMinDistance, &fx, &fy);
- else
- GetDeltaByDegree(number(0, 359), fMinDistance, &fx, &fy);
- dx = x + (int) fx;
- dy = y + (int) fy;
- LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), dx, dy);
- if (NULL == tree)
- break;
- if (0 == (tree->GetAttribute(dx, dy) & (ATTR_BLOCK | ATTR_OBJECT)))
- break;
- }
- //sys_log(0, "근처 어딘가로 이동 %s retry %d", GetName(), retry);
- if (!Goto(dx, dy))
- return false;
- }
- else
- {
- // 직선 따라가기
- float fDistToGo = fDist - fMinDistance;
- GetDeltaByDegree(GetRotation(), fDistToGo, &fx, &fy);
- //sys_log(0, "직선으로 이동 %s", GetName());
- if (!Goto(GetX() + (int) fx, GetY() + (int) fy))
- return false;
- }
- SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
- //MonsterLog("쫓아가기; %s", pkChr->GetName());
- return true;
- }
- float CHARACTER::GetDistanceFromSafeboxOpen() const
- {
- return DISTANCE_APPROX(GetX() - m_posSafeboxOpen.x, GetY() - m_posSafeboxOpen.y);
- }
- void CHARACTER::SetSafeboxOpenPosition()
- {
- m_posSafeboxOpen = GetXYZ();
- }
- CSafebox * CHARACTER::GetSafebox() const
- {
- return m_pkSafebox;
- }
- void CHARACTER::ReqSafeboxLoad(const char* pszPassword)
- {
- if (!*pszPassword || strlen(pszPassword) > SAFEBOX_PASSWORD_MAX_LEN)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 잘못된 암호를 입력하셨습니다."));
- return;
- }
- else if (m_pkSafebox)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고가 이미 열려있습니다."));
- return;
- }
- int iPulse = thecore_pulse();
- if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(10))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고를 닫은지 10초 안에는 열 수 없습니다."));
- return;
- }
- else if (GetDistanceFromSafeboxOpen() > 1000)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 거리가 멀어서 창고를 열 수 없습니다."));
- return;
- }
- else if (m_bOpeningSafebox)
- {
- sys_log(0, "Overlapped safebox load request from %s", GetName());
- return;
- }
- SetSafeboxLoadTime();
- m_bOpeningSafebox = true;
- TSafeboxLoadPacket p;
- p.dwID = GetDesc()->GetAccountTable().id;
- strlcpy(p.szLogin, GetDesc()->GetAccountTable().login, sizeof(p.szLogin));
- strlcpy(p.szPassword, pszPassword, sizeof(p.szPassword));
- db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_LOAD, GetDesc()->GetHandle(), &p, sizeof(p));
- }
- void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems)
- {
- bool bLoaded = false;
- //PREVENT_TRADE_WINDOW
- SetOpenSafebox(true);
- //END_PREVENT_TRADE_WINDOW
- if (m_pkSafebox)
- bLoaded = true;
- if (!m_pkSafebox)
- m_pkSafebox = M2_NEW CSafebox(this, iSize, dwGold);
- else
- m_pkSafebox->ChangeSize(iSize);
- m_iSafeboxSize = iSize;
- TPacketCGSafeboxSize p;
- p.bHeader = HEADER_GC_SAFEBOX_SIZE;
- p.bSize = iSize;
- GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
- if (!bLoaded)
- {
- for (int i = 0; i < iItemCount; ++i, ++pItems)
- {
- if (!m_pkSafebox->IsValidPosition(pItems->pos))
- continue;
- LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
- if (!item)
- {
- sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
- continue;
- }
- item->SetSkipSave(true);
- item->SetSockets(pItems->alSockets);
- item->SetAttributes(pItems->aAttr);
- if (!m_pkSafebox->Add(pItems->pos, item))
- {
- M2_DESTROY_ITEM(item);
- }
- else
- item->SetSkipSave(false);
- }
- }
- }
- void CHARACTER::ChangeSafeboxSize(BYTE bSize)
- {
- //if (!m_pkSafebox)
- //return;
- TPacketCGSafeboxSize p;
- p.bHeader = HEADER_GC_SAFEBOX_SIZE;
- p.bSize = bSize;
- GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
- if (m_pkSafebox)
- m_pkSafebox->ChangeSize(bSize);
- m_iSafeboxSize = bSize;
- }
- void CHARACTER::CloseSafebox()
- {
- if (!m_pkSafebox)
- return;
- //PREVENT_TRADE_WINDOW
- SetOpenSafebox(false);
- //END_PREVENT_TRADE_WINDOW
- m_pkSafebox->Save();
- M2_DELETE(m_pkSafebox);
- m_pkSafebox = NULL;
- ChatPacket(CHAT_TYPE_COMMAND, "CloseSafebox");
- SetSafeboxLoadTime();
- m_bOpeningSafebox = false;
- Save();
- }
- CSafebox * CHARACTER::GetMall() const
- {
- return m_pkMall;
- }
- void CHARACTER::LoadMall(int iItemCount, TPlayerItem * pItems)
- {
- bool bLoaded = false;
- if (m_pkMall)
- bLoaded = true;
- if (!m_pkMall)
- m_pkMall = M2_NEW CSafebox(this, 3 * SAFEBOX_PAGE_SIZE, 0);
- else
- m_pkMall->ChangeSize(3 * SAFEBOX_PAGE_SIZE);
- m_pkMall->SetWindowMode(MALL);
- TPacketCGSafeboxSize p;
- p.bHeader = HEADER_GC_MALL_OPEN;
- p.bSize = 3 * SAFEBOX_PAGE_SIZE;
- GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
- if (!bLoaded)
- {
- for (int i = 0; i < iItemCount; ++i, ++pItems)
- {
- if (!m_pkMall->IsValidPosition(pItems->pos))
- continue;
- LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
- if (!item)
- {
- sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
- continue;
- }
- item->SetSkipSave(true);
- item->SetSockets(pItems->alSockets);
- item->SetAttributes(pItems->aAttr);
- if (!m_pkMall->Add(pItems->pos, item))
- M2_DESTROY_ITEM(item);
- else
- item->SetSkipSave(false);
- }
- }
- }
- void CHARACTER::CloseMall()
- {
- if (!m_pkMall)
- return;
- m_pkMall->Save();
- M2_DELETE(m_pkMall);
- m_pkMall = NULL;
- ChatPacket(CHAT_TYPE_COMMAND, "CloseMall");
- }
- bool CHARACTER::BuildUpdatePartyPacket(TPacketGCPartyUpdate & out)
- {
- if (!GetParty())
- return false;
- memset(&out, 0, sizeof(out));
- out.header = HEADER_GC_PARTY_UPDATE;
- out.pid = GetPlayerID();
- out.percent_hp = MINMAX(0, GetHP() * 100 / GetMaxHP(), 100);
- out.role = GetParty()->GetRole(GetPlayerID());
- sys_log(1, "PARTY %s role is %d", GetName(), out.role);
- LPCHARACTER l = GetParty()->GetLeaderCharacter();
- if (l && DISTANCE_APPROX(GetX() - l->GetX(), GetY() - l->GetY()) < PARTY_DEFAULT_RANGE)
- {
- if (g_iUseLocale)
- out.affects[0] = GetParty()->GetPartyBonusExpPercent();
- else
- out.affects[0] = GetParty()->GetExpBonusPercent();
- out.affects[1] = GetPoint(POINT_PARTY_ATTACKER_BONUS);
- out.affects[2] = GetPoint(POINT_PARTY_TANKER_BONUS);
- out.affects[3] = GetPoint(POINT_PARTY_BUFFER_BONUS);
- out.affects[4] = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
- out.affects[5] = GetPoint(POINT_PARTY_HASTE_BONUS);
- out.affects[6] = GetPoint(POINT_PARTY_DEFENDER_BONUS);
- }
- return true;
- }
- int CHARACTER::GetLeadershipSkillLevel() const
- {
- return GetSkillLevel(SKILL_LEADERSHIP);
- }
- void CHARACTER::QuerySafeboxSize()
- {
- if (m_iSafeboxSize == -1)
- {
- DBManager::instance().ReturnQuery(QID_SAFEBOX_SIZE,
- GetPlayerID(),
- NULL,
- "SELECT size FROM safebox%s WHERE account_id = %u",
- get_table_postfix(),
- GetDesc()->GetAccountTable().id);
- }
- }
- void CHARACTER::SetSafeboxSize(int iSize)
- {
- sys_log(1, "SetSafeboxSize: %s %d", GetName(), iSize);
- m_iSafeboxSize = iSize;
- DBManager::instance().Query("UPDATE safebox%s SET size = %d WHERE account_id = %u", get_table_postfix(), iSize / SAFEBOX_PAGE_SIZE, GetDesc()->GetAccountTable().id);
- }
- int CHARACTER::GetSafeboxSize() const
- {
- return m_iSafeboxSize;
- }
- void CHARACTER::SetNowWalking(bool bWalkFlag)
- {
- //if (m_bNowWalking != bWalkFlag || IsNPC())
- if (m_bNowWalking != bWalkFlag)
- {
- if (bWalkFlag)
- {
- m_bNowWalking = true;
- m_dwWalkStartTime = get_dword_time();
- }
- else
- {
- m_bNowWalking = false;
- }
- //if (m_bNowWalking)
- {
- TPacketGCWalkMode p;
- p.vid = GetVID();
- p.header = HEADER_GC_WALK_MODE;
- p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
- PacketView(&p, sizeof(p));
- }
- if (IsNPC())
- {
- if (m_bNowWalking)
- MonsterLog("걷는다");
- else
- MonsterLog("뛴다");
- }
- //sys_log(0, "%s is now %s", GetName(), m_bNowWalking?"walking.":"running.");
- }
- }
- void CHARACTER::StartStaminaConsume()
- {
- if (m_bStaminaConsume)
- return;
- PointChange(POINT_STAMINA, 0);
- m_bStaminaConsume = true;
- //ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
- if (IsStaminaHalfConsume())
- ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec / 2, GetStamina());
- else
- ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
- }
- void CHARACTER::StopStaminaConsume()
- {
- if (!m_bStaminaConsume)
- return;
- PointChange(POINT_STAMINA, 0);
- m_bStaminaConsume = false;
- ChatPacket(CHAT_TYPE_COMMAND, "StopStaminaConsume %d", GetStamina());
- }
- bool CHARACTER::IsStaminaConsume() const
- {
- return m_bStaminaConsume;
- }
- bool CHARACTER::IsStaminaHalfConsume() const
- {
- return IsEquipUniqueItem(UNIQUE_ITEM_HALF_STAMINA);
- }
- void CHARACTER::ResetStopTime()
- {
- m_dwStopTime = get_dword_time();
- }
- DWORD CHARACTER::GetStopTime() const
- {
- return m_dwStopTime;
- }
- void CHARACTER::ResetPoint(int iLv)
- {
- BYTE bJob = GetJob();
- PointChange(POINT_LEVEL, iLv - GetLevel());
- SetRealPoint(POINT_ST, JobInitialPoints[bJob].st);
- SetPoint(POINT_ST, GetRealPoint(POINT_ST));
- SetRealPoint(POINT_HT, JobInitialPoints[bJob].ht);
- SetPoint(POINT_HT, GetRealPoint(POINT_HT));
- SetRealPoint(POINT_DX, JobInitialPoints[bJob].dx);
- SetPoint(POINT_DX, GetRealPoint(POINT_DX));
- SetRealPoint(POINT_IQ, JobInitialPoints[bJob].iq);
- SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
- SetRandomHP((iLv - 1) * number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end));
- SetRandomSP((iLv - 1) * number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end));
- //PointChange(POINT_STAT, ((MINMAX(1, iLv, 99) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
- PointChange(POINT_STAT, ((MINMAX(1, iLv, 90) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
- ComputePoints();
- // 회복
- PointChange(POINT_HP, GetMaxHP() - GetHP());
- PointChange(POINT_SP, GetMaxSP() - GetSP());
- PointsPacket();
- LogManager::instance().CharLog(this, 0, "RESET_POINT", "");
- }
- bool CHARACTER::IsChangeAttackPosition(LPCHARACTER target) const
- {
- if (!IsNPC())
- return true;
- DWORD dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_NEAR;
- if (DISTANCE_APPROX(GetX() - target->GetX(), GetY() - target->GetY()) >
- AI_CHANGE_ATTACK_POISITION_DISTANCE + GetMobAttackRange())
- dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_FAR;
- return get_dword_time() - m_dwLastChangeAttackPositionTime > dwChangeTime;
- }
- void CHARACTER::GiveRandomSkillBook()
- {
- LPITEM item = AutoGiveItem(50300);
- if (NULL != item)
- {
- BYTE bJob = 0;
- if (!number(0, 1))
- bJob = GetJob() + 1;
- DWORD dwSkillVnum = 0;
- do
- {
- dwSkillVnum = number(1, 111);
- const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum);
- if (NULL == pkSk)
- continue;
- if (bJob && bJob != pkSk->dwType)
- continue;
- break;
- } while (true);
- item->SetSocket(0, dwSkillVnum);
- }
- }
- void CHARACTER::ReviveInvisible(int iDur)
- {
- AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true);
- }
- void CHARACTER::ToggleMonsterLog()
- {
- m_bMonsterLog = !m_bMonsterLog;
- if (m_bMonsterLog)
- {
- CHARACTER_MANAGER::instance().RegisterForMonsterLog(this);
- }
- else
- {
- CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
- }
- }
- void CHARACTER::SetGuild(CGuild* pGuild)
- {
- if (m_pGuild != pGuild)
- {
- m_pGuild = pGuild;
- UpdatePacket();
- }
- }
- void CHARACTER::SendGreetMessage()
- {
- __typeof(DBManager::instance().GetGreetMessage()) v = DBManager::instance().GetGreetMessage();
- for (itertype(v) it = v.begin(); it != v.end(); ++it)
- {
- ChatPacket(CHAT_TYPE_NOTICE, it->c_str());
- }
- }
- void CHARACTER::BeginStateEmpty()
- {
- MonsterLog("!");
- }
- void CHARACTER::EffectPacket(int enumEffectType)
- {
- TPacketGCSpecialEffect p;
- p.header = HEADER_GC_SEPCIAL_EFFECT;
- p.type = enumEffectType;
- p.vid = GetVID();
- PacketAround(&p, sizeof(TPacketGCSpecialEffect));
- }
- void CHARACTER::SpecificEffectPacket(const char filename[MAX_EFFECT_FILE_NAME])
- {
- TPacketGCSpecificEffect p;
- p.header = HEADER_GC_SPECIFIC_EFFECT;
- p.vid = GetVID();
- memcpy (p.effect_file, filename, MAX_EFFECT_FILE_NAME);
- PacketAround(&p, sizeof(TPacketGCSpecificEffect));
- }
- void CHARACTER::MonsterChat(BYTE bMonsterChatType)
- {
- if (IsPC())
- return;
- char sbuf[256+1];
- if (IsMonster())
- {
- if (number(0, 60))
- return;
- snprintf(sbuf, sizeof(sbuf),
- "(locale.monster_chat[%i] and locale.monster_chat[%i][%d] or '')",
- GetRaceNum(), GetRaceNum(), bMonsterChatType*3 + number(1, 3));
- }
- else
- {
- if (bMonsterChatType != MONSTER_CHAT_WAIT)
- return;
- if (IsGuardNPC())
- {
- if (number(0, 6))
- return;
- }
- else
- {
- if (number(0, 30))
- return;
- }
- snprintf(sbuf, sizeof(sbuf), "(locale.monster_chat[%i] and locale.monster_chat[%i][number(1, table.getn(locale.monster_chat[%i]))] or '')", GetRaceNum(), GetRaceNum(), GetRaceNum());
- }
- std::string text = quest::ScriptToString(sbuf);
- if (text.empty())
- return;
- struct packet_chat pack_chat;
- pack_chat.header = HEADER_GC_CHAT;
- pack_chat.size = sizeof(struct packet_chat) + text.size() + 1;
- pack_chat.type = CHAT_TYPE_TALKING;
- pack_chat.id = GetVID();
- pack_chat.bEmpire = 0;
- TEMP_BUFFER buf;
- buf.write(&pack_chat, sizeof(struct packet_chat));
- buf.write(text.c_str(), text.size() + 1);
- PacketAround(buf.read_peek(), buf.size());
- }
- void CHARACTER::SetQuestNPCID(DWORD vid)
- {
- m_dwQuestNPCVID = vid;
- }
- LPCHARACTER CHARACTER::GetQuestNPC() const
- {
- return CHARACTER_MANAGER::instance().Find(m_dwQuestNPCVID);
- }
- void CHARACTER::SetQuestItemPtr(LPITEM item)
- {
- m_pQuestItem = item;
- }
- void CHARACTER::ClearQuestItemPtr()
- {
- m_pQuestItem = NULL;
- }
- LPITEM CHARACTER::GetQuestItemPtr() const
- {
- return m_pQuestItem;
- }
- LPDUNGEON CHARACTER::GetDungeonForce() const
- {
- if (m_lWarpMapIndex > 10000)
- return CDungeonManager::instance().FindByMapIndex(m_lWarpMapIndex);
- return m_pkDungeon;
- }
- void CHARACTER::SetBlockMode(BYTE bFlag)
- {
- m_pointsInstant.bBlockMode = bFlag;
- ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
- SetQuestFlag("game_option.block_exchange", bFlag & BLOCK_EXCHANGE ? 1 : 0);
- SetQuestFlag("game_option.block_party_invite", bFlag & BLOCK_PARTY_INVITE ? 1 : 0);
- SetQuestFlag("game_option.block_guild_invite", bFlag & BLOCK_GUILD_INVITE ? 1 : 0);
- SetQuestFlag("game_option.block_whisper", bFlag & BLOCK_WHISPER ? 1 : 0);
- SetQuestFlag("game_option.block_messenger_invite", bFlag & BLOCK_MESSENGER_INVITE ? 1 : 0);
- SetQuestFlag("game_option.block_party_request", bFlag & BLOCK_PARTY_REQUEST ? 1 : 0);
- }
- void CHARACTER::SetBlockModeForce(BYTE bFlag)
- {
- m_pointsInstant.bBlockMode = bFlag;
- ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
- }
- bool CHARACTER::IsGuardNPC() const
- {
- return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
- }
- int CHARACTER::GetPolymorphPower() const
- {
- if (test_server)
- {
- int value = quest::CQuestManager::instance().GetEventFlag("poly");
- if (value)
- return value;
- }
- return aiPolymorphPowerByLevel[MINMAX(0, GetSkillLevel(SKILL_POLYMORPH), 40)];
- }
- void CHARACTER::SetPolymorph(DWORD dwRaceNum, bool bMaintainStat)
- {
- if (dwRaceNum < JOB_MAX_NUM)
- {
- dwRaceNum = 0;
- bMaintainStat = false;
- }
- if (m_dwPolymorphRace == dwRaceNum)
- return;
- m_bPolyMaintainStat = bMaintainStat;
- m_dwPolymorphRace = dwRaceNum;
- sys_log(0, "POLYMORPH: %s race %u ", GetName(), dwRaceNum);
- if (dwRaceNum != 0)
- StopRiding();
- SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
- m_afAffectFlag.Set(AFF_SPAWN);
- ViewReencode();
- REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
- if (!bMaintainStat)
- {
- PointChange(POINT_ST, 0);
- PointChange(POINT_DX, 0);
- PointChange(POINT_IQ, 0);
- PointChange(POINT_HT, 0);
- }
- // 폴리모프 상태에서 죽는 경우, 폴리모프가 풀리게 되는데
- // 폴리 모프 전후로 valid combo interval이 다르기 때문에
- // Combo 핵 또는 Hacker로 인식하는 경우가 있다.
- // 따라서 폴리모프를 풀거나 폴리모프 하게 되면,
- // valid combo interval을 reset한다.
- SetValidComboInterval(0);
- SetComboSequence(0);
- ComputeBattlePoints();
- }
- int CHARACTER::GetQuestFlag(const std::string& flag) const
- {
- quest::CQuestManager& q = quest::CQuestManager::instance();
- quest::PC* pPC = q.GetPC(GetPlayerID());
- return pPC->GetFlag(flag);
- }
- void CHARACTER::SetQuestFlag(const std::string& flag, int value)
- {
- quest::CQuestManager& q = quest::CQuestManager::instance();
- quest::PC* pPC = q.GetPC(GetPlayerID());
- pPC->SetFlag(flag, value);
- }
- void CHARACTER::DetermineDropMetinStone()
- {
- const int METIN_STONE_NUM = 14;
- static DWORD c_adwMetin[METIN_STONE_NUM] =
- {
- 28030,
- 28031,
- 28032,
- 28033,
- 28034,
- 28035,
- 28036,
- 28037,
- 28038,
- 28039,
- 28040,
- 28041,
- 28042,
- 28043,
- };
- DWORD stone_num = GetRaceNum();
- int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop;
- if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num)
- {
- m_dwDropMetinStone = 0;
- }
- else
- {
- const SStoneDropInfo & info = aStoneDrop[idx];
- m_bDropMetinStonePct = info.iDropPct;
- {
- m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)];
- int iGradePct = number(1, 100);
- for (int iStoneLevel = 0; iStoneLevel < STONE_LEVEL_MAX_NUM; iStoneLevel ++)
- {
- int iLevelGradePortion = info.iLevelPct[iStoneLevel];
- if (iGradePct <= iLevelGradePortion)
- {
- break;
- }
- else
- {
- iGradePct -= iLevelGradePortion;
- m_dwDropMetinStone += 100; // 돌 +a -> +(a+1)이 될때마다 100씩 증가
- }
- }
- }
- }
- }
- void CHARACTER::SendEquipment(LPCHARACTER ch)
- {
- TPacketViewEquip p;
- p.header = HEADER_GC_VIEW_EQUIP;
- p.vid = GetVID();
- for (int i = 0; i<WEAR_MAX_NUM; i++)
- {
- LPITEM item = GetWear(i);
- if (item)
- {
- p.equips[i].vnum = item->GetVnum();
- p.equips[i].count = item->GetCount();
- thecore_memcpy(p.equips[i].alSockets, item->GetSockets(), sizeof(p.equips[i].alSockets));
- thecore_memcpy(p.equips[i].aAttr, item->GetAttributes(), sizeof(p.equips[i].aAttr));
- }
- else
- {
- p.equips[i].vnum = 0;
- }
- }
- ch->GetDesc()->Packet(&p, sizeof(p));
- }
- bool CHARACTER::CanSummon(int iLeaderShip)
- {
- return (iLeaderShip >= 20 || iLeaderShip >= 12 && m_dwLastDeadTime + 180 > get_dword_time());
- }
- void CHARACTER::MountVnum(DWORD vnum)
- {
- if (m_dwMountVnum == vnum)
- return;
- m_dwMountVnum = vnum;
- m_dwMountTime = get_dword_time();
- if (m_bIsObserver)
- return;
- //NOTE : Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
- //그리고 서버Side에서 탔을때 위치 이동은 하지 않는다. 왜냐하면 Client Side에서 Coliision Adjust를 할수 있는데
- //객체를 소멸시켰다가 서버위치로 이동시키면 이때 collision check를 하지는 않으므로 배경에 끼거나 뚫고 나가는 문제가 존재한다.
- m_posDest.x = m_posStart.x = GetX();
- m_posDest.y = m_posStart.y = GetY();
- //EncodeRemovePacket(this);
- EncodeInsertPacket(this);
- ENTITY_MAP::iterator it = m_map_view.begin();
- while (it != m_map_view.end())
- {
- LPENTITY entity = (it++)->first;
- //Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
- //EncodeRemovePacket(entity);
- //if (!m_bIsObserver)
- EncodeInsertPacket(entity);
- //if (!entity->IsObserverMode())
- // entity->EncodeInsertPacket(this);
- }
- SetValidComboInterval(0);
- SetComboSequence(0);
- ComputePoints();
- }
- namespace {
- class FuncCheckWarp
- {
- public:
- FuncCheckWarp(LPCHARACTER pkWarp)
- {
- m_lTargetY = 0;
- m_lTargetX = 0;
- m_lX = pkWarp->GetX();
- m_lY = pkWarp->GetY();
- m_bInvalid = false;
- m_bEmpire = pkWarp->GetEmpire();
- char szTmp[64];
- if (3 != sscanf(pkWarp->GetName(), " %s %ld %ld ", szTmp, &m_lTargetX, &m_lTargetY))
- {
- if (number(1, 100) < 5)
- sys_err("Warp NPC name wrong : vnum(%d) name(%s)", pkWarp->GetRaceNum(), pkWarp->GetName());
- m_bInvalid = true;
- return;
- }
- m_lTargetX *= 100;
- m_lTargetY *= 100;
- m_bUseWarp = true;
- if (pkWarp->IsGoto())
- {
- LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(pkWarp->GetMapIndex());
- m_lTargetX += pkSectreeMap->m_setting.iBaseX;
- m_lTargetY += pkSectreeMap->m_setting.iBaseY;
- m_bUseWarp = false;
- }
- }
- bool Valid()
- {
- return !m_bInvalid;
- }
- void operator () (LPENTITY ent)
- {
- if (!Valid())
- return;
- if (!ent->IsType(ENTITY_CHARACTER))
- return;
- LPCHARACTER pkChr = (LPCHARACTER) ent;
- if (!pkChr->IsPC())
- return;
- int iDist = DISTANCE_APPROX(pkChr->GetX() - m_lX, pkChr->GetY() - m_lY);
- if (iDist > 300)
- return;
- if (m_bEmpire && pkChr->GetEmpire() && m_bEmpire != pkChr->GetEmpire())
- return;
- if (pkChr->IsHack())
- return;
- if (!pkChr->CanHandleItem(false, true))
- return;
- if (m_bUseWarp)
- pkChr->WarpSet(m_lTargetX, m_lTargetY);
- else
- {
- pkChr->Show(pkChr->GetMapIndex(), m_lTargetX, m_lTargetY);
- pkChr->Stop();
- }
- }
- bool m_bInvalid;
- bool m_bUseWarp;
- long m_lX;
- long m_lY;
- long m_lTargetX;
- long m_lTargetY;
- BYTE m_bEmpire;
- };
- }
- EVENTFUNC(warp_npc_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "warp_npc_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (ch == NULL) { // <Factor>
- return 0;
- }
- if (!ch->GetSectree())
- {
- ch->m_pkWarpNPCEvent = NULL;
- return 0;
- }
- FuncCheckWarp f(ch);
- if (f.Valid())
- ch->GetSectree()->ForEachAround(f);
- return passes_per_sec / 2;
- }
- void CHARACTER::StartWarpNPCEvent()
- {
- if (m_pkWarpNPCEvent)
- return;
- if (!IsWarp() && !IsGoto())
- return;
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2);
- }
- void CHARACTER::SyncPacket()
- {
- TEMP_BUFFER buf;
- TPacketCGSyncPositionElement elem;
- elem.dwVID = GetVID();
- elem.lX = GetX();
- elem.lY = GetY();
- TPacketGCSyncPosition pack;
- pack.bHeader = HEADER_GC_SYNC_POSITION;
- pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem);
- buf.write(&pack, sizeof(pack));
- buf.write(&elem, sizeof(elem));
- PacketAround(buf.read_peek(), buf.size());
- }
- LPCHARACTER CHARACTER::GetMarryPartner() const
- {
- return m_pkChrMarried;
- }
- void CHARACTER::SetMarryPartner(LPCHARACTER ch)
- {
- m_pkChrMarried = ch;
- }
- int CHARACTER::GetMarriageBonus(DWORD dwItemVnum, bool bSum)
- {
- if (IsNPC())
- return 0;
- marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
- if (!pMarriage)
- return 0;
- return pMarriage->GetBonus(dwItemVnum, bSum, this);
- }
- void CHARACTER::ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID)
- {
- if (!IsPC())
- return;
- TPacketGCQuestConfirm p;
- p.header = HEADER_GC_QUEST_CONFIRM;
- p.requestPID = dwRequestPID;
- p.timeout = iTimeout;
- strlcpy(p.msg, szMsg, sizeof(p.msg));
- GetDesc()->Packet(&p, sizeof(p));
- }
- int CHARACTER::GetPremiumRemainSeconds(BYTE bType) const
- {
- if (bType >= PREMIUM_MAX_NUM)
- return 0;
- return m_aiPremiumTimes[bType] - get_global_time();
- }
- bool CHARACTER::WarpToPID(DWORD dwPID)
- {
- LPCHARACTER victim;
- if ((victim = (CHARACTER_MANAGER::instance().FindByPID(dwPID))))
- {
- int mapIdx = victim->GetMapIndex();
- if (IS_SUMMONABLE_ZONE(mapIdx))
- {
- if (CAN_ENTER_ZONE(this, mapIdx))
- {
- WarpSet(victim->GetX(), victim->GetY());
- }
- else
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
- return false;
- }
- }
- else
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
- return false;
- }
- }
- else
- {
- // 다른 서버에 로그인된 사람이 있음 -> 메시지 보내 좌표를 받아오자
- // 1. A.pid, B.pid 를 뿌림
- // 2. B.pid를 가진 서버가 뿌린서버에게 A.pid, 좌표 를 보냄
- // 3. 워프
- CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID);
- if (!pcci)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 온라인 상태가 아닙니다."));
- return false;
- }
- if (pcci->bChannel != g_bChannel)
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 %d 채널에 있습니다. (현재 채널 %d)"), pcci->bChannel, g_bChannel);
- return false;
- }
- else if (false == IS_SUMMONABLE_ZONE(pcci->lMapIndex))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
- return false;
- }
- else
- {
- if (!CAN_ENTER_ZONE(this, pcci->lMapIndex))
- {
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
- return false;
- }
- TPacketGGFindPosition p;
- p.header = HEADER_GG_FIND_POSITION;
- p.dwFromPID = GetPlayerID();
- p.dwTargetPID = dwPID;
- pcci->pkDesc->Packet(&p, sizeof(TPacketGGFindPosition));
- if (test_server)
- ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for teleport");
- }
- }
- return true;
- }
- // ADD_REFINE_BUILDING
- CGuild* CHARACTER::GetRefineGuild() const
- {
- LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
- return (chRefineNPC ? chRefineNPC->GetGuild() : NULL);
- }
- bool CHARACTER::IsRefineThroughGuild() const
- {
- return GetRefineGuild() != NULL;
- }
- int CHARACTER::ComputeRefineFee(int iCost, int iMultiply) const
- {
- CGuild* pGuild = GetRefineGuild();
- if (pGuild)
- {
- if (pGuild == GetGuild())
- return iCost * iMultiply * 9 / 10;
- // 다른 제국 사람이 시도하는 경우 추가로 3배 더
- LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
- if (chRefineNPC && chRefineNPC->GetEmpire() != GetEmpire())
- return iCost * iMultiply * 3;
- return iCost * iMultiply;
- }
- else
- return iCost;
- }
- void CHARACTER::PayRefineFee(int iTotalMoney)
- {
- int iFee = iTotalMoney / 10;
- CGuild* pGuild = GetRefineGuild();
- int iRemain = iTotalMoney;
- if (pGuild)
- {
- // 자기 길드이면 iTotalMoney에 이미 10%가 제외되어있다
- if (pGuild != GetGuild())
- {
- pGuild->RequestDepositMoney(this, iFee);
- iRemain -= iFee;
- }
- }
- PointChange(POINT_GOLD, -iRemain);
- }
- // END_OF_ADD_REFINE_BUILDING
- //Hack 방지를 위한 체크.
- bool CHARACTER::IsHack(bool bSendMsg, bool bCheckShopOwner, int limittime)
- {
- const int iPulse = thecore_pulse();
- if (test_server)
- bSendMsg = true;
- //창고 연후 체크
- if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(limittime))
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 다른곳으로 이동할수 없습니다."), limittime);
- if (test_server)
- ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(limittime));
- return true;
- }
- //거래관련 창 체크
- if (bCheckShopOwner)
- {
- if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
- return true;
- }
- }
- else
- {
- if (GetExchange() || GetMyShop() || IsOpenSafebox() || IsCubeOpen())
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
- return true;
- }
- }
- //PREVENT_PORTAL_AFTER_EXCHANGE
- //교환 후 시간체크
- if (iPulse - GetExchangeTime() < PASSES_PER_SEC(limittime))
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime );
- return true;
- }
- //END_PREVENT_PORTAL_AFTER_EXCHANGE
- //PREVENT_ITEM_COPY
- if (iPulse - GetMyShopTime() < PASSES_PER_SEC(limittime))
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime);
- return true;
- }
- if (iPulse - GetRefineTime() < PASSES_PER_SEC(limittime))
- {
- if (bSendMsg)
- ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 개량후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), limittime);
- return true;
- }
- //END_PREVENT_ITEM_COPY
- return false;
- }
- BOOL CHARACTER::IsMonarch() const
- {
- //MONARCH_LIMIT
- if (CMonarch::instance().IsMonarch(GetPlayerID(), GetEmpire()))
- return true;
- return false;
- //END_MONARCH_LIMIT
- }
- void CHARACTER::Say(const std::string & s)
- {
- struct ::packet_script packet_script;
- packet_script.header = HEADER_GC_SCRIPT;
- packet_script.skin = 1;
- packet_script.src_size = s.size();
- packet_script.size = packet_script.src_size + sizeof(struct packet_script);
- TEMP_BUFFER buf;
- buf.write(&packet_script, sizeof(struct packet_script));
- buf.write(&s[0], s.size());
- if (IsPC())
- {
- GetDesc()->Packet(buf.read_peek(), buf.size());
- }
- }
- //
- // Monarch
- //
- void CHARACTER::InitMC()
- {
- for (int n = 0; n < MI_MAX; ++n)
- {
- m_dwMonarchCooltime[n] = thecore_pulse();
- }
- m_dwMonarchCooltimelimit[MI_HEAL] = PASSES_PER_SEC(MC_HEAL);
- m_dwMonarchCooltimelimit[MI_WARP] = PASSES_PER_SEC(MC_WARP);
- m_dwMonarchCooltimelimit[MI_TRANSFER] = PASSES_PER_SEC(MC_TRANSFER);
- m_dwMonarchCooltimelimit[MI_TAX] = PASSES_PER_SEC(MC_TAX);
- m_dwMonarchCooltimelimit[MI_SUMMON] = PASSES_PER_SEC(MC_SUMMON);
- m_dwMonarchCooltime[MI_HEAL] -= PASSES_PER_SEC(GetMCL(MI_HEAL));
- m_dwMonarchCooltime[MI_WARP] -= PASSES_PER_SEC(GetMCL(MI_WARP));
- m_dwMonarchCooltime[MI_TRANSFER] -= PASSES_PER_SEC(GetMCL(MI_TRANSFER));
- m_dwMonarchCooltime[MI_TAX] -= PASSES_PER_SEC(GetMCL(MI_TAX));
- m_dwMonarchCooltime[MI_SUMMON] -= PASSES_PER_SEC(GetMCL(MI_SUMMON));
- }
- DWORD CHARACTER::GetMC(enum MONARCH_INDEX e) const
- {
- return m_dwMonarchCooltime[e];
- }
- void CHARACTER::SetMC(enum MONARCH_INDEX e)
- {
- m_dwMonarchCooltime[e] = thecore_pulse();
- }
- bool CHARACTER::IsMCOK(enum MONARCH_INDEX e) const
- {
- int iPulse = thecore_pulse();
- if ((iPulse - GetMC(e)) < GetMCL(e))
- {
- if (test_server)
- sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
- return false;
- }
- if (test_server)
- sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
- return true;
- }
- DWORD CHARACTER::GetMCL(enum MONARCH_INDEX e) const
- {
- return m_dwMonarchCooltimelimit[e];
- }
- DWORD CHARACTER::GetMCLTime(enum MONARCH_INDEX e) const
- {
- int iPulse = thecore_pulse();
- if (test_server)
- sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
- return (GetMCL(e)) / passes_per_sec - (iPulse - GetMC(e)) / passes_per_sec;
- }
- bool CHARACTER::IsSiegeNPC() const
- {
- return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
- }
- //------------------------------------------------
- void CHARACTER::UpdateDepositPulse()
- {
- m_deposit_pulse = thecore_pulse() + PASSES_PER_SEC(60*5); // 5분
- }
- bool CHARACTER::CanDeposit() const
- {
- return (m_deposit_pulse == 0 || (m_deposit_pulse < thecore_pulse()));
- }
- //------------------------------------------------
- ESex GET_SEX(LPCHARACTER ch)
- {
- switch (ch->GetRaceNum())
- {
- case MAIN_RACE_WARRIOR_M:
- case MAIN_RACE_SURA_M:
- case MAIN_RACE_ASSASSIN_M:
- case MAIN_RACE_SHAMAN_M:
- return SEX_MALE;
- case MAIN_RACE_ASSASSIN_W:
- case MAIN_RACE_SHAMAN_W:
- case MAIN_RACE_WARRIOR_W:
- case MAIN_RACE_SURA_W:
- return SEX_FEMALE;
- }
- /* default sex = male */
- return SEX_MALE;
- }
- int CHARACTER::GetHPPct() const
- {
- return (GetHP() * 100) / GetMaxHP();
- }
- bool CHARACTER::IsBerserk() const
- {
- if (m_pkMobInst != NULL)
- return m_pkMobInst->m_IsBerserk;
- else
- return false;
- }
- void CHARACTER::SetBerserk(bool mode)
- {
- if (m_pkMobInst != NULL)
- m_pkMobInst->m_IsBerserk = mode;
- }
- bool CHARACTER::IsGodSpeed() const
- {
- if (m_pkMobInst != NULL)
- {
- return m_pkMobInst->m_IsGodSpeed;
- }
- else
- {
- return false;
- }
- }
- void CHARACTER::SetGodSpeed(bool mode)
- {
- if (m_pkMobInst != NULL)
- {
- m_pkMobInst->m_IsGodSpeed = mode;
- if (mode == true)
- {
- SetPoint(POINT_ATT_SPEED, 250);
- }
- else
- {
- SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
- }
- }
- }
- bool CHARACTER::IsDeathBlow() const
- {
- if (number(1, 100) <= m_pkMobData->m_table.bDeathBlowPoint)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- struct FFindReviver
- {
- FFindReviver()
- {
- pChar = NULL;
- HasReviver = false;
- }
- void operator() (LPCHARACTER ch)
- {
- if (ch->IsMonster() != true)
- {
- return;
- }
- if (ch->IsReviver() == true && pChar != ch && ch->IsDead() != true)
- {
- if (number(1, 100) <= ch->GetMobTable().bRevivePoint)
- {
- HasReviver = true;
- pChar = ch;
- }
- }
- }
- LPCHARACTER pChar;
- bool HasReviver;
- };
- bool CHARACTER::HasReviverInParty() const
- {
- LPPARTY party = GetParty();
- if (party != NULL)
- {
- if (party->GetMemberCount() == 1) return false;
- FFindReviver f;
- party->ForEachMemberPtr(f);
- return f.HasReviver;
- }
- return false;
- }
- bool CHARACTER::IsRevive() const
- {
- if (m_pkMobInst != NULL)
- {
- return m_pkMobInst->m_IsRevive;
- }
- return false;
- }
- void CHARACTER::SetRevive(bool mode)
- {
- if (m_pkMobInst != NULL)
- {
- m_pkMobInst->m_IsRevive = mode;
- }
- }
- #define IS_SPEED_HACK_PLAYER(ch) (ch->m_speed_hack_count > SPEEDHACK_LIMIT_COUNT)
- EVENTFUNC(check_speedhack_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "check_speedhack_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (NULL == ch || ch->IsNPC())
- return 0;
- if (IS_SPEED_HACK_PLAYER(ch))
- {
- // write hack log
- LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count);
- if (false == LC_IsEurope())
- {
- // close connection
- LPDESC desc = ch->GetDesc();
- if (desc)
- {
- DESC_MANAGER::instance().DestroyDesc(desc);
- return 0;
- }
- }
- }
- ch->m_speed_hack_count = 0;
- ch->ResetComboHackCount();
- return PASSES_PER_SEC(60);
- }
- void CHARACTER::StartCheckSpeedHackEvent()
- {
- if (m_pkCheckSpeedHackEvent)
- return;
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- m_pkCheckSpeedHackEvent = event_create(check_speedhack_event, info, PASSES_PER_SEC(60)); // 1분
- }
- void CHARACTER::GoHome()
- {
- WarpSet(EMPIRE_START_X(GetEmpire()), EMPIRE_START_Y(GetEmpire()));
- }
- void CHARACTER::SendGuildName(CGuild* pGuild)
- {
- if (NULL == pGuild) return;
- DESC *desc = GetDesc();
- if (NULL == desc) return;
- if (m_known_guild.find(pGuild->GetID()) != m_known_guild.end()) return;
- m_known_guild.insert(pGuild->GetID());
- TPacketGCGuildName pack;
- memset(&pack, 0x00, sizeof(pack));
- pack.header = HEADER_GC_GUILD;
- pack.subheader = GUILD_SUBHEADER_GC_GUILD_NAME;
- pack.size = sizeof(TPacketGCGuildName);
- pack.guildID = pGuild->GetID();
- memcpy(pack.guildName, pGuild->GetName(), GUILD_NAME_MAX_LEN);
- desc->Packet(&pack, sizeof(pack));
- }
- void CHARACTER::SendGuildName(DWORD dwGuildID)
- {
- SendGuildName(CGuildManager::instance().FindGuild(dwGuildID));
- }
- EVENTFUNC(destroy_when_idle_event)
- {
- char_event_info* info = dynamic_cast<char_event_info*>( event->info );
- if ( info == NULL )
- {
- sys_err( "destroy_when_idle_event> <Factor> Null pointer" );
- return 0;
- }
- LPCHARACTER ch = info->ch;
- if (ch == NULL) { // <Factor>
- return 0;
- }
- if (ch->GetVictim())
- {
- return PASSES_PER_SEC(300);
- }
- sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName());
- ch->m_pkDestroyWhenIdleEvent = NULL;
- M2_DESTROY_CHARACTER(ch);
- return 0;
- }
- void CHARACTER::StartDestroyWhenIdleEvent()
- {
- if (m_pkDestroyWhenIdleEvent)
- return;
- char_event_info* info = AllocEventInfo<char_event_info>();
- info->ch = this;
- m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300));
- }
- void CHARACTER::SetComboSequence(BYTE seq)
- {
- m_bComboSequence = seq;
- }
- BYTE CHARACTER::GetComboSequence() const
- {
- return m_bComboSequence;
- }
- void CHARACTER::SetLastComboTime(DWORD time)
- {
- m_dwLastComboTime = time;
- }
- DWORD CHARACTER::GetLastComboTime() const
- {
- return m_dwLastComboTime;
- }
- void CHARACTER::SetValidComboInterval(int interval)
- {
- m_iValidComboInterval = interval;
- }
- int CHARACTER::GetValidComboInterval() const
- {
- return m_iValidComboInterval;
- }
- BYTE CHARACTER::GetComboIndex() const
- {
- return m_bComboIndex;
- }
- void CHARACTER::IncreaseComboHackCount(int k)
- {
- m_iComboHackCount += k;
- if (m_iComboHackCount >= 10)
- {
- if (GetDesc())
- if (GetDesc()->DelayedDisconnect(number(2, 7)))
- {
- sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount);
- LogManager::instance().HackLog("Combo", this);
- }
- }
- }
- void CHARACTER::ResetComboHackCount()
- {
- m_iComboHackCount = 0;
- }
- void CHARACTER::SkipComboAttackByTime(int interval)
- {
- m_dwSkipComboAttackByTime = get_dword_time() + interval;
- }
- DWORD CHARACTER::GetSkipComboAttackByTime() const
- {
- return m_dwSkipComboAttackByTime;
- }
- void CHARACTER::ResetChatCounter()
- {
- m_bChatCounter = 0;
- }
- BYTE CHARACTER::IncreaseChatCounter()
- {
- return ++m_bChatCounter;
- }
- BYTE CHARACTER::GetChatCounter() const
- {
- return m_bChatCounter;
- }
- // 말이나 다른것을 타고 있나?
- bool CHARACTER::IsRiding() const
- {
- return IsHorseRiding() || GetMountVnum();
- }
- bool CHARACTER::CanWarp() const
- {
- const int iPulse = thecore_pulse();
- const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime);
- if ((iPulse - GetSafeboxLoadTime()) < limit_time)
- return false;
- if ((iPulse - GetExchangeTime()) < limit_time)
- return false;
- if ((iPulse - GetMyShopTime()) < limit_time)
- return false;
- if ((iPulse - GetRefineTime()) < limit_time)
- return false;
- if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
- return false;
- return true;
- }
- long long CHARACTER::GetNextExp()
- {
- if (PLAYER_EXP_TABLE_MAX < GetLevel())
- return 420000000000LLU;
- else
- return exp_table[GetLevel()];
- }
- int CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const
- {
- return CTableBySkill::instance().GetSkillPowerByLevelFromType(GetJob(), GetSkillGroup(), MINMAX(0, level, SKILL_MAX_LEVEL), bMob);
- }
- void CHARACTER::SetLastPMPulse(void)
- {
- m_iLastPMPulse = thecore_pulse() + 25;
- }
char.h
Code
- #ifndef __INC_METIN_II_CHAR_H__
- #define __INC_METIN_II_CHAR_H__
- #include <boost/unordered_map.hpp>
- #include "../../common/stl.h"
- #include "entity.h"
- #include "FSM.h"
- #include "horse_rider.h"
- #include "vid.h"
- #include "constants.h"
- #include "affect.h"
- #include "affect_flag.h"
- #include "cube.h"
- #include "mining.h"
- class CBuffOnAttributes;
- class CPetSystem;
- #define INSTANT_FLAG_DEATH_PENALTY (1 << 0)
- #define INSTANT_FLAG_SHOP (1 << 1)
- #define INSTANT_FLAG_EXCHANGE (1 << 2)
- #define INSTANT_FLAG_STUN (1 << 3)
- #define INSTANT_FLAG_NO_REWARD (1 << 4)
- #define AI_FLAG_NPC (1 << 0)
- #define AI_FLAG_AGGRESSIVE (1 << 1)
- #define AI_FLAG_HELPER (1 << 2)
- #define AI_FLAG_STAYZONE (1 << 3)
- #define SET_OVER_TIME(ch, time) (ch)->SetOverTime(time)
- extern int g_nPortalLimitTime;
- enum
- {
- MAIN_RACE_WARRIOR_M,
- MAIN_RACE_ASSASSIN_W,
- MAIN_RACE_SURA_M,
- MAIN_RACE_SHAMAN_W,
- MAIN_RACE_WARRIOR_W,
- MAIN_RACE_ASSASSIN_M,
- MAIN_RACE_SURA_W,
- MAIN_RACE_SHAMAN_M,
- MAIN_RACE_MAX_NUM,
- };
- enum
- {
- POISON_LENGTH = 30,
- STAMINA_PER_STEP = 1,
- SAFEBOX_PAGE_SIZE = 9,
- AI_CHANGE_ATTACK_POISITION_TIME_NEAR = 10000,
- AI_CHANGE_ATTACK_POISITION_TIME_FAR = 1000,
- AI_CHANGE_ATTACK_POISITION_DISTANCE = 100,
- SUMMON_MONSTER_COUNT = 3,
- };
- enum
- {
- FLY_NONE,
- FLY_EXP,
- FLY_HP_MEDIUM,
- FLY_HP_BIG,
- FLY_SP_SMALL,
- FLY_SP_MEDIUM,
- FLY_SP_BIG,
- FLY_FIREWORK1,
- FLY_FIREWORK2,
- FLY_FIREWORK3,
- FLY_FIREWORK4,
- FLY_FIREWORK5,
- FLY_FIREWORK6,
- FLY_FIREWORK_CHRISTMAS,
- FLY_CHAIN_LIGHTNING,
- FLY_HP_SMALL,
- FLY_SKILL_MUYEONG,
- };
- enum EDamageType
- {
- DAMAGE_TYPE_NONE,
- DAMAGE_TYPE_NORMAL,
- DAMAGE_TYPE_NORMAL_RANGE,
- //스킬
- DAMAGE_TYPE_MELEE,
- DAMAGE_TYPE_RANGE,
- DAMAGE_TYPE_FIRE,
- DAMAGE_TYPE_ICE,
- DAMAGE_TYPE_ELEC,
- DAMAGE_TYPE_MAGIC,
- DAMAGE_TYPE_POISON,
- DAMAGE_TYPE_SPECIAL,
- };
- enum EPointTypes
- {
- POINT_NONE, // 0
- POINT_LEVEL, // 1
- POINT_VOICE, // 2
- POINT_EXP, // 3
- POINT_NEXT_EXP, // 4
- POINT_HP, // 5
- POINT_MAX_HP, // 6
- POINT_SP, // 7
- POINT_MAX_SP, // 8
- POINT_STAMINA, // 9 스테미너
- POINT_MAX_STAMINA, // 10 최대 스테미너
- POINT_GOLD, // 11
- POINT_ST, // 12 근력
- POINT_HT, // 13 체력
- POINT_DX, // 14 민첩성
- POINT_IQ, // 15 정신력
- POINT_DEF_GRADE, // 16 ...
- POINT_ATT_SPEED, // 17 공격속도
- POINT_ATT_GRADE, // 18 공격력 MAX
- POINT_MOV_SPEED, // 19 이동속도
- POINT_CLIENT_DEF_GRADE, // 20 방어등급
- POINT_CASTING_SPEED, // 21 주문속도 (쿨다운타임*100) / (100 + 이값) = 최종 쿨다운 타임
- POINT_MAGIC_ATT_GRADE, // 22 마법공격력
- POINT_MAGIC_DEF_GRADE, // 23 마법방어력
- POINT_EMPIRE_POINT, // 24 제국점수
- POINT_LEVEL_STEP, // 25 한 레벨에서의 단계.. (1 2 3 될 때 보상, 4 되면 레벨 업)
- POINT_STAT, // 26 능력치 올릴 수 있는 개수
- POINT_SUB_SKILL, // 27 보조 스킬 포인트
- POINT_SKILL, // 28 액티브 스킬 포인트
- POINT_WEAPON_MIN, // 29 무기 최소 데미지
- POINT_WEAPON_MAX, // 30 무기 최대 데미지
- POINT_PLAYTIME, // 31 플레이시간
- POINT_HP_REGEN, // 32 HP 회복률
- POINT_SP_REGEN, // 33 SP 회복률
- POINT_BOW_DISTANCE, // 34 활 사정거리 증가치 (meter)
- POINT_HP_RECOVERY, // 35 체력 회복 증가량
- POINT_SP_RECOVERY, // 36 정신력 회복 증가량
- POINT_POISON_PCT, // 37 독 확률
- POINT_STUN_PCT, // 38 기절 확률
- POINT_SLOW_PCT, // 39 슬로우 확률
- POINT_CRITICAL_PCT, // 40 크리티컬 확률
- POINT_PENETRATE_PCT, // 41 관통타격 확률
- POINT_CURSE_PCT, // 42 저주 확률
- POINT_ATTBONUS_HUMAN, // 43 인간에게 강함
- POINT_ATTBONUS_ANIMAL, // 44 동물에게 데미지 % 증가
- POINT_ATTBONUS_ORC, // 45 웅귀에게 데미지 % 증가
- POINT_ATTBONUS_MILGYO, // 46 밀교에게 데미지 % 증가
- POINT_ATTBONUS_UNDEAD, // 47 시체에게 데미지 % 증가
- POINT_ATTBONUS_DEVIL, // 48 마귀(악마)에게 데미지 % 증가
- POINT_ATTBONUS_INSECT, // 49 벌레족
- POINT_ATTBONUS_FIRE, // 50 화염족
- POINT_ATTBONUS_ICE, // 51 빙설족
- POINT_ATTBONUS_DESERT, // 52 사막족
- POINT_ATTBONUS_MONSTER, // 53 모든 몬스터에게 강함
- POINT_ATTBONUS_WARRIOR, // 54 무사에게 강함
- POINT_ATTBONUS_ASSASSIN, // 55 자객에게 강함
- POINT_ATTBONUS_SURA, // 56 수라에게 강함
- POINT_ATTBONUS_SHAMAN, // 57 무당에게 강함
- POINT_ATTBONUS_TREE, // 58 나무에게 강함 20050729.myevan UNUSED5
- POINT_RESIST_WARRIOR, // 59 무사에게 저항
- POINT_RESIST_ASSASSIN, // 60 자객에게 저항
- POINT_RESIST_SURA, // 61 수라에게 저항
- POINT_RESIST_SHAMAN, // 62 무당에게 저항
- POINT_STEAL_HP, // 63 생명력 흡수
- POINT_STEAL_SP, // 64 정신력 흡수
- POINT_MANA_BURN_PCT, // 65 마나 번
- /// 피해시 보너스 ///
- POINT_DAMAGE_SP_RECOVER, // 66 공격당할 시 정신력 회복 확률
- POINT_BLOCK, // 67 블럭율
- POINT_DODGE, // 68 회피율
- POINT_RESIST_SWORD, // 69
- POINT_RESIST_TWOHAND, // 70
- POINT_RESIST_DAGGER, // 71
- POINT_RESIST_BELL, // 72
- POINT_RESIST_FAN, // 73
- POINT_RESIST_BOW, // 74 화살 저항 : 대미지 감소
- POINT_RESIST_FIRE, // 75 화염 저항 : 화염공격에 대한 대미지 감소
- POINT_RESIST_ELEC, // 76 전기 저항 : 전기공격에 대한 대미지 감소
- POINT_RESIST_MAGIC, // 77 술법 저항 : 모든술법에 대한 대미지 감소
- POINT_RESIST_WIND, // 78 바람 저항 : 바람공격에 대한 대미지 감소
- POINT_REFLECT_MELEE, // 79 공격 반사
- /// 특수 피해시 ///
- POINT_REFLECT_CURSE, // 80 저주 반사
- POINT_POISON_REDUCE, // 81 독데미지 감소
- /// 적 소멸시 ///
- POINT_KILL_SP_RECOVER, // 82 적 소멸시 MP 회복
- POINT_EXP_DOUBLE_BONUS, // 83
- POINT_GOLD_DOUBLE_BONUS, // 84
- POINT_ITEM_DROP_BONUS, // 85
- /// 회복 관련 ///
- POINT_POTION_BONUS, // 86
- POINT_KILL_HP_RECOVERY, // 87
- POINT_IMMUNE_STUN, // 88
- POINT_IMMUNE_SLOW, // 89
- POINT_IMMUNE_FALL, // 90
- //////////////////
- POINT_PARTY_ATTACKER_BONUS, // 91
- POINT_PARTY_TANKER_BONUS, // 92
- POINT_ATT_BONUS, // 93
- POINT_DEF_BONUS, // 94
- POINT_ATT_GRADE_BONUS, // 95
- POINT_DEF_GRADE_BONUS, // 96
- POINT_MAGIC_ATT_GRADE_BONUS, // 97
- POINT_MAGIC_DEF_GRADE_BONUS, // 98
- POINT_RESIST_NORMAL_DAMAGE, // 99
- POINT_HIT_HP_RECOVERY, // 100
- POINT_HIT_SP_RECOVERY, // 101
- POINT_MANASHIELD, // 102 흑신수호 스킬에 의한 마나쉴드 효과 정도
- POINT_PARTY_BUFFER_BONUS, // 103
- POINT_PARTY_SKILL_MASTER_BONUS, // 104
- POINT_HP_RECOVER_CONTINUE, // 105
- POINT_SP_RECOVER_CONTINUE, // 106
- POINT_STEAL_GOLD, // 107
- POINT_POLYMORPH, // 108 변신한 몬스터 번호
- POINT_MOUNT, // 109 타고있는 몬스터 번호
- POINT_PARTY_HASTE_BONUS, // 110
- POINT_PARTY_DEFENDER_BONUS, // 111
- POINT_STAT_RESET_COUNT, // 112 피의 단약 사용을 통한 스텟 리셋 포인트 (1당 1포인트 리셋가능)
- POINT_HORSE_SKILL, // 113
- POINT_MALL_ATTBONUS, // 114 공격력 +x%
- POINT_MALL_DEFBONUS, // 115 방어력 +x%
- POINT_MALL_EXPBONUS, // 116 경험치 +x%
- POINT_MALL_ITEMBONUS, // 117 아이템 드롭율 x/10배
- POINT_MALL_GOLDBONUS, // 118 돈 드롭율 x/10배
- POINT_MAX_HP_PCT, // 119 최대생명력 +x%
- POINT_MAX_SP_PCT, // 120 최대정신력 +x%
- POINT_SKILL_DAMAGE_BONUS, // 121 스킬 데미지 *(100+x)%
- POINT_NORMAL_HIT_DAMAGE_BONUS, // 122 평타 데미지 *(100+x)%
- // DEFEND_BONUS_ATTRIBUTES
- POINT_SKILL_DEFEND_BONUS, // 123 스킬 방어 데미지
- POINT_NORMAL_HIT_DEFEND_BONUS, // 124 평타 방어 데미지
- // END_OF_DEFEND_BONUS_ATTRIBUTES
- // PC_BANG_ITEM_ADD
- POINT_PC_BANG_EXP_BONUS, // 125 PC방 전용 경험치 보너스
- POINT_PC_BANG_DROP_BONUS, // 126 PC방 전용 드롭률 보너스
- // END_PC_BANG_ITEM_ADD
- POINT_RAMADAN_CANDY_BONUS_EXP, // 라마단 사탕 경험치 증가용
- POINT_ENERGY = 128, // 128 기력
- // 기력 ui 용.
- // 서버에서 쓰지 않기만, 클라이언트에서 기력의 끝 시간을 POINT로 관리하기 때문에 이렇게 한다.
- // 아 부끄럽다
- POINT_ENERGY_END_TIME = 129, // 129 기력 종료 시간
- POINT_COSTUME_ATTR_BONUS = 130,
- POINT_MAGIC_ATT_BONUS_PER = 131,
- POINT_MELEE_MAGIC_ATT_BONUS_PER = 132,
- // 추가 속성 저항
- POINT_RESIST_ICE = 133, // 냉기 저항 : 얼음공격에 대한 대미지 감소
- POINT_RESIST_EARTH = 134, // 대지 저항 : 얼음공격에 대한 대미지 감소
- POINT_RESIST_DARK = 135, // 어둠 저항 : 얼음공격에 대한 대미지 감소
- POINT_RESIST_CRITICAL = 136, // 크리티컬 저항 : 상대의 크리티컬 확률을 감소
- POINT_RESIST_PENETRATE = 137, // 관통타격 저항 : 상대의 관통타격 확률을 감소
- //POINT_MAX_NUM = 129 common/length.h
- };
- enum EPKModes
- {
- PK_MODE_PEACE,
- PK_MODE_REVENGE,
- PK_MODE_FREE,
- PK_MODE_PROTECT,
- PK_MODE_GUILD,
- PK_MODE_MAX_NUM
- };
- enum EPositions
- {
- POS_DEAD,
- POS_SLEEPING,
- POS_RESTING,
- POS_SITTING,
- POS_FISHING,
- POS_FIGHTING,
- POS_MOUNTING,
- POS_STANDING
- };
- enum EBlockAction
- {
- BLOCK_EXCHANGE = (1 << 0),
- BLOCK_PARTY_INVITE = (1 << 1),
- BLOCK_GUILD_INVITE = (1 << 2),
- BLOCK_WHISPER = (1 << 3),
- BLOCK_MESSENGER_INVITE = (1 << 4),
- BLOCK_PARTY_REQUEST = (1 << 5),
- };
- // <Factor> Dynamically evaluated CHARACTER* equivalent.
- // Referring to SCharDeadEventInfo.
- struct DynamicCharacterPtr {
- DynamicCharacterPtr() : is_pc(false), id(0) {}
- DynamicCharacterPtr(const DynamicCharacterPtr& o)
- : is_pc(o.is_pc), id(o.id) {}
- // Returns the LPCHARACTER found in CHARACTER_MANAGER.
- LPCHARACTER Get() const;
- // Clears the current settings.
- void Reset() {
- is_pc = false;
- id = 0;
- }
- // Basic assignment operator.
- DynamicCharacterPtr& operator=(const DynamicCharacterPtr& rhs) {
- is_pc = rhs.is_pc;
- id = rhs.id;
- return *this;
- }
- // Supports assignment with LPCHARACTER type.
- DynamicCharacterPtr& operator=(LPCHARACTER character);
- // Supports type casting to LPCHARACTER.
- operator LPCHARACTER() const {
- return Get();
- }
- bool is_pc;
- uint32_t id;
- };
- /* 저장하는 데이터 */
- typedef struct character_point
- {
- long points[POINT_MAX_NUM];
- BYTE job;
- BYTE voice;
- int level;
- DWORD exp;
- long gold;
- int hp;
- int sp;
- int iRandomHP;
- int iRandomSP;
- int stamina;
- BYTE skill_group;
- } CHARACTER_POINT;
- /* 저장되지 않는 캐릭터 데이터 */
- typedef struct character_point_instant
- {
- long points[POINT_MAX_NUM];
- float fRot;
- int iMaxHP;
- int iMaxSP;
- long position;
- long instant_flag;
- DWORD dwAIFlag;
- DWORD dwImmuneFlag;
- DWORD dwLastShoutPulse;
- WORD parts[PART_MAX_NUM];
- LPITEM pItems[INVENTORY_AND_EQUIP_SLOT_MAX];
- BYTE bItemGrid[INVENTORY_AND_EQUIP_SLOT_MAX];
- // 용혼석 인벤토리.
- LPITEM pDSItems[DRAGON_SOUL_INVENTORY_MAX_NUM];
- WORD wDSItemGrid[DRAGON_SOUL_INVENTORY_MAX_NUM];
- // by mhh
- LPITEM pCubeItems[CUBE_MAX_NUM];
- LPCHARACTER pCubeNpc;
- LPCHARACTER battle_victim;
- BYTE gm_level;
- BYTE bBasePart; // 평상복 번호
- int iMaxStamina;
- BYTE bBlockMode;
- int iDragonSoulActiveDeck;
- LPENTITY m_pDragonSoulRefineWindowOpener;
- } CHARACTER_POINT_INSTANT;
- #define TRIGGERPARAM LPCHARACTER ch, LPCHARACTER causer
- typedef struct trigger
- {
- BYTE type;
- int (*func) (TRIGGERPARAM);
- long value;
- } TRIGGER;
- class CTrigger
- {
- public:
- CTrigger() : bType(0), pFunc(NULL)
- {
- }
- BYTE bType;
- int (*pFunc) (TRIGGERPARAM);
- };
- EVENTINFO(char_event_info)
- {
- DynamicCharacterPtr ch;
- };
- struct TSkillUseInfo
- {
- int iHitCount;
- int iMaxHitCount;
- int iSplashCount;
- DWORD dwNextSkillUsableTime;
- int iRange;
- bool bUsed;
- DWORD dwVID;
- bool isGrandMaster;
- boost::unordered_map<DWORD, size_t> TargetVIDMap;
- TSkillUseInfo()
- : iHitCount(0), iMaxHitCount(0), iSplashCount(0), dwNextSkillUsableTime(0), iRange(0), bUsed(false),
- dwVID(0), isGrandMaster(false)
- {}
- bool HitOnce(DWORD dwVnum = 0);
- bool UseSkill(bool isGrandMaster, DWORD vid, DWORD dwCooltime, int splashcount = 1, int hitcount = -1, int range = -1);
- DWORD GetMainTargetVID() const { return dwVID; }
- void SetMainTargetVID(DWORD vid) { dwVID=vid; }
- void ResetHitCount() { if (iSplashCount) { iHitCount = iMaxHitCount; iSplashCount--; } }
- };
- typedef struct packet_party_update TPacketGCPartyUpdate;
- class CExchange;
- class CSkillProto;
- class CParty;
- class CDungeon;
- class CWarMap;
- class CAffect;
- class CGuild;
- class CSafebox;
- class CArena;
- class CShop;
- typedef class CShop * LPSHOP;
- class CMob;
- class CMobInstance;
- typedef struct SMobSkillInfo TMobSkillInfo;
- //SKILL_POWER_BY_LEVEL
- extern int GetSkillPowerByLevelFromType(int job, int skillgroup, int skilllevel);
- //END_SKILL_POWER_BY_LEVEL
- namespace marriage
- {
- class WeddingMap;
- }
- enum e_overtime
- {
- OT_NONE,
- OT_3HOUR,
- OT_5HOUR,
- };
- class CHARACTER : public CEntity, public CFSM, public CHorseRider
- {
- protected:
- //////////////////////////////////////////////////////////////////////////////////
- // Entity 관련
- virtual void EncodeInsertPacket(LPENTITY entity);
- virtual void EncodeRemovePacket(LPENTITY entity);
- //////////////////////////////////////////////////////////////////////////////////
- public:
- LPCHARACTER FindCharacterInView(const char * name, bool bFindPCOnly);
- void UpdatePacket();
- //////////////////////////////////////////////////////////////////////////////////
- // FSM (Finite State Machine) 관련
- protected:
- CStateTemplate<CHARACTER> m_stateMove;
- CStateTemplate<CHARACTER> m_stateBattle;
- CStateTemplate<CHARACTER> m_stateIdle;
- public:
- virtual void StateMove();
- virtual void StateBattle();
- virtual void StateIdle();
- virtual void StateFlag();
- virtual void StateFlagBase();
- void StateHorse();
- protected:
- // STATE_IDLE_REFACTORING
- void __StateIdle_Monster();
- void __StateIdle_Stone();
- void __StateIdle_NPC();
- // END_OF_STATE_IDLE_REFACTORING
- public:
- DWORD GetAIFlag() const { return m_pointsInstant.dwAIFlag; }
- void SetAggressive();
- bool IsAggressive() const;
- void SetCoward();
- bool IsCoward() const;
- void CowardEscape();
- void SetNoAttackShinsu();
- bool IsNoAttackShinsu() const;
- void SetNoAttackChunjo();
- bool IsNoAttackChunjo() const;
- void SetNoAttackJinno();
- bool IsNoAttackJinno() const;
- void SetAttackMob();
- bool IsAttackMob() const;
- virtual void BeginStateEmpty();
- virtual void EndStateEmpty() {}
- void RestartAtSamePos();
- protected:
- DWORD m_dwStateDuration;
- //////////////////////////////////////////////////////////////////////////////////
- public:
- CHARACTER();
- virtual ~CHARACTER();
- void Create(const char * c_pszName, DWORD vid, bool isPC);
- void Destroy();
- void Disconnect(const char * c_pszReason);
- protected:
- void Initialize();
- //////////////////////////////////////////////////////////////////////////////////
- // Basic Points
- public:
- DWORD GetPlayerID() const { return m_dwPlayerID; }
- void SetPlayerProto(const TPlayerTable * table);
- void CreatePlayerProto(TPlayerTable & tab); // 저장 시 사용
- void SetProto(const CMob * c_pkMob);
- WORD GetRaceNum() const;
- void Save(); // DelayedSave
- void SaveReal(); // 실제 저장
- void FlushDelayedSaveItem();
- const char * GetName() const;
- const VID & GetVID() const { return m_vid; }
- void SetName(const std::string& name) { m_stName = name; }
- void SetRace(BYTE race);
- bool ChangeSex();
- DWORD GetAID() const;
- int GetChangeEmpireCount() const;
- void SetChangeEmpireCount();
- int ChangeEmpire(BYTE empire);
- BYTE GetJob() const;
- BYTE GetCharType() const;
- bool IsPC() const { return GetDesc() ? true : false; }
- bool IsNPC() const { return m_bCharType != CHAR_TYPE_PC; }
- bool IsMonster() const { return m_bCharType == CHAR_TYPE_MONSTER; }
- bool IsStone() const { return m_bCharType == CHAR_TYPE_STONE; }
- bool IsDoor() const { return m_bCharType == CHAR_TYPE_DOOR; }
- bool IsBuilding() const { return m_bCharType == CHAR_TYPE_BUILDING; }
- bool IsWarp() const { return m_bCharType == CHAR_TYPE_WARP; }
- bool IsGoto() const { return m_bCharType == CHAR_TYPE_GOTO; }
- // bool IsPet() const { return m_bCharType == CHAR_TYPE_PET; }
- DWORD GetLastShoutPulse() const { return m_pointsInstant.dwLastShoutPulse; }
- void SetLastShoutPulse(DWORD pulse) { m_pointsInstant.dwLastShoutPulse = pulse; }
- int GetLevel() const { return m_points.level; }
- void SetLevel(int level);
- BYTE GetGMLevel() const;
- BOOL IsGM() const;
- void SetGMLevel();
- DWORD GetExp() const { return m_points.exp; }
- void SetExp(DWORD exp) { m_points.exp = exp; }
- long long GetNextExp();
- LPCHARACTER DistributeExp(); // 제일 많이 때린 사람을 리턴한다.
- void DistributeHP(LPCHARACTER pkKiller);
- void DistributeSP(LPCHARACTER pkKiller, int iMethod=0);
- void SetPosition(int pos);
- bool IsPosition(int pos) const { return m_pointsInstant.position == pos ? true : false; }
- int GetPosition() const { return m_pointsInstant.position; }
- void SetPart(BYTE bPartPos, WORD wVal);
- WORD GetPart(BYTE bPartPos) const;
- WORD GetOriginalPart(BYTE bPartPos) const;
- void SetHP(int hp) { m_points.hp = hp; }
- int GetHP() const { return m_points.hp; }
- void SetSP(int sp) { m_points.sp = sp; }
- int GetSP() const { return m_points.sp; }
- void SetStamina(int stamina) { m_points.stamina = stamina; }
- int GetStamina() const { return m_points.stamina; }
- void SetMaxHP(int iVal) { m_pointsInstant.iMaxHP = iVal; }
- int GetMaxHP() const { return m_pointsInstant.iMaxHP; }
- void SetMaxSP(int iVal) { m_pointsInstant.iMaxSP = iVal; }
- int GetMaxSP() const { return m_pointsInstant.iMaxSP; }
- void SetMaxStamina(int iVal) { m_pointsInstant.iMaxStamina = iVal; }
- int GetMaxStamina() const { return m_pointsInstant.iMaxStamina; }
- void SetRandomHP(int v) { m_points.iRandomHP = v; }
- void SetRandomSP(int v) { m_points.iRandomSP = v; }
- int GetRandomHP() const { return m_points.iRandomHP; }
- int GetRandomSP() const { return m_points.iRandomSP; }
- int GetHPPct() const;
- void SetRealPoint(BYTE idx, int val);
- int GetRealPoint(BYTE idx) const;
- void SetPoint(BYTE idx, int val);
- int GetPoint(BYTE idx) const;
- int GetLimitPoint(BYTE idx) const;
- int GetPolymorphPoint(BYTE idx) const;
- const TMobTable & GetMobTable() const;
- BYTE GetMobRank() const;
- BYTE GetMobBattleType() const;
- BYTE GetMobSize() const;
- DWORD GetMobDamageMin() const;
- DWORD GetMobDamageMax() const;
- WORD GetMobAttackRange() const;
- DWORD GetMobDropItemVnum() const;
- float GetMobDamageMultiply() const;
- // NEWAI
- bool IsBerserker() const;
- bool IsBerserk() const;
- void SetBerserk(bool mode);
- bool IsStoneSkinner() const;
- bool IsGodSpeeder() const;
- bool IsGodSpeed() const;
- void SetGodSpeed(bool mode);
- bool IsDeathBlower() const;
- bool IsDeathBlow() const;
- bool IsReviver() const;
- bool HasReviverInParty() const;
- bool IsRevive() const;
- void SetRevive(bool mode);
- // NEWAI END
- bool IsRaceFlag(DWORD dwBit) const;
- bool IsSummonMonster() const;
- DWORD GetSummonVnum() const;
- DWORD GetPolymorphItemVnum() const;
- DWORD GetMonsterDrainSPPoint() const;
- void MainCharacterPacket(); // 내가 메인캐릭터라고 보내준다.
- void ComputePoints();
- void ComputeBattlePoints();
- void PointChange(BYTE type, int amount, bool bAmount = false, bool bBroadcast = false);
- void PointsPacket();
- void ApplyPoint(BYTE bApplyType, int iVal);
- void CheckMaximumPoints(); // HP, SP 등의 현재 값이 최대값 보다 높은지 검사하고 높다면 낮춘다.
- bool Show(long lMapIndex, long x, long y, long z = LONG_MAX, bool bShowSpawnMotion = false);
- void Sitdown(int is_ground);
- void Standup();
- void SetRotation(float fRot);
- void SetRotationToXY(long x, long y);
- float GetRotation() const { return m_pointsInstant.fRot; }
- void MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet);
- void Motion(BYTE motion, LPCHARACTER victim = NULL);
- void ChatPacket(BYTE type, const char *format, ...);
- void MonsterChat(BYTE bMonsterChatType);
- void SendGreetMessage();
- void ResetPoint(int iLv);
- void SetBlockMode(BYTE bFlag);
- void SetBlockModeForce(BYTE bFlag);
- bool IsBlockMode(BYTE bFlag) const { return (m_pointsInstant.bBlockMode & bFlag)?true:false; }
- bool IsPolymorphed() const { return m_dwPolymorphRace>0; }
- bool IsPolyMaintainStat() const { return m_bPolyMaintainStat; } // 이전 스텟을 유지하는 폴리모프.
- void SetPolymorph(DWORD dwRaceNum, bool bMaintainStat = false);
- DWORD GetPolymorphVnum() const { return m_dwPolymorphRace; }
- int GetPolymorphPower() const;
- // FISING
- void fishing();
- void fishing_take();
- // END_OF_FISHING
- // MINING
- void mining(LPCHARACTER chLoad);
- void mining_cancel();
- void mining_take();
- // END_OF_MINING
- void ResetPlayTime(DWORD dwTimeRemain = 0);
- void CreateFly(BYTE bType, LPCHARACTER pkVictim);
- void ResetChatCounter();
- BYTE IncreaseChatCounter();
- BYTE GetChatCounter() const;
- protected:
- DWORD m_dwPolymorphRace;
- bool m_bPolyMaintainStat;
- DWORD m_dwLoginPlayTime;
- DWORD m_dwPlayerID;
- VID m_vid;
- std::string m_stName;
- BYTE m_bCharType;
- CHARACTER_POINT m_points;
- CHARACTER_POINT_INSTANT m_pointsInstant;
- int m_iMoveCount;
- DWORD m_dwPlayStartTime;
- BYTE m_bAddChrState;
- bool m_bSkipSave;
- std::string m_stMobile;
- char m_szMobileAuth[5];
- BYTE m_bChatCounter;
- // End of Basic Points
- //////////////////////////////////////////////////////////////////////////////////
- // Move & Synchronize Positions
- //////////////////////////////////////////////////////////////////////////////////
- public:
- bool IsStateMove() const { return IsState((CState&)m_stateMove); }
- bool IsStateIdle() const { return IsState((CState&)m_stateIdle); }
- bool IsWalking() const { return m_bNowWalking || GetStamina()<=0; }
- void SetWalking(bool bWalkFlag) { m_bWalking=bWalkFlag; }
- void SetNowWalking(bool bWalkFlag);
- void ResetWalking() { SetNowWalking(m_bWalking); }
- bool Goto(long x, long y); // 바로 이동 시키지 않고 목표 위치로 BLENDING 시킨다.
- void Stop();
- bool CanMove() const; // 이동할 수 있는가?
- void SyncPacket();
- bool Sync(long x, long y); // 실제 이 메소드로 이동 한다 (각 종 조건에 의한 이동 불가가 없음)
- bool Move(long x, long y); // 조건을 검사하고 Sync 메소드를 통해 이동 한다.
- void OnMove(bool bIsAttack = false); // 움직일때 불린다. Move() 메소드 이외에서도 불릴 수 있다.
- DWORD GetMotionMode() const;
- float GetMoveMotionSpeed() const;
- float GetMoveSpeed() const;
- void CalculateMoveDuration();
- void SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime=0, int iRot=-1);
- DWORD GetCurrentMoveDuration() const { return m_dwMoveDuration; }
- DWORD GetWalkStartTime() const { return m_dwWalkStartTime; }
- DWORD GetLastMoveTime() const { return m_dwLastMoveTime; }
- DWORD GetLastAttackTime() const { return m_dwLastAttackTime; }
- void SetLastAttacked(DWORD time); // 마지막으로 공격받은 시간 및 위치를 저장함
- bool SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList = true);
- bool IsSyncOwner(LPCHARACTER ch) const;
- bool WarpSet(long x, long y, long lRealMapIndex = 0);
- void SetWarpLocation(long lMapIndex, long x, long y);
- void WarpEnd();
- const PIXEL_POSITION & GetWarpPosition() const { return m_posWarp; }
- bool WarpToPID(DWORD dwPID);
- void SaveExitLocation();
- void ExitToSavedLocation();
- void StartStaminaConsume();
- void StopStaminaConsume();
- bool IsStaminaConsume() const;
- bool IsStaminaHalfConsume() const;
- void ResetStopTime();
- DWORD GetStopTime() const;
- protected:
- void ClearSync();
- float m_fSyncTime;
- LPCHARACTER m_pkChrSyncOwner;
- CHARACTER_LIST m_kLst_pkChrSyncOwned; // 내가 SyncOwner인 자들
- PIXEL_POSITION m_posDest;
- PIXEL_POSITION m_posStart;
- PIXEL_POSITION m_posWarp;
- long m_lWarpMapIndex;
- PIXEL_POSITION m_posExit;
- long m_lExitMapIndex;
- DWORD m_dwMoveStartTime;
- DWORD m_dwMoveDuration;
- DWORD m_dwLastMoveTime;
- DWORD m_dwLastAttackTime;
- DWORD m_dwWalkStartTime;
- DWORD m_dwStopTime;
- bool m_bWalking;
- bool m_bNowWalking;
- bool m_bStaminaConsume;
- // End
- // Quickslot 관련
- public:
- void SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos);
- bool GetQuickslot(BYTE pos, TQuickslot ** ppSlot);
- bool SetQuickslot(BYTE pos, TQuickslot & rSlot);
- bool DelQuickslot(BYTE pos);
- bool SwapQuickslot(BYTE a, BYTE b);
- void ChainQuickslotItem(LPITEM pItem, BYTE bType, BYTE bOldPos);
- protected:
- TQuickslot m_quickslot[QUICKSLOT_MAX_NUM];
- ////////////////////////////////////////////////////////////////////////////////////////
- // Affect
- public:
- void StartAffectEvent();
- void ClearAffect(bool bSave=false);
- void ComputeAffect(CAffect * pkAff, bool bAdd);
- bool AddAffect(DWORD dwType, BYTE bApplyOn, long lApplyValue, DWORD dwFlag, long lDuration, long lSPCost, bool bOverride, bool IsCube = false);
- void RefreshAffect();
- bool RemoveAffect(DWORD dwType);
- bool IsAffectFlag(DWORD dwAff) const;
- bool UpdateAffect(); // called from EVENT
- int ProcessAffect();
- void LoadAffect(DWORD dwCount, TPacketAffectElement * pElements);
- void SaveAffect();
- // Affect loading이 끝난 상태인가?
- bool IsLoadedAffect() const { return m_bIsLoadedAffect; }
- bool IsGoodAffect(BYTE bAffectType) const;
- void RemoveGoodAffect();
- void RemoveBadAffect();
- CAffect * FindAffect(DWORD dwType, BYTE bApply=APPLY_NONE) const;
- const std::list<CAffect *> & GetAffectContainer() const { return m_list_pkAffect; }
- bool RemoveAffect(CAffect * pkAff);
- protected:
- bool m_bIsLoadedAffect;
- TAffectFlag m_afAffectFlag;
- std::list<CAffect *> m_list_pkAffect;
- public:
- // PARTY_JOIN_BUG_FIX
- void SetParty(LPPARTY pkParty);
- LPPARTY GetParty() const { return m_pkParty; }
- bool RequestToParty(LPCHARACTER leader);
- void DenyToParty(LPCHARACTER member);
- void AcceptToParty(LPCHARACTER member);
- /// 자신의 파티에 다른 character 를 초대한다.
- /**
- * @param pchInvitee 초대할 대상 character. 파티에 참여 가능한 상태이어야 한다.
- *
- * 양측 character 의 상태가 파티에 초대하고 초대받을 수 있는 상태가 아니라면 초대하는 캐릭터에게 해당하는 채팅 메세지를 전송한다.
- */
- void PartyInvite(LPCHARACTER pchInvitee);
- /// 초대했던 character 의 수락을 처리한다.
- /**
- * @param pchInvitee 파티에 참여할 character. 파티에 참여가능한 상태이어야 한다.
- *
- * pchInvitee 가 파티에 가입할 수 있는 상황이 아니라면 해당하는 채팅 메세지를 전송한다.
- */
- void PartyInviteAccept(LPCHARACTER pchInvitee);
- /// 초대했던 character 의 초대 거부를 처리한다.
- /**
- * @param [in] dwPID 초대 했던 character 의 PID
- */
- void PartyInviteDeny(DWORD dwPID);
- bool BuildUpdatePartyPacket(TPacketGCPartyUpdate & out);
- int GetLeadershipSkillLevel() const;
- bool CanSummon(int iLeaderShip);
- void SetPartyRequestEvent(LPEVENT pkEvent) { m_pkPartyRequestEvent = pkEvent; }
- protected:
- /// 파티에 가입한다.
- /**
- * @param pkLeader 가입할 파티의 리더
- */
- void PartyJoin(LPCHARACTER pkLeader);
- /**
- * 파티 가입을 할 수 없을 경우의 에러코드.
- * Error code 는 시간에 의존적인가에 따라 변경가능한(mutable) type 과 정적(static) type 으로 나뉜다.
- * Error code 의 값이 PERR_SEPARATOR 보다 낮으면 변경가능한 type 이고 높으면 정적 type 이다.
- */
- enum PartyJoinErrCode {
- PERR_NONE = 0, ///< 처리성공
- PERR_SERVER, ///< 서버문제로 파티관련 처리 불가
- PERR_DUNGEON, ///< 캐릭터가 던전에 있음
- PERR_OBSERVER, ///< 관전모드임
- PERR_LVBOUNDARY, ///< 상대 캐릭터와 레벨차이가 남
- PERR_LOWLEVEL, ///< 상대파티의 최고레벨보다 30레벨 낮음
- PERR_HILEVEL, ///< 상대파티의 최저레벨보다 30레벨 높음
- PERR_ALREADYJOIN, ///< 파티가입 대상 캐릭터가 이미 파티중
- PERR_PARTYISFULL, ///< 파티인원 제한 초과
- PERR_SEPARATOR, ///< Error type separator.
- PERR_DIFFEMPIRE, ///< 상대 캐릭터와 다른 제국임
- PERR_MAX ///< Error code 최고치. 이 앞에 Error code 를 추가한다.
- };
- /// 파티 가입이나 결성 가능한 조건을 검사한다.
- /**
- * @param pchLeader 파티의 leader 이거나 초대한 character
- * @param pchGuest 초대받는 character
- * @return 모든 PartyJoinErrCode 가 반환될 수 있다.
- */
- static PartyJoinErrCode IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest);
- /// 파티 가입이나 결성 가능한 동적인 조건을 검사한다.
- /**
- * @param pchLeader 파티의 leader 이거나 초대한 character
- * @param pchGuest 초대받는 character
- * @return mutable type 의 code 만 반환한다.
- */
- static PartyJoinErrCode IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest);
- LPPARTY m_pkParty;
- DWORD m_dwLastDeadTime;
- LPEVENT m_pkPartyRequestEvent;
- /**
- * 파티초청 Event map.
- * key: 초대받은 캐릭터의 PID
- * value: event의 pointer
- *
- * 초대한 캐릭터들에 대한 event map.
- */
- typedef std::map< DWORD, LPEVENT > EventMap;
- EventMap m_PartyInviteEventMap;
- // END_OF_PARTY_JOIN_BUG_FIX
- ////////////////////////////////////////////////////////////////////////////////////////
- // Dungeon
- public:
- void SetDungeon(LPDUNGEON pkDungeon);
- LPDUNGEON GetDungeon() const { return m_pkDungeon; }
- LPDUNGEON GetDungeonForce() const;
- protected:
- LPDUNGEON m_pkDungeon;
- int m_iEventAttr;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Guild
- public:
- void SetGuild(CGuild * pGuild);
- CGuild* GetGuild() const { return m_pGuild; }
- void SetWarMap(CWarMap* pWarMap);
- CWarMap* GetWarMap() const { return m_pWarMap; }
- protected:
- CGuild * m_pGuild;
- DWORD m_dwUnderGuildWarInfoMessageTime;
- CWarMap * m_pWarMap;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Item related
- public:
- bool CanHandleItem(bool bSkipRefineCheck = false, bool bSkipObserver = false); // 아이템 관련 행위를 할 수 있는가?
- bool IsItemLoaded() const { return m_bItemLoaded; }
- void SetItemLoaded() { m_bItemLoaded = true; }
- void ClearItem();
- void SetItem(TItemPos Cell, LPITEM item);
- LPITEM GetItem(TItemPos Cell) const;
- LPITEM GetInventoryItem(WORD wCell) const;
- bool IsEmptyItemGrid(TItemPos Cell, BYTE size, int iExceptionCell = -1) const;
- void SetWear(BYTE bCell, LPITEM item);
- LPITEM GetWear(BYTE bCell) const;
- // MYSHOP_PRICE_LIST
- void UseSilkBotary(void); /// 비단 보따리 아이템의 사용
- /// DB 캐시로 부터 받아온 가격정보 리스트를 유저에게 전송하고 보따리 아이템 사용을 처리한다.
- /**
- * @param [in] p 가격정보 리스트 패킷
- *
- * 접속한 후 처음 비단 보따리 아이템 사용 시 UseSilkBotary 에서 DB 캐시로 가격정보 리스트를 요청하고
- * 응답받은 시점에 이 함수에서 실제 비단보따리 사용을 처리한다.
- */
- void UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p);
- // END_OF_MYSHOP_PRICE_LIST
- bool UseItemEx(LPITEM item, TItemPos DestCell);
- bool UseItem(TItemPos Cell, TItemPos DestCell = NPOS);
- // ADD_REFINE_BUILDING
- bool IsRefineThroughGuild() const;
- CGuild * GetRefineGuild() const;
- int ComputeRefineFee(int iCost, int iMultiply = 5) const;
- void PayRefineFee(int iTotalMoney);
- void SetRefineNPC(LPCHARACTER ch);
- // END_OF_ADD_REFINE_BUILDING
- bool RefineItem(LPITEM pkItem, LPITEM pkTarget);
- bool DropItem(TItemPos Cell, BYTE bCount=0);
- bool GiveRecallItem(LPITEM item);
- void ProcessRecallItem(LPITEM item);
- // void PotionPacket(int iPotionType);
- void EffectPacket(int enumEffectType);
- void SpecificEffectPacket(const char filename[128]);
- // ADD_MONSTER_REFINE
- bool DoRefine(LPITEM item, bool bMoneyOnly = false);
- // END_OF_ADD_MONSTER_REFINE
- bool DoRefineWithScroll(LPITEM item);
- bool RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell = -1);
- void SetRefineMode(int iAdditionalCell = -1);
- void ClearRefineMode();
- bool GiveItem(LPCHARACTER victim, TItemPos Cell);
- bool CanReceiveItem(LPCHARACTER from, LPITEM item) const;
- void ReceiveItem(LPCHARACTER from, LPITEM item);
- bool GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector <DWORD> &dwItemVnums,
- std::vector <DWORD> &dwItemCounts, std::vector <LPITEM> &item_gets, int &count);
- bool MoveItem(TItemPos pos, TItemPos change_pos, BYTE num);
- bool PickupItem(DWORD vid);
- bool EquipItem(LPITEM item, int iCandidateCell = -1);
- bool UnequipItem(LPITEM item);
- // 현재 item을 착용할 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
- bool CanEquipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS);
- // 착용중인 item을 벗을 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
- bool CanUnequipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS);
- bool SwapItem(BYTE bCell, BYTE bDestCell);
- LPITEM AutoGiveItem(DWORD dwItemVnum, BYTE bCount=1, int iRarePct = -1, bool bMsg = true);
- void AutoGiveItem(LPITEM item, bool longOwnerShip = false);
- int GetEmptyInventory(BYTE size) const;
- int GetEmptyDragonSoulInventory(LPITEM pItem) const;
- void CopyDragonSoulItemGrid(std::vector<WORD>& vDragonSoulItemGrid) const;
- int CountEmptyInventory() const;
- int CountSpecifyItem(DWORD vnum) const;
- void RemoveSpecifyItem(DWORD vnum, DWORD count = 1);
- LPITEM FindSpecifyItem(DWORD vnum) const;
- LPITEM FindItemByID(DWORD id) const;
- int CountSpecifyTypeItem(BYTE type) const;
- void RemoveSpecifyTypeItem(BYTE type, DWORD count = 1);
- bool IsEquipUniqueItem(DWORD dwItemVnum) const;
- // CHECK_UNIQUE_GROUP
- bool IsEquipUniqueGroup(DWORD dwGroupVnum) const;
- // END_OF_CHECK_UNIQUE_GROUP
- void SendEquipment(LPCHARACTER ch);
- // End of Item
- protected:
- /// 한 아이템에 대한 가격정보를 전송한다.
- /**
- * @param [in] dwItemVnum 아이템 vnum
- * @param [in] dwItemPrice 아이템 가격
- */
- void SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice);
- bool m_bNoOpenedShop; ///< 이번 접속 후 개인상점을 연 적이 있는지의 여부(열었던 적이 없다면 true)
- bool m_bItemLoaded;
- int m_iRefineAdditionalCell;
- bool m_bUnderRefine;
- DWORD m_dwRefineNPCVID;
- public:
- ////////////////////////////////////////////////////////////////////////////////////////
- // Money related
- INT GetGold() const { return m_points.gold; }
- void SetGold(INT gold) { m_points.gold = gold; }
- bool DropGold(INT gold);
- INT GetAllowedGold() const;
- void GiveGold(INT iAmount); // 파티가 있으면 파티 분배, 로그 등의 처리
- // End of Money
- ////////////////////////////////////////////////////////////////////////////////////////
- // Shop related
- public:
- void SetShop(LPSHOP pkShop);
- LPSHOP GetShop() const { return m_pkShop; }
- void ShopPacket(BYTE bSubHeader);
- void SetShopOwner(LPCHARACTER ch) { m_pkChrShopOwner = ch; }
- LPCHARACTER GetShopOwner() const { return m_pkChrShopOwner;}
- void OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount);
- LPSHOP GetMyShop() const { return m_pkMyShop; }
- void CloseMyShop();
- protected:
- LPSHOP m_pkShop;
- LPSHOP m_pkMyShop;
- std::string m_stShopSign;
- LPCHARACTER m_pkChrShopOwner;
- // End of shop
- ////////////////////////////////////////////////////////////////////////////////////////
- // Exchange related
- public:
- bool ExchangeStart(LPCHARACTER victim);
- void SetExchange(CExchange * pkExchange);
- CExchange * GetExchange() const { return m_pkExchange; }
- protected:
- CExchange * m_pkExchange;
- // End of Exchange
- ////////////////////////////////////////////////////////////////////////////////////////
- // Battle
- public:
- struct TBattleInfo
- {
- int iTotalDamage;
- int iAggro;
- TBattleInfo(int iTot, int iAggr)
- : iTotalDamage(iTot), iAggro(iAggr)
- {}
- };
- typedef std::map<VID, TBattleInfo> TDamageMap;
- typedef struct SAttackLog
- {
- DWORD dwVID;
- DWORD dwTime;
- } AttackLog;
- bool Damage(LPCHARACTER pAttacker, int dam, EDamageType type = DAMAGE_TYPE_NORMAL);
- bool __Profile__Damage(LPCHARACTER pAttacker, int dam, EDamageType type = DAMAGE_TYPE_NORMAL);
- void DeathPenalty(BYTE bExpLossPercent);
- void ReviveInvisible(int iDur);
- bool Attack(LPCHARACTER pkVictim, BYTE bType = 0);
- bool IsAlive() const { return m_pointsInstant.position == POS_DEAD ? false : true; }
- bool CanFight() const;
- bool CanBeginFight() const;
- void BeginFight(LPCHARACTER pkVictim); // pkVictimr과 싸우기 시작한다. (강제적임, 시작할 수 있나 체크하려면 CanBeginFight을 사용)
- bool CounterAttack(LPCHARACTER pkChr); // 반격하기 (몬스터만 사용)
- bool IsStun() const;
- void Stun();
- bool IsDead() const;
- void Dead(LPCHARACTER pkKiller = NULL, bool bImmediateDead=false);
- void Reward(bool bItemDrop);
- void RewardGold(LPCHARACTER pkAttacker);
- bool Shoot(BYTE bType);
- void FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader);
- void ForgetMyAttacker();
- void AggregateMonster();
- void AttractRanger();
- void PullMonster();
- int GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount = 1);
- void UseArrow(LPITEM pkArrow, DWORD dwArrowCount);
- void AttackedByPoison(LPCHARACTER pkAttacker);
- void RemovePoison();
- void AttackedByFire(LPCHARACTER pkAttacker, int amount, int count);
- void RemoveFire();
- void UpdateAlignment(int iAmount);
- int GetAlignment() const;
- //선악치 얻기
- int GetRealAlignment() const;
- void ShowAlignment(bool bShow);
- void SetKillerMode(bool bOn);
- bool IsKillerMode() const;
- void UpdateKillerMode();
- BYTE GetPKMode() const;
- void SetPKMode(BYTE bPKMode);
- void ItemDropPenalty(LPCHARACTER pkKiller);
- void UpdateAggrPoint(LPCHARACTER ch, EDamageType type, int dam);
- //
- // HACK
- //
- public:
- void SetComboSequence(BYTE seq);
- BYTE GetComboSequence() const;
- void SetLastComboTime(DWORD time);
- DWORD GetLastComboTime() const;
- int GetValidComboInterval() const;
- void SetValidComboInterval(int interval);
- BYTE GetComboIndex() const;
- void IncreaseComboHackCount(int k = 1);
- void ResetComboHackCount();
- void SkipComboAttackByTime(int interval);
- DWORD GetSkipComboAttackByTime() const;
- protected:
- BYTE m_bComboSequence;
- DWORD m_dwLastComboTime;
- int m_iValidComboInterval;
- BYTE m_bComboIndex;
- int m_iComboHackCount;
- DWORD m_dwSkipComboAttackByTime;
- protected:
- void UpdateAggrPointEx(LPCHARACTER ch, EDamageType type, int dam, TBattleInfo & info);
- void ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim);
- DWORD m_dwFlyTargetID;
- std::vector<DWORD> m_vec_dwFlyTargets;
- TDamageMap m_map_kDamage; // 어떤 캐릭터가 나에게 얼마만큼의 데미지를 주었는가?
- // AttackLog m_kAttackLog;
- DWORD m_dwKillerPID;
- int m_iAlignment; // Lawful/Chaotic value -200000 ~ 200000
- int m_iRealAlignment;
- int m_iKillerModePulse;
- BYTE m_bPKMode;
- // Aggro
- DWORD m_dwLastVictimSetTime;
- int m_iMaxAggro;
- // End of Battle
- // Stone
- public:
- void SetStone(LPCHARACTER pkChrStone);
- void ClearStone();
- void DetermineDropMetinStone();
- DWORD GetDropMetinStoneVnum() const { return m_dwDropMetinStone; }
- BYTE GetDropMetinStonePct() const { return m_bDropMetinStonePct; }
- protected:
- LPCHARACTER m_pkChrStone; // 나를 스폰한 돌
- CHARACTER_SET m_set_pkChrSpawnedBy; // 내가 스폰한 놈들
- DWORD m_dwDropMetinStone;
- BYTE m_bDropMetinStonePct;
- // End of Stone
- public:
- enum
- {
- SKILL_UP_BY_POINT,
- SKILL_UP_BY_BOOK,
- SKILL_UP_BY_TRAIN,
- // ADD_GRANDMASTER_SKILL
- SKILL_UP_BY_QUEST,
- // END_OF_ADD_GRANDMASTER_SKILL
- };
- void SkillLevelPacket();
- void SkillLevelUp(DWORD dwVnum, BYTE bMethod = SKILL_UP_BY_POINT);
- bool SkillLevelDown(DWORD dwVnum);
- // ADD_GRANDMASTER_SKILL
- bool UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster = true);
- void ResetSkill();
- void SetSkillLevel(DWORD dwVnum, BYTE bLev);
- int GetUsedSkillMasterType(DWORD dwVnum);
- bool IsLearnableSkill(DWORD dwSkillVnum) const;
- // END_OF_ADD_GRANDMASTER_SKILL
- bool CheckSkillHitCount(const BYTE SkillID, const VID dwTargetVID);
- bool CanUseSkill(DWORD dwSkillVnum) const;
- bool IsUsableSkillMotion(DWORD dwMotionIndex) const;
- int GetSkillLevel(DWORD dwVnum) const;
- int GetSkillMasterType(DWORD dwVnum) const;
- int GetSkillPower(DWORD dwVnum, BYTE bLevel = 0) const;
- time_t GetSkillNextReadTime(DWORD dwVnum) const;
- void SetSkillNextReadTime(DWORD dwVnum, time_t time);
- void SkillLearnWaitMoreTimeMessage(DWORD dwVnum);
- void ComputePassiveSkill(DWORD dwVnum);
- int ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel = 0);
- int ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTarget, BYTE bSkillLevel = 0);
- void ComputeSkillPoints();
- void SetSkillGroup(BYTE bSkillGroup);
- BYTE GetSkillGroup() const { return m_points.skill_group; }
- int ComputeCooltime(int time);
- void GiveRandomSkillBook();
- void DisableCooltime();
- bool LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb = 0);
- bool LearnGrandMasterSkill(DWORD dwSkillVnum);
- private:
- bool m_bDisableCooltime;
- DWORD m_dwLastSkillTime; ///< 마지막으로 skill 을 쓴 시간(millisecond).
- // End of Skill
- // MOB_SKILL
- public:
- bool HasMobSkill() const;
- size_t CountMobSkill() const;
- const TMobSkillInfo * GetMobSkill(unsigned int idx) const;
- bool CanUseMobSkill(unsigned int idx) const;
- bool UseMobSkill(unsigned int idx);
- void ResetMobSkillCooltime();
- protected:
- DWORD m_adwMobSkillCooltime[MOB_SKILL_MAX_NUM];
- // END_OF_MOB_SKILL
- // for SKILL_MUYEONG
- public:
- void StartMuyeongEvent();
- void StopMuyeongEvent();
- private:
- LPEVENT m_pkMuyeongEvent;
- // for SKILL_CHAIN lighting
- public:
- int GetChainLightningIndex() const { return m_iChainLightingIndex; }
- void IncChainLightningIndex() { ++m_iChainLightingIndex; }
- void AddChainLightningExcept(LPCHARACTER ch) { m_setExceptChainLighting.insert(ch); }
- void ResetChainLightningIndex() { m_iChainLightingIndex = 0; m_setExceptChainLighting.clear(); }
- int GetChainLightningMaxCount() const;
- const CHARACTER_SET& GetChainLightingExcept() const { return m_setExceptChainLighting; }
- private:
- int m_iChainLightingIndex;
- CHARACTER_SET m_setExceptChainLighting;
- // for SKILL_EUNHYUNG
- public:
- void SetAffectedEunhyung();
- void ClearAffectedEunhyung() { m_dwAffectedEunhyungLevel = 0; }
- bool GetAffectedEunhyung() const { return m_dwAffectedEunhyungLevel; }
- private:
- DWORD m_dwAffectedEunhyungLevel;
- //
- // Skill levels
- //
- protected:
- TPlayerSkill* m_pSkillLevels;
- boost::unordered_map<BYTE, int> m_SkillDamageBonus;
- std::map<int, TSkillUseInfo> m_SkillUseInfo;
- ////////////////////////////////////////////////////////////////////////////////////////
- // AI related
- public:
- void AssignTriggers(const TMobTable * table);
- LPCHARACTER GetVictim() const; // 공격할 대상 리턴
- void SetVictim(LPCHARACTER pkVictim);
- LPCHARACTER GetNearestVictim(LPCHARACTER pkChr);
- LPCHARACTER GetProtege() const; // 보호해야 할 대상 리턴
- bool Follow(LPCHARACTER pkChr, float fMinimumDistance = 150.0f);
- bool Return();
- bool IsGuardNPC() const;
- bool IsChangeAttackPosition(LPCHARACTER target) const;
- void ResetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time() - AI_CHANGE_ATTACK_POISITION_TIME_NEAR;}
- void SetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time();}
- bool OnIdle();
- void OnAttack(LPCHARACTER pkChrAttacker);
- void OnClick(LPCHARACTER pkChrCauser);
- VID m_kVIDVictim;
- protected:
- DWORD m_dwLastChangeAttackPositionTime;
- CTrigger m_triggerOnClick;
- // End of AI
- ////////////////////////////////////////////////////////////////////////////////////////
- // Target
- protected:
- LPCHARACTER m_pkChrTarget; // 내 타겟
- CHARACTER_SET m_set_pkChrTargetedBy; // 나를 타겟으로 가지고 있는 사람들
- public:
- void SetTarget(LPCHARACTER pkChrTarget);
- void BroadcastTargetPacket();
- void ClearTarget();
- void CheckTarget();
- LPCHARACTER GetTarget() const { return m_pkChrTarget; }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Safebox
- public:
- int GetSafeboxSize() const;
- void QuerySafeboxSize();
- void SetSafeboxSize(int size);
- CSafebox * GetSafebox() const;
- void LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems);
- void ChangeSafeboxSize(BYTE bSize);
- void CloseSafebox();
- /// 창고 열기 요청
- /**
- * @param [in] pszPassword 1자 이상 6자 이하의 창고 비밀번호
- *
- * DB 에 창고열기를 요청한다.
- * 창고는 중복으로 열지 못하며, 최근 창고를 닫은 시간으로 부터 10초 이내에는 열 지 못한다.
- */
- void ReqSafeboxLoad(const char* pszPassword);
- /// 창고 열기 요청의 취소
- /**
- * ReqSafeboxLoad 를 호출하고 CloseSafebox 하지 않았을 때 이 함수를 호출하면 창고를 열 수 있다.
- * 창고열기의 요청이 DB 서버에서 실패응답을 받았을 경우 이 함수를 사용해서 요청을 할 수 있게 해준다.
- */
- void CancelSafeboxLoad( void ) { m_bOpeningSafebox = false; }
- void SetMallLoadTime(int t) { m_iMallLoadTime = t; }
- int GetMallLoadTime() const { return m_iMallLoadTime; }
- CSafebox * GetMall() const;
- void LoadMall(int iItemCount, TPlayerItem * pItems);
- void CloseMall();
- void SetSafeboxOpenPosition();
- float GetDistanceFromSafeboxOpen() const;
- protected:
- CSafebox * m_pkSafebox;
- int m_iSafeboxSize;
- int m_iSafeboxLoadTime;
- bool m_bOpeningSafebox; ///< 창고가 열기 요청 중이거나 열려있는가 여부, true 일 경우 열기요청이거나 열려있음.
- CSafebox * m_pkMall;
- int m_iMallLoadTime;
- PIXEL_POSITION m_posSafeboxOpen;
- ////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////
- // Mounting
- public:
- void MountVnum(DWORD vnum);
- DWORD GetMountVnum() const { return m_dwMountVnum; }
- DWORD GetLastMountTime() const { return m_dwMountTime; }
- bool CanUseHorseSkill();
- // Horse
- virtual void SetHorseLevel(int iLevel);
- virtual bool StartRiding();
- virtual bool StopRiding();
- virtual DWORD GetMyHorseVnum() const;
- virtual void HorseDie();
- virtual bool ReviveHorse();
- virtual void SendHorseInfo();
- virtual void ClearHorseInfo();
- void HorseSummon(bool bSummon, bool bFromFar = false, DWORD dwVnum = 0, const char* name = 0);
- LPCHARACTER GetHorse() const { return m_chHorse; } // 현재 소환중인 말
- LPCHARACTER GetRider() const; // rider on horse
- void SetRider(LPCHARACTER ch);
- bool IsRiding() const;
- #ifdef __PET_SYSTEM__
- public:
- CPetSystem* GetPetSystem() { return m_petSystem; }
- protected:
- CPetSystem* m_petSystem;
- public:
- #endif
- protected:
- LPCHARACTER m_chHorse;
- LPCHARACTER m_chRider;
- DWORD m_dwMountVnum;
- DWORD m_dwMountTime;
- BYTE m_bSendHorseLevel;
- BYTE m_bSendHorseHealthGrade;
- BYTE m_bSendHorseStaminaGrade;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Detailed Log
- public:
- void DetailLog() { m_bDetailLog = !m_bDetailLog; }
- void ToggleMonsterLog();
- void MonsterLog(const char* format, ...);
- private:
- bool m_bDetailLog;
- bool m_bMonsterLog;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Empire
- public:
- void SetEmpire(BYTE bEmpire);
- BYTE GetEmpire() const { return m_bEmpire; }
- protected:
- BYTE m_bEmpire;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Regen
- public:
- void SetRegen(LPREGEN pkRegen);
- protected:
- PIXEL_POSITION m_posRegen;
- float m_fRegenAngle;
- LPREGEN m_pkRegen;
- size_t regen_id_; // to help dungeon regen identification
- // End of Regen
- ////////////////////////////////////////////////////////////////////////////////////////
- // Resists & Proofs
- public:
- bool CannotMoveByAffect() const; // 특정 효과에 의해 움직일 수 없는 상태인가?
- bool IsImmune(DWORD dwImmuneFlag);
- void SetImmuneFlag(DWORD dw) { m_pointsInstant.dwImmuneFlag = dw; }
- protected:
- void ApplyMobAttribute(const TMobTable* table);
- // End of Resists & Proofs
- ////////////////////////////////////////////////////////////////////////////////////////
- // QUEST
- //
- public:
- void SetQuestNPCID(DWORD vid);
- DWORD GetQuestNPCID() const { return m_dwQuestNPCVID; }
- LPCHARACTER GetQuestNPC() const;
- void SetQuestItemPtr(LPITEM item);
- void ClearQuestItemPtr();
- LPITEM GetQuestItemPtr() const;
- void SetQuestBy(DWORD dwQuestVnum) { m_dwQuestByVnum = dwQuestVnum; }
- DWORD GetQuestBy() const { return m_dwQuestByVnum; }
- int GetQuestFlag(const std::string& flag) const;
- void SetQuestFlag(const std::string& flag, int value);
- void ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID);
- private:
- DWORD m_dwQuestNPCVID;
- DWORD m_dwQuestByVnum;
- LPITEM m_pQuestItem;
- // Events
- public:
- bool StartStateMachine(int iPulse = 1);
- void StopStateMachine();
- void UpdateStateMachine(DWORD dwPulse);
- void SetNextStatePulse(int iPulseNext);
- // 캐릭터 인스턴스 업데이트 함수. 기존엔 이상한 상속구조로 CFSM::Update 함수를 호출하거나 UpdateStateMachine 함수를 사용했는데, 별개의 업데이트 함수 추가함.
- void UpdateCharacter(DWORD dwPulse);
- protected:
- DWORD m_dwNextStatePulse;
- // Marriage
- public:
- LPCHARACTER GetMarryPartner() const;
- void SetMarryPartner(LPCHARACTER ch);
- int GetMarriageBonus(DWORD dwItemVnum, bool bSum = true);
- void SetWeddingMap(marriage::WeddingMap* pMap);
- marriage::WeddingMap* GetWeddingMap() const { return m_pWeddingMap; }
- private:
- marriage::WeddingMap* m_pWeddingMap;
- LPCHARACTER m_pkChrMarried;
- // Warp Character
- public:
- void StartWarpNPCEvent();
- public:
- void StartSaveEvent();
- void StartRecoveryEvent();
- void StartCheckSpeedHackEvent();
- void StartDestroyWhenIdleEvent();
- LPEVENT m_pkDeadEvent;
- LPEVENT m_pkStunEvent;
- LPEVENT m_pkSaveEvent;
- LPEVENT m_pkRecoveryEvent;
- LPEVENT m_pkTimedEvent;
- LPEVENT m_pkFishingEvent;
- LPEVENT m_pkAffectEvent;
- LPEVENT m_pkPoisonEvent;
- LPEVENT m_pkFireEvent;
- LPEVENT m_pkWarpNPCEvent;
- //DELAYED_WARP
- //END_DELAYED_WARP
- // MINING
- LPEVENT m_pkMiningEvent;
- // END_OF_MINING
- LPEVENT m_pkWarpEvent;
- LPEVENT m_pkCheckSpeedHackEvent;
- LPEVENT m_pkDestroyWhenIdleEvent;
- LPEVENT m_pkPetSystemUpdateEvent;
- bool IsWarping() const { return m_pkWarpEvent ? true : false; }
- bool m_bHasPoisoned;
- const CMob * m_pkMobData;
- CMobInstance * m_pkMobInst;
- std::map<int, LPEVENT> m_mapMobSkillEvent;
- friend struct FuncSplashDamage;
- friend struct FuncSplashAffect;
- friend class CFuncShoot;
- public:
- int GetPremiumRemainSeconds(BYTE bType) const;
- private:
- int m_aiPremiumTimes[PREMIUM_MAX_NUM];
- // CHANGE_ITEM_ATTRIBUTES
- static const DWORD msc_dwDefaultChangeItemAttrCycle; ///< 디폴트 아이템 속성변경 가능 주기
- static const char msc_szLastChangeItemAttrFlag[]; ///< 최근 아이템 속성을 변경한 시간의 Quest Flag 이름
- static const char msc_szChangeItemAttrCycleFlag[]; ///< 아이템 속성병경 가능 주기의 Quest Flag 이름
- // END_OF_CHANGE_ITEM_ATTRIBUTES
- // PC_BANG_ITEM_ADD
- private :
- bool m_isinPCBang;
- public :
- bool SetPCBang(bool flag) { m_isinPCBang = flag; return m_isinPCBang; }
- bool IsPCBang() const { return m_isinPCBang; }
- // END_PC_BANG_ITEM_ADD
- // NEW_HAIR_STYLE_ADD
- public :
- bool ItemProcess_Hair(LPITEM item, int iDestCell);
- // END_NEW_HAIR_STYLE_ADD
- public :
- void ClearSkill();
- void ClearSubSkill();
- // RESET_ONE_SKILL
- bool ResetOneSkill(DWORD dwVnum);
- // END_RESET_ONE_SKILL
- private :
- void SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag);
- // ARENA
- private :
- CArena *m_pArena;
- bool m_ArenaObserver;
- int m_nPotionLimit;
- public :
- void SetArena(CArena* pArena) { m_pArena = pArena; }
- void SetArenaObserverMode(bool flag) { m_ArenaObserver = flag; }
- CArena* GetArena() const { return m_pArena; }
- bool GetArenaObserverMode() const { return m_ArenaObserver; }
- void SetPotionLimit(int count) { m_nPotionLimit = count; }
- int GetPotionLimit() const { return m_nPotionLimit; }
- // END_ARENA
- //PREVENT_TRADE_WINDOW
- public:
- bool IsOpenSafebox() const { return m_isOpenSafebox ? true : false; }
- void SetOpenSafebox(bool b) { m_isOpenSafebox = b; }
- int GetSafeboxLoadTime() const { return m_iSafeboxLoadTime; }
- void SetSafeboxLoadTime() { m_iSafeboxLoadTime = thecore_pulse(); }
- //END_PREVENT_TRADE_WINDOW
- private:
- bool m_isOpenSafebox;
- public:
- int GetSkillPowerByLevel(int level, bool bMob = false) const;
- //PREVENT_REFINE_HACK
- int GetRefineTime() const { return m_iRefineTime; }
- void SetRefineTime() { m_iRefineTime = thecore_pulse(); }
- int m_iRefineTime;
- //END_PREVENT_REFINE_HACK
- //RESTRICT_USE_SEED_OR_MOONBOTTLE
- int GetUseSeedOrMoonBottleTime() const { return m_iSeedTime; }
- void SetUseSeedOrMoonBottleTime() { m_iSeedTime = thecore_pulse(); }
- int m_iSeedTime;
- //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
- //PREVENT_PORTAL_AFTER_EXCHANGE
- int GetExchangeTime() const { return m_iExchangeTime; }
- void SetExchangeTime() { m_iExchangeTime = thecore_pulse(); }
- int m_iExchangeTime;
- //END_PREVENT_PORTAL_AFTER_EXCHANGE
- int m_iMyShopTime;
- int GetMyShopTime() const { return m_iMyShopTime; }
- void SetMyShopTime() { m_iMyShopTime = thecore_pulse(); }
- // Hack 방지를 위한 체크.
- bool IsHack(bool bSendMsg = true, bool bCheckShopOwner = true, int limittime = g_nPortalLimitTime);
- // MONARCH
- BOOL IsMonarch() const;
- // END_MONARCH
- void Say(const std::string & s);
- enum MONARCH_COOLTIME
- {
- MC_HEAL = 10,
- MC_WARP = 60,
- MC_TRANSFER = 60,
- MC_TAX = (60 * 60 * 24 * 7),
- MC_SUMMON = (60 * 60),
- };
- enum MONARCH_INDEX
- {
- MI_HEAL = 0,
- MI_WARP,
- MI_TRANSFER,
- MI_TAX,
- MI_SUMMON,
- MI_MAX
- };
- DWORD m_dwMonarchCooltime[MI_MAX];
- DWORD m_dwMonarchCooltimelimit[MI_MAX];
- void InitMC();
- DWORD GetMC(enum MONARCH_INDEX e) const;
- void SetMC(enum MONARCH_INDEX e);
- bool IsMCOK(enum MONARCH_INDEX e) const;
- DWORD GetMCL(enum MONARCH_INDEX e) const;
- DWORD GetMCLTime(enum MONARCH_INDEX e) const;
- public:
- bool ItemProcess_Polymorph(LPITEM item);
- // by mhh
- LPITEM* GetCubeItem() { return m_pointsInstant.pCubeItems; }
- bool IsCubeOpen () const { return (m_pointsInstant.pCubeNpc?true:false); }
- void SetCubeNpc(LPCHARACTER npc) { m_pointsInstant.pCubeNpc = npc; }
- bool CanDoCube() const;
- public:
- bool IsSiegeNPC() const;
- private:
- //중국 전용
- //18세 미만 전용
- //3시간 : 50 % 5 시간 0%
- e_overtime m_eOverTime;
- public:
- bool IsOverTime(e_overtime e) const { return (e == m_eOverTime); }
- void SetOverTime(e_overtime e) { m_eOverTime = e; }
- private:
- int m_deposit_pulse;
- public:
- void UpdateDepositPulse();
- bool CanDeposit() const;
- private:
- void __OpenPrivateShop();
- public:
- struct AttackedLog
- {
- DWORD dwPID;
- DWORD dwAttackedTime;
- AttackedLog() : dwPID(0), dwAttackedTime(0)
- {
- }
- };
- AttackLog m_kAttackLog;
- AttackedLog m_AttackedLog;
- int m_speed_hack_count;
- private :
- std::string m_strNewName;
- public :
- const std::string GetNewName() const { return this->m_strNewName; }
- void SetNewName(const std::string name) { this->m_strNewName = name; }
- public :
- void GoHome();
- private :
- std::set<DWORD> m_known_guild;
- public :
- void SendGuildName(CGuild* pGuild);
- void SendGuildName(DWORD dwGuildID);
- private :
- DWORD m_dwLogOffInterval;
- public :
- DWORD GetLogOffInterval() const { return m_dwLogOffInterval; }
- public:
- bool UnEquipSpecialRideUniqueItem ();
- bool CanWarp () const;
- private:
- DWORD m_dwLastGoldDropTime;
- // public:
- // void StartHackShieldCheckCycle(int seconds);
- // void StopHackShieldCheckCycle();
- // bool GetHackShieldCheckMode() const { return m_HackShieldCheckMode; }
- // void SetHackShieldCheckMode(bool m) { m_HackShieldCheckMode = m; }
- // LPEVENT m_HackShieldCheckEvent;
- // private:
- // bool m_HackShieldCheckMode;
- public:
- void AutoRecoveryItemProcess (const EAffectTypes);
- public:
- void BuffOnAttr_AddBuffsFromItem(LPITEM pItem);
- void BuffOnAttr_RemoveBuffsFromItem(LPITEM pItem);
- private:
- void BuffOnAttr_ValueChange(BYTE bType, BYTE bOldValue, BYTE bNewValue);
- void BuffOnAttr_ClearAll();
- typedef std::map <BYTE, CBuffOnAttributes*> TMapBuffOnAttrs;
- TMapBuffOnAttrs m_map_buff_on_attrs;
- // 무적 : 원활한 테스트를 위하여.
- public:
- void SetArmada() { cannot_dead = true; }
- void ResetArmada() { cannot_dead = false; }
- private:
- bool cannot_dead;
- #ifdef __PET_SYSTEM__
- private:
- bool m_bIsPet;
- public:
- void SetPet() { m_bIsPet = true; }
- bool IsPet() { return m_bIsPet; }
- #endif
- //최종 데미지 보정.
- private:
- float m_fAttMul;
- float m_fDamMul;
- public:
- float GetAttMul() { return this->m_fAttMul; }
- void SetAttMul(float newAttMul) {this->m_fAttMul = newAttMul; }
- float GetDamMul() { return this->m_fDamMul; }
- void SetDamMul(float newDamMul) {this->m_fDamMul = newDamMul; }
- private:
- bool IsValidItemPosition(TItemPos Pos) const;
- //독일 선물 기능 패킷 임시 저장
- private:
- unsigned int itemAward_vnum;
- char itemAward_cmd[20];
- //bool itemAward_flag;
- public:
- unsigned int GetItemAward_vnum() { return itemAward_vnum; }
- char* GetItemAward_cmd() { return itemAward_cmd; }
- //bool GetItemAward_flag() { return itemAward_flag; }
- void SetItemAward_vnum(unsigned int vnum) { itemAward_vnum = vnum; }
- void SetItemAward_cmd(char* cmd) { strcpy(itemAward_cmd,cmd); }
- //void SetItemAward_flag(bool flag) { itemAward_flag = flag; }
- public:
- //용혼석
- // 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
- // affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
- void DragonSoul_Initialize();
- bool DragonSoul_IsQualified() const;
- void DragonSoul_GiveQualification();
- int DragonSoul_GetActiveDeck() const;
- bool DragonSoul_IsDeckActivated() const;
- bool DragonSoul_ActivateDeck(int deck_idx);
- void DragonSoul_DeactivateAll();
- // 반드시 ClearItem 전에 불러야 한다.
- // 왜냐하면....
- // 용혼석 하나 하나를 deactivate할 때마다 덱에 active인 용혼석이 있는지 확인하고,
- // active인 용혼석이 하나도 없다면, 캐릭터의 용혼석 affect와, 활성 상태를 제거한다.
- //
- // 하지만 ClearItem 시, 캐릭터가 착용하고 있는 모든 아이템을 unequip하는 바람에,
- // 용혼석 Affect가 제거되고, 결국 로그인 시, 용혼석이 활성화되지 않는다.
- // (Unequip할 때에는 로그아웃 상태인지, 아닌지 알 수 없다.)
- // 용혼석만 deactivate시키고 캐릭터의 용혼석 덱 활성 상태는 건드리지 않는다.
- void DragonSoul_CleanUp();
- // 용혼석 강화창
- public:
- bool DragonSoul_RefineWindow_Open(LPENTITY pEntity);
- bool DragonSoul_RefineWindow_Close();
- LPENTITY DragonSoul_RefineWindow_GetOpener() { return m_pointsInstant.m_pDragonSoulRefineWindowOpener; }
- bool DragonSoul_RefineWindow_CanRefine();
- private:
- // SyncPosition을 악용하여 타유저를 이상한 곳으로 보내는 핵 방어하기 위하여,
- // SyncPosition이 일어날 때를 기록.
- timeval m_tvLastSyncTime;
- int m_iSyncHackCount;
- public:
- void SetLastSyncTime(const timeval &tv) { memcpy(&m_tvLastSyncTime, &tv, sizeof(timeval)); }
- const timeval& GetLastSyncTime() { return m_tvLastSyncTime; }
- void SetSyncHackCount(int iCount) { m_iSyncHackCount = iCount;}
- int GetSyncHackCount() { return m_iSyncHackCount; }
- public:
- int LastDropTime;
- int CountDrops;
- void ClearPMCounter(void) { m_iPMCounter = 0; }
- void IncreasePMCounter(void) { m_iPMCounter++; }
- void SetLastPMPulse(void);
- int GetPMCounter(void) const { return m_iPMCounter; }
- int GetLastPMPulse(void) const { return m_iLastPMPulse; }
- protected:
- int m_iLastPMPulse;
- int m_iPMCounter;
- };
- ESex GET_SEX(LPCHARACTER ch);
- #endif
Das hier bin ich durchgegangen:
Bitte melden Sie sich an, um diesen Link zu sehen.
Sind anscheinend Fehler drinnen oder nicht komplett...