diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b3a74bf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.12) + +# Build mode debug/release? +set(CMAKE_BUILD_TYPE Debug) + +# Set C++ standard to C++11 +set(CMAKE_CXX_STANDARD 11) + +project("Metin2 Server") + +set(BINARY_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin) + +# Set build directory +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# Set the CMake module directory +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/Modules/") + +# Set environment variables +set(METIN2_OS_NAME ${CMAKE_SYSTEM}) +set(METIN2_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID}) +set(METIN2_CPU_TARGET ${CMAKE_SYSTEM_PROCESSOR}) + +# Git revision +include(FindGit) +find_package(Git) + +set(METIN2_REVISION "unknown") + +message(STATUS "Current revision is ${METIN2_REVISION}") + +# Set the global include directories +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_subdirectory(libgame) +add_subdirectory(liblua) +add_subdirectory(libpoly) +add_subdirectory(libsql) +add_subdirectory(libthecore) +add_subdirectory(game) +add_subdirectory(db) +add_subdirectory(quest) diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..836cc1e --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "WSL-GCC-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeExecutable": "cmake", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "linux_x64" ], + "wslPath": "${defaultWSLPath}" + } + ] +} \ No newline at end of file diff --git a/cmake/Modules/FindCryptoPP.cmake b/cmake/Modules/FindCryptoPP.cmake new file mode 100644 index 0000000..5ca01e4 --- /dev/null +++ b/cmake/Modules/FindCryptoPP.cmake @@ -0,0 +1,108 @@ +# Module for locating the Crypto++ encryption library. +# +# Customizable variables: +# CRYPTOPP_ROOT_DIR +# This variable points to the CryptoPP root directory. On Windows the +# library location typically will have to be provided explicitly using the +# -D command-line option. The directory should include the include/cryptopp, +# lib and/or bin sub-directories. +# +# Read-only variables: +# CRYPTOPP_FOUND +# Indicates whether the library has been found. +# +# CRYPTOPP_INCLUDE_DIRS +# Points to the CryptoPP include directory. +# +# CRYPTOPP_LIBRARIES +# Points to the CryptoPP libraries that should be passed to +# target_link_libararies. +# +# +# Copyright (c) 2012 Sergiu Dotenco +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +INCLUDE (FindPackageHandleStandardArgs) + +FIND_PATH (CRYPTOPP_ROOT_DIR + NAMES cryptopp/cryptlib.h include/cryptopp/cryptlib.h + PATHS ENV CRYPTOPPROOT + DOC "CryptoPP root directory") + +# Re-use the previous path: +FIND_PATH (CRYPTOPP_INCLUDE_DIR + NAMES cryptopp/cryptlib.h + HINTS ${CRYPTOPP_ROOT_DIR} + PATH_SUFFIXES include + DOC "CryptoPP include directory") + +FIND_LIBRARY (CRYPTOPP_LIBRARY_DEBUG + NAMES cryptlibd cryptoppd + HINTS ${CRYPTOPP_ROOT_DIR} + PATH_SUFFIXES lib + DOC "CryptoPP debug library") + +FIND_LIBRARY (CRYPTOPP_LIBRARY_RELEASE + NAMES cryptlib cryptopp + HINTS ${CRYPTOPP_ROOT_DIR} + PATH_SUFFIXES lib + DOC "CryptoPP release library") + +IF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE) + SET (CRYPTOPP_LIBRARY + optimized ${CRYPTOPP_LIBRARY_RELEASE} + debug ${CRYPTOPP_LIBRARY_DEBUG} CACHE DOC "CryptoPP library") +ELSEIF (CRYPTOPP_LIBRARY_RELEASE) + SET (CRYPTOPP_LIBRARY ${CRYPTOPP_LIBRARY_RELEASE} CACHE DOC + "CryptoPP library") +ENDIF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE) + +IF (CRYPTOPP_INCLUDE_DIR) + SET (_CRYPTOPP_VERSION_HEADER ${CRYPTOPP_INCLUDE_DIR}/cryptopp/config.h) + + IF (EXISTS ${_CRYPTOPP_VERSION_HEADER}) + FILE (STRINGS ${_CRYPTOPP_VERSION_HEADER} _CRYPTOPP_VERSION_TMP REGEX + "^#define CRYPTOPP_VERSION[ \t]+[0-9]+$") + + STRING (REGEX REPLACE + "^#define CRYPTOPP_VERSION[ \t]+([0-9]+)" "\\1" _CRYPTOPP_VERSION_TMP + ${_CRYPTOPP_VERSION_TMP}) + + STRING (REGEX REPLACE "([0-9]+)[0-9][0-9]" "\\1" CRYPTOPP_VERSION_MAJOR + ${_CRYPTOPP_VERSION_TMP}) + STRING (REGEX REPLACE "[0-9]([0-9])[0-9]" "\\1" CRYPTOPP_VERSION_MINOR + ${_CRYPTOPP_VERSION_TMP}) + STRING (REGEX REPLACE "[0-9][0-9]([0-9])" "\\1" CRYPTOPP_VERSION_PATCH + ${_CRYPTOPP_VERSION_TMP}) + + SET (CRYPTOPP_VERSION_COUNT 3) + SET (CRYPTOPP_VERSION + ${CRYPTOPP_VERSION_MAJOR}.${CRYPTOPP_VERSION_MINOR}.${CRYPTOPP_VERSION_PATCH}) + ENDIF (EXISTS ${_CRYPTOPP_VERSION_HEADER}) +ENDIF (CRYPTOPP_INCLUDE_DIR) + +SET (CRYPTOPP_INCLUDE_DIRS ${CRYPTOPP_INCLUDE_DIR}) +SET (CRYPTOPP_LIBRARIES ${CRYPTOPP_LIBRARY}) + +MARK_AS_ADVANCED (CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY CRYPTOPP_LIBRARY_DEBUG + CRYPTOPP_LIBRARY_RELEASE) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS (CryptoPP REQUIRED_VARS CRYPTOPP_ROOT_DIR + CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY VERSION_VAR CRYPTOPP_VERSION) \ No newline at end of file diff --git a/cmake/Modules/FindGit.cmake b/cmake/Modules/FindGit.cmake new file mode 100644 index 0000000..94d864d --- /dev/null +++ b/cmake/Modules/FindGit.cmake @@ -0,0 +1,176 @@ +################################################################################ +# +# Program: 3D Slicer +# +# Copyright (c) Kitware Inc. +# +# See COPYRIGHT.txt +# or http://www.slicer.org/copyright/copyright.txt for details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. +# and was partially funded by NIH grant 3P41RR013218-12S1 +# +################################################################################ + +# +# The module defines the following variables: +# GIT_EXECUTABLE - path to git command line client +# GIT_FOUND - true if the command line client was found +# GIT_VERSION_STRING - the version of git found (since CMake 2.8.8) +# +# If the command line client executable is found the macro +# GIT_WC_INFO( ) +# is defined to extract information of a git working copy at +# a given location. +# +# The macro defines the following variables: +# _WC_REVISION_HASH - Current SHA1 hash +# _WC_REVISION - Current SHA1 hash +# _WC_REVISION_NAME - Name associated with _WC_REVISION_HASH +# _WC_URL - output of command `git config --get remote.origin.url' +# _WC_ROOT - Same value as working copy URL +# _WC_LAST_CHANGED_DATE - date of last commit +# _WC_GITSVN - Set to false +# +# ... and also the following ones if it's a git-svn repository: +# _WC_GITSVN - Set to True if it is a +# _WC_INFO - output of command `git svn info' +# _WC_URL - url of the associated SVN repository +# _WC_ROOT - root url of the associated SVN repository +# _WC_REVISION - current SVN revision number +# _WC_LAST_CHANGED_AUTHOR - author of last commit +# _WC_LAST_CHANGED_DATE - date of last commit +# _WC_LAST_CHANGED_REV - revision of last commit +# _WC_LAST_CHANGED_LOG - last log of base revision +# +# Example usage: +# find_package(Git) +# if(GIT_FOUND) +# GIT_WC_INFO(${PROJECT_SOURCE_DIR} Project) +# message("Current revision is ${Project_WC_REVISION_HASH}") +# message("git found: ${GIT_EXECUTABLE}") +# endif() +# + +# Look for 'git' or 'eg' (easy git) +# +set(git_names git eg) + +# Prefer .cmd variants on Windows unless running in a Makefile +# in the MSYS shell. +# +if(WIN32) + if(NOT CMAKE_GENERATOR MATCHES "MSYS") + # Note: Due to a bug in 'git.cmd' preventing it from returning the exit code of 'git', + # we excluded it from the list of executables to search. + # See http://code.google.com/p/msysgit/issues/detail?id=428 + # TODO Check if 'git' exists, get the associated version, if the corresponding version + # is known to have a working version of 'git.cmd', use it. + set(git_names git eg.cmd eg) + endif() +endif() + +find_program(GIT_EXECUTABLE ${git_names} + PATHS + "C:/Program Files/Git/bin" + "C:/Program Files (x86)/Git/bin" + DOC "git command line client") +# XXX Comment to workaround https://gitlab.kitware.com/cmake/cmake/issues/15448 +# mark_as_advanced(GIT_EXECUTABLE) + +if(GIT_EXECUTABLE) + execute_process(COMMAND ${GIT_EXECUTABLE} --version + OUTPUT_VARIABLE git_version + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (git_version MATCHES "^git version [0-9]") + string(REPLACE "git version " "" GIT_VERSION_STRING "${git_version}") + endif() + unset(git_version) + + macro(GIT_WC_INFO dir prefix) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=7 HEAD + WORKING_DIRECTORY ${dir} + ERROR_VARIABLE GIT_error + OUTPUT_VARIABLE ${prefix}_WC_REVISION_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(${prefix}_WC_REVISION ${${prefix}_WC_REVISION_HASH}) + if(NOT ${GIT_error} EQUAL 0) + message(SEND_ERROR "Command \"${GIT_EXECUTBALE} rev-parse --verify -q --short=7 HEAD\" in directory ${dir} failed with output:\n${GIT_error}") + else(NOT ${GIT_error} EQUAL 0) + execute_process(COMMAND ${GIT_EXECUTABLE} name-rev ${${prefix}_WC_REVISION_HASH} + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE ${prefix}_WC_REVISION_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(NOT ${GIT_error} EQUAL 0) + + execute_process(COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE ${prefix}_WC_URL + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format="%ci" ${${prefix}_WC_REVISION_HASH} + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE ${prefix}_show_output + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX REPLACE "^([0-9][0-9][0-9][0-9]\\-[0-9][0-9]\\-[0-9][0-9]).*" + "\\1" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_show_output}") + + set(${prefix}_WC_GITSVN False) + + # Check if this git is likely to be a git-svn repository + execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp "^svn-remote" + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE git_config_output + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(NOT "${git_config_output}" STREQUAL "") + # In case git-svn is used, attempt to extract svn info + execute_process(COMMAND ${GIT_EXECUTABLE} svn info + WORKING_DIRECTORY ${dir} + TIMEOUT 3 + ERROR_VARIABLE git_svn_info_error + OUTPUT_VARIABLE ${prefix}_WC_INFO + RESULT_VARIABLE git_svn_info_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(${git_svn_info_result} EQUAL 0) + set(${prefix}_WC_GITSVN True) + string(REGEX REPLACE "^(.*\n)?URL: ([^\n]+).*" + "\\2" ${prefix}_WC_URL "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Revision: ([^\n]+).*" + "\\2" ${prefix}_WC_REVISION "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Repository Root: ([^\n]+).*" + "\\2" ${prefix}_WC_ROOT "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Author: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_AUTHOR "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Rev: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_REV "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Date: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_WC_INFO}") + endif(${git_svn_info_result} EQUAL 0) + endif(NOT "${git_config_output}" STREQUAL "") + + # If there is no 'remote.origin', default to "NA" value and print a warning message. + if(NOT ${prefix}_WC_URL) + message(WARNING "No remote origin set for git repository: ${dir}" ) + set( ${prefix}_WC_URL "NA" ) + else() + set(${prefix}_WC_ROOT ${${prefix}_WC_URL}) + endif() + + endmacro(GIT_WC_INFO) +endif(GIT_EXECUTABLE) + +# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if +# all listed variables are TRUE + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE) diff --git a/cmake/Modules/FindLZO.cmake b/cmake/Modules/FindLZO.cmake new file mode 100644 index 0000000..4d7ca52 --- /dev/null +++ b/cmake/Modules/FindLZO.cmake @@ -0,0 +1,45 @@ +# Find liblzo2 +# LZO_FOUND - system has the LZO library +# LZO_INCLUDE_DIR - the LZO include directory +# LZO_LIBRARIES - The libraries needed to use LZO + +if (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + # in cache already + SET(LZO_FOUND TRUE) +else (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + FIND_PATH(LZO_INCLUDE_DIR lzo/lzo1x.h + ${LZO_ROOT}/include/ + /usr/include/ + /usr/local/include/ + /sw/lib/ + /sw/local/lib/ + ) + + if(WIN32 AND MSVC) + else(WIN32 AND MSVC) + FIND_LIBRARY(LZO_LIBRARIES NAMES lzo2 + PATHS + ${LZO_ROOT}/lib + /usr/lib + /usr/local/lib + /sw/lib + /sw/local/lib + ) + endif(WIN32 AND MSVC) + + if (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + set(LZO_FOUND TRUE) + endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES) + + if (LZO_FOUND) + if (NOT LZO_FIND_QUIETLY) + message(STATUS "Found LZO: ${LZO_LIBRARIES}") + endif (NOT LZO_FIND_QUIETLY) + else (LZO_FOUND) + if (LZO_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find LZO") + endif (LZO_FIND_REQUIRED) + endif (LZO_FOUND) + + MARK_AS_ADVANCED(LZO_INCLUDE_DIR LZO_LIBRARIES) +endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES) \ No newline at end of file diff --git a/common/VnumHelper.h b/common/VnumHelper.h new file mode 100644 index 0000000..c025f9d --- /dev/null +++ b/common/VnumHelper.h @@ -0,0 +1,59 @@ +#ifndef __HEADER_VNUM_HELPER__ +#define __HEADER_VNUM_HELPER__ + +/** + ̹ ϰų ߰ , ҽ ĺ + ĺ(=VNum) ϵڵϴ Ǿ־ ſ µ + + δ ҽ  (Ȥ ) ְ ڴ ö ߰. + + * Ǵµ PCH ٲ ü ؾϴ + ϴ ʿ cppϿ include ؼ . + + * cpp ϸ ~ ũؾϴ ׳ common ־. (game, dbƮ ) + + @date 2011. 8. 29. +*/ + + +class CItemVnumHelper +{ +public: + /// DVD һ ȯ + static const bool IsPhoenix(DWORD vnum) { return 53001 == vnum; } // NOTE: һ ȯ 53001 mob-vnum 34001 Դϴ. + + /// 󸶴 ̺Ʈ ʽ´ ( 󸶴 ̺Ʈ Ư ̾ Ȱؼ ٰ ) + static const bool IsRamadanMoonRing(DWORD vnum) { return 71135 == vnum; } + + /// ҷ ( ʽ´ ) + static const bool IsHalloweenCandy(DWORD vnum) { return 71136 == vnum; } + + /// ũ ູ + static const bool IsHappinessRing(DWORD vnum) { return 71143 == vnum; } + + /// ߷Ÿ ҴƮ + static const bool IsLovePendant(DWORD vnum) { return 71145 == vnum; } +}; + +class CMobVnumHelper +{ +public: + /// DVD һ ȣ + static bool IsPhoenix(DWORD vnum) { return 34001 == vnum; } + static bool IsIcePhoenix(DWORD vnum) { return 34003 == vnum; } + /// PetSystem ϴ ΰ? + static bool IsPetUsingPetSystem(DWORD vnum) { return (IsPhoenix(vnum) || IsReindeerYoung(vnum)) || IsIcePhoenix(vnum); } + + /// 2011 ũ ̺Ʈ (Ʊ ) + static bool IsReindeerYoung(DWORD vnum) { return 34002 == vnum; } + + /// 󸶴 ̺Ʈ 渶(20119) .. ҷ ̺Ʈ 󸶴 渶 Ŭ( , 20219) + static bool IsRamadanBlackHorse(DWORD vnum) { return 20119 == vnum || 20219 == vnum || 22022 == vnum; } +}; + +class CVnumHelper +{ +}; + + +#endif //__HEADER_VNUM_HELPER__ \ No newline at end of file diff --git a/common/auction_table.h b/common/auction_table.h new file mode 100644 index 0000000..e0d4568 --- /dev/null +++ b/common/auction_table.h @@ -0,0 +1,335 @@ +#ifndef __INC_AUCTION_TABLES_H__ +#define __INC_AUCTION_TABLES_H__ + +#include "tables.h" + +typedef struct _base_auction +{ +public: + DWORD item_num; + TItemTable* item_proto; + int offer_price; + int price; + DWORD offer_id; + char shown_name[CHARACTER_NAME_MAX_LEN + 1]; + BYTE empire; + time_t expired_time; + + DWORD get_item_num () { return item_num; } + DWORD get_offer_id () { return offer_id; } + BYTE get_empire () { return empire; } + time_t get_expired_time () { return expired_time; } + bool is_expired () + { + return (time(NULL) > expired_time); + } + int get_price () { return offer_price; } +} TAuctionSimpleItemInfo; + +// auction . +// primary key (item_id) +typedef struct _auction : public _base_auction +{ +public: + DWORD item_id; + DWORD bidder_id; + + _auction (){} + _auction (DWORD _item_num, int _offer_price, int _price, DWORD _offer_id, + char* _shown_name, time_t _expired_time, DWORD _item_id, DWORD _bidder_id, BYTE _empire) + { + item_num = _item_num; + offer_price= _offer_price; + price = _price; + offer_id = _offer_id; + thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1); + expired_time = _expired_time; + item_id = _item_id; + bidder_id = _bidder_id; + empire = _empire; + } + + // ޼ҵ  auction  ϴ + // Ҵ. + // by rtsummit + DWORD get_item_id () { return item_id; } + DWORD get_bidder_id () { return bidder_id; } + int get_bid_price () { return offer_price; } + void set_bid_price (int new_price) + { + offer_price = new_price; + } + int get_impur_price () { return price; } + + const char* get_bidder_name () { return shown_name; } + void set_bidder_name (const char* new_bidder_name) + { + thecore_memcpy(shown_name, new_bidder_name, strlen(new_bidder_name) + 1); + } +} TAuctionItemInfo; + +// primary key (item_id) +typedef struct _sale : public _base_auction +{ + _sale (){} + + _sale (DWORD _item_num, int _offer_price, DWORD _offer_id, + char* _shown_name, DWORD _item_id, DWORD _wisher_id) + { + item_num = _item_num; + offer_price= _offer_price; + offer_id = _offer_id; + thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1); + item_id = _item_id; + wisher_id = _wisher_id; + } + + DWORD item_id; + DWORD wisher_id; + +} TSaleItemInfo; + +// wish ϴ . +// primary key (item_num, wisher_id) +typedef struct _wish : public _base_auction +{ + _wish (){} + + _wish (DWORD _item_num, int _offer_price, DWORD _offer_id, + char* _shown_name, time_t _expired_time, BYTE _empire) + { + item_num = _item_num; + offer_price= _offer_price; + offer_id = _offer_id; + thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1); + expired_time = _expired_time; + empire = _empire; + } +} TWishItemInfo; + +enum AuctionType {_AUCTION, _WISH_AUCTION, _MY_AUCTION, _MY_WISH_AUCTION, _AUCTION_MAX}; + +enum AuctionCmd {OPEN_AUCTION, OPEN_WISH_AUCTION, OPEN_MY_AUCTION, OPEN_MY_WISH_AUCTION, + AUCTION_BID, AUCTION_IMME_PUR, AUCTION_ENR_AUC, AUCTION_ENR_WISH, AUCTION_ENR_SALE, + AUCTION_GET_AUC, AUCTION_BUY_SOLD, + AUCTION_CANCEL_AUC, AUCTION_CANCEL_WISH, AUCTION_CANCEL_SALE, + AUCTION_DELETE_AUCTION_ITEM, AUCTION_DELETE_SALE_ITEM, + AUCTION_CHANGING_MONEY, + AUCTION_REBID, AUCTION_BID_CANCEL, +}; + +// ݵ FAIL տ, ;Ѵ. +// ֳ, <= AUCTION_FAIL ̷ CHECK Űŵ +// ݴ SUCCESS ڿ, ;Ѵ. ٵ ֱ Ϸ... + +enum AuctionResult { AUCTION_EXPIRED, AUCTION_NOT_EXPIRED, AUCTION_NOT_ENOUGH_MONEY, + AUCTION_SOLD, AUCTION_CANCEL, AUCTION_ALREADY_IN, AUCTION_NOT_IN, AUCTION_FAIL, AUCTION_SUCCESS }; + +enum AuctionSort { AUCTION_NO_ORDER, + AUCTION_ITEM_NAME_AC, AUCTION_ITEM_NAME_DC, + AUCTION_CATEGORY_AC, AUCTION_CATEGORY_DC, + AUCTION_TIME_AC, AUCTION_TIME_DC, + AUCTION_CHAR_NAME_AC, AUCTION_CHAR_NAME_DC, + AUCTION_PRICE_AC, AUCTION_PRICE_DC, +}; + +typedef struct command_get_auction_list +{ + AuctionCmd cmd; + DWORD start_idx; + BYTE size; +} TPacketGDGetAuctionList; + +typedef struct command_auction +{ + void enroll_product (DWORD _item_id, BYTE _empire, int _bidPrice, int _impurPrice) + { + cmd = AUCTION_ENR_AUC; + item = _item_id; + empire = _empire; + price1 = _bidPrice; + price2 = _impurPrice; + } + void enroll_sale (DWORD _item_id, DWORD _wisher_id, int _salePrice) + { + cmd = AUCTION_ENR_SALE; + item = _item_id; + price1 = _salePrice; + player_id = _wisher_id; + } + void enroll_wish (DWORD _item_num, BYTE _empire, int _wishPrice) + { + cmd = AUCTION_ENR_WISH; + item = _item_num; + empire = _empire; + price1 = _wishPrice; + } + void bid (DWORD _item_id, int _bidPrice) + { + cmd = AUCTION_BID; + item = _item_id; + price1 = _bidPrice; + } + void impur (DWORD _item_id) + { + cmd = AUCTION_IMME_PUR; + item = _item_id; + } + void get_auctioned_item (DWORD _item_id) + { + cmd = AUCTION_GET_AUC; + item = _item_id; + } + void buy_sold_item (DWORD _item_id) + { + cmd = AUCTION_BUY_SOLD; + item = _item_id; + } + void cancel_auction (DWORD _item_id) + { + cmd = AUCTION_CANCEL_AUC; + item = _item_id; + } + void cancel_wish (DWORD _item_num) + { + cmd = AUCTION_CANCEL_WISH; + item = _item_num; + } + void cancel_sale (DWORD _item_id) + { + cmd = AUCTION_CANCEL_SALE; + item = _item_id; + } + + void delete_auction_item (DWORD _item_id) + { + cmd = AUCTION_DELETE_AUCTION_ITEM; + item = _item_id; + } + + void delete_sale_item (DWORD _item_id) + { + cmd = AUCTION_DELETE_SALE_ITEM; + item = _item_id; + } + + void changing_money (int _money) + { + cmd = AUCTION_CHANGING_MONEY; + price1 = _money; + } + // bid cmd ٸ. + void rebid (DWORD _item_id, int _bidPrice) + { + cmd = AUCTION_REBID; + item = _item_id; + price1 = _bidPrice; + } + void bid_cancel (DWORD _item_id) + { + cmd = AUCTION_BID_CANCEL; + item = _item_id; + } + DWORD get_item () { return item; } + +protected: + AuctionCmd cmd; + DWORD player_id; + DWORD item; + BYTE empire; + int price1; + int price2; + +public: + AuctionCmd get_cmd() { return cmd; } + BYTE get_empire () { return empire; } +} TPacketGDCommnadAuction; + +typedef struct result_auction +{ + AuctionCmd cmd; + BYTE result; + DWORD target; +} TPacketDGResultAuction; + +// wrapper struct +typedef struct auction_enroll_product : public command_auction +{ + DWORD get_item_id() { return item; } + int get_bid_price() { return price1; } + int get_impur_price() { return price2; } +} AuctionEnrollProductInfo; + +typedef struct auction_enroll_sale : public command_auction +{ + DWORD get_item_id() { return item; } + DWORD get_wisher_id() { return player_id; } + int get_sale_price() { return price1; } +} AuctionEnrollSaleInfo; + +typedef struct auction_enroll_wish : public command_auction +{ + DWORD get_item_num() { return item; } + int get_wish_price() { return price1; } +} AuctionEnrollWishInfo; + +typedef struct auction_bid : public command_auction +{ + DWORD get_item_id() { return item; } + int get_bid_price() { return price1; } +} AuctionBidInfo; + +typedef struct auction_impur : public command_auction +{ + DWORD get_item_id() { return item; } +} AuctionImpurInfo; + + +//typedef struct get_auction_list +// +//bid +//{ +// item_id; +// bidder_id; +// price; +//} +//impur +//{ +// item_id; +// purchaser_id; +//} +//enroll_wish +//{ +// item_num; +// wisher_id; +// wish_price; +//} +//enroll_sale +//{ +// item_id; +// seller_id; +// sale_price; +//} +// +//return_packet +//{ +// isSuccess; +//} +// +// +//get_auction_simple_item_info_list +//{ +// auction_type; +// start_idx; +// size; +// conditions; öԲ غ. +//} +// +//get_auction_detail_item_info +//{ +// item_id; +//} + + +#endif + diff --git a/common/billing.h b/common/billing.h new file mode 100644 index 0000000..807c0cc --- /dev/null +++ b/common/billing.h @@ -0,0 +1,15 @@ +#ifndef __INC_METIN_II_COMMON_BILLING_H__ +#define __INC_METIN_II_COMMON_BILLING_H__ + +enum EBillingTypes +{ + BILLING_NONE, + BILLING_IP_FREE, + BILLING_FREE, + BILLING_IP_TIME, + BILLING_IP_DAY, + BILLING_TIME, + BILLING_DAY, +}; + +#endif diff --git a/common/building.h b/common/building.h new file mode 100644 index 0000000..d42af04 --- /dev/null +++ b/common/building.h @@ -0,0 +1,63 @@ +#ifndef __METIN_II_COMMON_BUILDING_H__ +#define __METIN_II_COMMON_BUILDING_H__ + +namespace building +{ + enum + { + OBJECT_MATERIAL_MAX_NUM = 5, + }; + + typedef struct SLand + { + DWORD dwID; + long lMapIndex; + long x, y; + long width, height; + DWORD dwGuildID; + BYTE bGuildLevelLimit; + DWORD dwPrice; + } TLand; + + typedef struct SObjectMaterial + { + DWORD dwItemVnum; + DWORD dwCount; + } TObjectMaterial; + + typedef struct SObjectProto + { + DWORD dwVnum; + DWORD dwPrice; + + TObjectMaterial kMaterials[OBJECT_MATERIAL_MAX_NUM]; + + DWORD dwUpgradeVnum; + DWORD dwUpgradeLimitTime; + long lLife; + long lRegion[4]; + + DWORD dwNPCVnum; + long lNPCX; + long lNPCY; + + DWORD dwGroupVnum; // ׷ ϳ Ǽ + DWORD dwDependOnGroupVnum; // ־ϴ ׷ + } TObjectProto; + + typedef struct SObject + { + DWORD dwID; + DWORD dwLandID; + DWORD dwVnum; + long lMapIndex; + long x, y; + + float xRot; + float yRot; + float zRot; + long lLife; + } TObject; +}; + +#endif diff --git a/common/cache.h b/common/cache.h new file mode 100644 index 0000000..e4bb759 --- /dev/null +++ b/common/cache.h @@ -0,0 +1,69 @@ +#ifndef __INC_COMMON_CACHE_H__ +#define __INC_COMMON_CACHE_H__ + +template class cache +{ + public: + cache() + : m_bNeedQuery(false), m_expireTime(600), m_lastUpdateTime(0) + { + m_lastFlushTime = time(0); + + memset( &m_data, 0, sizeof(m_data) ); + } + + T * Get(bool bUpdateTime = true) + { + if (bUpdateTime) + m_lastUpdateTime = time(0); + + return &m_data; + } + + void Put(T * pNew, bool bSkipQuery = false) + { + thecore_memcpy(&m_data, pNew, sizeof(T)); + m_lastUpdateTime = time(0); + + if (!bSkipQuery) + m_bNeedQuery = true; + } + + bool CheckFlushTimeout() + { + if (m_bNeedQuery && time(0) - m_lastFlushTime > m_expireTime) + return true; + + return false; + } + + bool CheckTimeout() + { + if (time(0) - m_lastUpdateTime > m_expireTime) + return true; + + return false; + } + + void Flush() + { + if (!m_bNeedQuery) + return; + + OnFlush(); + m_bNeedQuery = false; + m_lastFlushTime = time(0); + } + + virtual void OnFlush() = 0; + + + protected: + T m_data; + bool m_bNeedQuery; + time_t m_expireTime; + time_t m_lastUpdateTime; + time_t m_lastFlushTime; +}; + +#endif diff --git a/common/d3dtype.h b/common/d3dtype.h new file mode 100644 index 0000000..907b58c --- /dev/null +++ b/common/d3dtype.h @@ -0,0 +1,40 @@ +#ifndef __INC_METIN_II_D3DTYPE_H__ +#define __INC_METIN_II_D3DTYPE_H__ + +typedef struct D3DXVECTOR2 +{ + float x, y; +} D3DXVECTOR2, *LPD3DXVECTOR2; + +typedef struct D3DXVECTOR3 +{ + float x, y, z; +} D3DXVECTOR3, *LPD3DXVECTOR3; + +typedef struct D3DXVECTOR4 +{ + float x, y, z, w; +} D3DXVECTOR4, *LPD3DXVECTOR4; + +typedef struct D3DXQUATERNION +{ + float x, y, z, w; +} D3DXQUATERNION, *LPD3DXQUATERNION; + +typedef struct D3DXCOLOR +{ + float r, g, b, a; +} D3DXCOLOR, *LPD3DXCOLOR; + +typedef struct _D3DCOLORVALUE +{ + float r; + float g; + float b; + float a; +} D3DCOLORVALUE; + +typedef D3DXVECTOR3 D3DVECTOR; + +#endif + diff --git a/common/item_length.h b/common/item_length.h new file mode 100644 index 0000000..2f44961 --- /dev/null +++ b/common/item_length.h @@ -0,0 +1,371 @@ +#ifndef __INC_METIN2_ITEM_LENGTH_H__ +#define __INC_METIN2_ITEM_LENGTH_H__ + +enum EItemMisc +{ + ITEM_NAME_MAX_LEN = 24, + ITEM_VALUES_MAX_NUM = 6, + ITEM_SMALL_DESCR_MAX_LEN = 256, + ITEM_LIMIT_MAX_NUM = 2, + ITEM_APPLY_MAX_NUM = 3, + ITEM_SOCKET_MAX_NUM = 3, + ITEM_MAX_COUNT = 200, + ITEM_ATTRIBUTE_MAX_NUM = 7, + ITEM_ATTRIBUTE_MAX_LEVEL = 5, + ITEM_AWARD_WHY_MAX_LEN = 50, + + REFINE_MATERIAL_MAX_NUM = 5, + + ITEM_ELK_VNUM = 50026, +}; + +const BYTE ITEM_SOCKET_REMAIN_SEC = 0; +enum EItemValueIdice +{ + ITEM_VALUE_DRAGON_SOUL_POLL_OUT_BONUS_IDX = 0, + ITEM_VALUE_CHARGING_AMOUNT_IDX = 0, + ITEM_VALUE_SECONDARY_COIN_UNIT_IDX = 0, +}; +enum EItemDragonSoulSockets +{ + ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX = 2, + ITEM_SOCKET_CHARGING_AMOUNT_IDX = 2, +}; +// ̰ ģ ƴϾ? +// ߿ Ȯϸ ¼ -_-;;; +enum EItemUniqueSockets +{ + ITEM_SOCKET_UNIQUE_SAVE_TIME = ITEM_SOCKET_MAX_NUM - 2, + ITEM_SOCKET_UNIQUE_REMAIN_TIME = ITEM_SOCKET_MAX_NUM - 1 +}; + +enum EItemTypes +{ + ITEM_NONE, //0 + ITEM_WEAPON, //1// + ITEM_ARMOR, //2// + ITEM_USE, //3// + ITEM_AUTOUSE, //4 + ITEM_MATERIAL, //5 + ITEM_SPECIAL, //6 // + ITEM_TOOL, //7 + ITEM_LOTTERY, //8// + ITEM_ELK, //9// + ITEM_METIN, //10 + ITEM_CONTAINER, //11 + ITEM_FISH, //12// + ITEM_ROD, //13 + ITEM_RESOURCE, //14 + ITEM_CAMPFIRE, //15 + ITEM_UNIQUE, //16 + ITEM_SKILLBOOK, //17 + ITEM_QUEST, //18 + ITEM_POLYMORPH, //19 + ITEM_TREASURE_BOX, //20// + ITEM_TREASURE_KEY, //21// + ITEM_SKILLFORGET, //22 + ITEM_GIFTBOX, //23 + ITEM_PICK, //24 + ITEM_HAIR, //25//Ӹ + ITEM_TOTEM, //26// + ITEM_BLEND, //27//ɶ ϰ Ӽ ٴ ๰ + ITEM_COSTUME, //28//ڽ (2011 8 ߰ ڽ ýۿ ) + ITEM_DS, //29 //ȥ + ITEM_SPECIAL_DS, //30 // Ư ȥ (DS_SLOT ϴ UNIQUE ̶ ϸ ) + ITEM_EXTRACT, //31 ⵵. + ITEM_SECONDARY_COIN, //32 ?? ?? + ITEM_RING, //33 + ITEM_BELT, //34 Ʈ +}; + +enum EMetinSubTypes +{ + METIN_NORMAL, + METIN_GOLD, +}; + +enum EWeaponSubTypes +{ + WEAPON_SWORD, + WEAPON_DAGGER, + WEAPON_BOW, + WEAPON_TWO_HANDED, + WEAPON_BELL, + WEAPON_FAN, + WEAPON_ARROW, + WEAPON_MOUNT_SPEAR, + WEAPON_NUM_TYPES, +}; + +enum EArmorSubTypes +{ + ARMOR_BODY, + ARMOR_HEAD, + ARMOR_SHIELD, + ARMOR_WRIST, + ARMOR_FOOTS, + ARMOR_NECK, + ARMOR_EAR, + ARMOR_NUM_TYPES +}; + +enum ECostumeSubTypes +{ + COSTUME_BODY = ARMOR_BODY, // [߿!!] ECostumeSubTypes enum value EArmorSubTypes װͰ ƾ . + COSTUME_HAIR = ARMOR_HEAD, // ̴ ڽ ۿ ߰ Ӽ ̰ڴٴ û Ȱϱ . + COSTUME_NUM_TYPES, +}; + +enum EDragonSoulSubType +{ + DS_SLOT1, + DS_SLOT2, + DS_SLOT3, + DS_SLOT4, + DS_SLOT5, + DS_SLOT6, + DS_SLOT_MAX, +}; + +enum EDragonSoulGradeTypes +{ + DRAGON_SOUL_GRADE_NORMAL, + DRAGON_SOUL_GRADE_BRILLIANT, + DRAGON_SOUL_GRADE_RARE, + DRAGON_SOUL_GRADE_ANCIENT, + DRAGON_SOUL_GRADE_LEGENDARY, + DRAGON_SOUL_GRADE_MAX, + +}; + +enum EDragonSoulStepTypes +{ + DRAGON_SOUL_STEP_LOWEST, + DRAGON_SOUL_STEP_LOW, + DRAGON_SOUL_STEP_MID, + DRAGON_SOUL_STEP_HIGH, + DRAGON_SOUL_STEP_HIGHEST, + DRAGON_SOUL_STEP_MAX, +}; +#define DRAGON_SOUL_STRENGTH_MAX 7 + +enum EDSInventoryMaxNum +{ + DRAGON_SOUL_INVENTORY_MAX_NUM = DS_SLOT_MAX * DRAGON_SOUL_GRADE_MAX * DRAGON_SOUL_BOX_SIZE, +}; + +enum EFishSubTypes +{ + FISH_ALIVE, + FISH_DEAD, +}; + +enum EResourceSubTypes +{ + RESOURCE_FISHBONE, + RESOURCE_WATERSTONEPIECE, + RESOURCE_WATERSTONE, + RESOURCE_BLOOD_PEARL, + RESOURCE_BLUE_PEARL, + RESOURCE_WHITE_PEARL, + RESOURCE_BUCKET, + RESOURCE_CRYSTAL, + RESOURCE_GEM, + RESOURCE_STONE, + RESOURCE_METIN, + RESOURCE_ORE, +}; + +enum EUniqueSubTypes +{ + UNIQUE_NONE, + UNIQUE_BOOK, + UNIQUE_SPECIAL_RIDE, + UNIQUE_SPECIAL_MOUNT_RIDE, +}; + +enum EUseSubTypes +{ + USE_POTION, // 0 + USE_TALISMAN, + USE_TUNING, + USE_MOVE, + USE_TREASURE_BOX, + USE_MONEYBAG, + USE_BAIT, + USE_ABILITY_UP, + USE_AFFECT, + USE_CREATE_STONE, + USE_SPECIAL, // 10 + USE_POTION_NODELAY, + USE_CLEAR, + USE_INVISIBILITY, + USE_DETACHMENT, + USE_BUCKET, + USE_POTION_CONTINUE, + USE_CLEAN_SOCKET, + USE_CHANGE_ATTRIBUTE, + USE_ADD_ATTRIBUTE, + USE_ADD_ACCESSORY_SOCKET, // 20 + USE_PUT_INTO_ACCESSORY_SOCKET, + USE_ADD_ATTRIBUTE2, + USE_RECIPE, + USE_CHANGE_ATTRIBUTE2, + USE_BIND, + USE_UNBIND, + USE_TIME_CHARGE_PER, + USE_TIME_CHARGE_FIX, // 28 + USE_PUT_INTO_BELT_SOCKET, // 29 Ʈ Ͽ ִ + USE_PUT_INTO_RING_SOCKET, // 30 Ͽ ִ (ũ , ߰ ) +}; + +enum EExtractSubTypes +{ + EXTRACT_DRAGON_SOUL, + EXTRACT_DRAGON_HEART, +}; + +enum EAutoUseSubTypes +{ + AUTOUSE_POTION, + AUTOUSE_ABILITY_UP, + AUTOUSE_BOMB, + AUTOUSE_GOLD, + AUTOUSE_MONEYBAG, + AUTOUSE_TREASURE_BOX +}; + +enum EMaterialSubTypes +{ + MATERIAL_LEATHER, + MATERIAL_BLOOD, + MATERIAL_ROOT, + MATERIAL_NEEDLE, + MATERIAL_JEWEL, + MATERIAL_DS_REFINE_NORMAL, + MATERIAL_DS_REFINE_BLESSED, + MATERIAL_DS_REFINE_HOLLY, +}; + +enum ESpecialSubTypes +{ + SPECIAL_MAP, + SPECIAL_KEY, + SPECIAL_DOC, + SPECIAL_SPIRIT, +}; + +enum EToolSubTypes +{ + TOOL_FISHING_ROD +}; + +enum ELotterySubTypes +{ + LOTTERY_TICKET, + LOTTERY_INSTANT +}; + +enum EItemFlag +{ + ITEM_FLAG_REFINEABLE = (1 << 0), + ITEM_FLAG_SAVE = (1 << 1), + ITEM_FLAG_STACKABLE = (1 << 2), // ĥ + ITEM_FLAG_COUNT_PER_1GOLD = (1 << 3), + ITEM_FLAG_SLOW_QUERY = (1 << 4), + ITEM_FLAG_UNUSED01 = (1 << 5), // UNUSED + ITEM_FLAG_UNIQUE = (1 << 6), + ITEM_FLAG_MAKECOUNT = (1 << 7), + ITEM_FLAG_IRREMOVABLE = (1 << 8), + ITEM_FLAG_CONFIRM_WHEN_USE = (1 << 9), + ITEM_FLAG_QUEST_USE = (1 << 10), + ITEM_FLAG_QUEST_USE_MULTIPLE = (1 << 11), + ITEM_FLAG_QUEST_GIVE = (1 << 12), + ITEM_FLAG_LOG = (1 << 13), + ITEM_FLAG_APPLICABLE = (1 << 14), +}; + +enum EItemAntiFlag +{ + ITEM_ANTIFLAG_FEMALE = (1 << 0), // Ұ + ITEM_ANTIFLAG_MALE = (1 << 1), // Ұ + ITEM_ANTIFLAG_WARRIOR = (1 << 2), // Ұ + ITEM_ANTIFLAG_ASSASSIN = (1 << 3), // ڰ Ұ + ITEM_ANTIFLAG_SURA = (1 << 4), // Ұ + ITEM_ANTIFLAG_SHAMAN = (1 << 5), // Ұ + ITEM_ANTIFLAG_GET = (1 << 6), // + ITEM_ANTIFLAG_DROP = (1 << 7), // + ITEM_ANTIFLAG_SELL = (1 << 8), // + ITEM_ANTIFLAG_EMPIRE_A = (1 << 9), // A Ұ + ITEM_ANTIFLAG_EMPIRE_B = (1 << 10), // B Ұ + ITEM_ANTIFLAG_EMPIRE_C = (1 << 11), // C Ұ + ITEM_ANTIFLAG_SAVE = (1 << 12), // + ITEM_ANTIFLAG_GIVE = (1 << 13), // ŷ Ұ + ITEM_ANTIFLAG_PKDROP = (1 << 14), // PK + ITEM_ANTIFLAG_STACK = (1 << 15), // ĥ + ITEM_ANTIFLAG_MYSHOP = (1 << 16), // ø + ITEM_ANTIFLAG_SAFEBOX = (1 << 17), // â +}; + +enum EItemWearableFlag +{ + WEARABLE_BODY = (1 << 0), + WEARABLE_HEAD = (1 << 1), + WEARABLE_FOOTS = (1 << 2), + WEARABLE_WRIST = (1 << 3), + WEARABLE_WEAPON = (1 << 4), + WEARABLE_NECK = (1 << 5), + WEARABLE_EAR = (1 << 6), + WEARABLE_UNIQUE = (1 << 7), + WEARABLE_SHIELD = (1 << 8), + WEARABLE_ARROW = (1 << 9), + WEARABLE_HAIR = (1 << 10), + WEARABLE_ABILITY = (1 << 11), + WEARABLE_COSTUME_BODY = (1 << 12), +}; + +enum ELimitTypes +{ + LIMIT_NONE, + + LIMIT_LEVEL, + LIMIT_STR, + LIMIT_DEX, + LIMIT_INT, + LIMIT_CON, + LIMIT_PCBANG, + + /// ο ǽð ð (socket0 Ҹ ð : unix_timestamp Ÿ) + LIMIT_REAL_TIME, + + /// ó (Ȥ ) Ÿ Ÿ̸ + /// socket0 밡ɽð(ʴ, 0̸ limit value ) ִٰ + /// socket1 Ƚ socket0 unix_timestamp Ÿ Ҹð . + LIMIT_REAL_TIME_START_FIRST_USE, + + /// ð Ǵ + /// socket0 ð ʴ . ( ش 0̸ limit value socket0 ) + LIMIT_TIMER_BASED_ON_WEAR, + + LIMIT_MAX_NUM +}; + +enum EAttrAddonTypes +{ + ATTR_ADDON_NONE, + // positive values are reserved for set + ATTR_DAMAGE_ADDON = -1, +}; + +enum ERefineType +{ + REFINE_TYPE_NORMAL, + REFINE_TYPE_NOT_USED1, + REFINE_TYPE_SCROLL, + REFINE_TYPE_HYUNIRON, + REFINE_TYPE_MONEY_ONLY, + REFINE_TYPE_MUSIN, + REFINE_TYPE_BDRAGON, +}; + +#endif diff --git a/common/length.h b/common/length.h new file mode 100644 index 0000000..90782cc --- /dev/null +++ b/common/length.h @@ -0,0 +1,788 @@ +#ifndef __INC_METIN_II_LENGTH_H__ +#define __INC_METIN_II_LENGTH_H__ + +#define WORD_MAX 0xffff +enum EMisc +{ + MAX_HOST_LENGTH = 15, + IP_ADDRESS_LENGTH = 15, + LOGIN_MAX_LEN = 30, + PASSWD_MAX_LEN = 16, + PLAYER_PER_ACCOUNT = 4, + ACCOUNT_STATUS_MAX_LEN = 8, + CHARACTER_NAME_MAX_LEN = 24, + SHOP_SIGN_MAX_LEN = 32, + INVENTORY_MAX_NUM = 90, + ABILITY_MAX_NUM = 50, + EMPIRE_MAX_NUM = 4, + BANWORD_MAX_LEN = 24, + SMS_MAX_LEN = 80, + MOBILE_MAX_LEN = 32, + SOCIAL_ID_MAX_LEN = 18, + + GUILD_NAME_MAX_LEN = 12, + + SHOP_HOST_ITEM_MAX_NUM = 40, /* ȣƮ ִ */ + SHOP_GUEST_ITEM_MAX_NUM = 18, /* ԽƮ ִ */ + + SHOP_PRICELIST_MAX_NUM = 40, ///< λ Ʈ ִ + + CHAT_MAX_LEN = 512, + + QUICKSLOT_MAX_NUM = 36, + + JOURNAL_MAX_NUM = 2, + + QUERY_MAX_LEN = 8192, + + FILE_MAX_LEN = 128, + + PLAYER_EXP_TABLE_MAX = 120, + PLAYER_MAX_LEVEL_CONST = 120, + + GUILD_MAX_LEVEL = 20, + MOB_MAX_LEVEL = 100, + + ATTRIBUTE_MAX_VALUE = 20, + CHARACTER_PATH_MAX_NUM = 64, + SKILL_MAX_NUM = 255, + SKILLBOOK_DELAY_MIN = 64800, + SKILLBOOK_DELAY_MAX = 108000, + SKILL_MAX_LEVEL = 40, + + APPLY_NAME_MAX_LEN = 32, + EVENT_FLAG_NAME_MAX_LEN = 32, + + MOB_SKILL_MAX_NUM = 5, + + POINT_MAX_NUM = 255, + DRAGON_SOUL_BOX_SIZE = 32, + DRAGON_SOUL_BOX_COLUMN_NUM = 8, + DRAGON_SOUL_BOX_ROW_NUM = DRAGON_SOUL_BOX_SIZE / DRAGON_SOUL_BOX_COLUMN_NUM, + DRAGON_SOUL_REFINE_GRID_SIZE = 15, + MAX_AMOUNT_OF_MALL_BONUS = 20, + + WEAR_MAX_NUM = 32, + + //LIMIT_GOLD + GOLD_MAX = 2000000000, + + MAX_PASSPOD = 8 , + + + //END_LIMIT_GOLD + + OPENID_AUTHKEY_LEN = 32, + + SHOP_TAB_NAME_MAX = 32, + SHOP_TAB_COUNT_MAX = 3, + + BELT_INVENTORY_SLOT_WIDTH = 4, + BELT_INVENTORY_SLOT_HEIGHT= 4, + + BELT_INVENTORY_SLOT_COUNT = BELT_INVENTORY_SLOT_WIDTH * BELT_INVENTORY_SLOT_HEIGHT, + + +/** + **** Ҵ (DB Item Position) **** + +------------------------------------------------------+ 0 + | ij ⺻ κ丮 (45ĭ * 2) 90ĭ | + +------------------------------------------------------+ 90 = INVENTORY_MAX_NUM(90) + | ij â ( ) 32ĭ | + +------------------------------------------------------+ 122 = INVENTORY_MAX_NUM(90) + WEAR_MAX_NUM(32) + | ȥ â ( ȥ) 12ĭ | + +------------------------------------------------------+ 134 = 122 + DS_SLOT_MAX(6) * DRAGON_SOUL_DECK_MAX_NUM(2) + | ȥ â ( ̻) 18ĭ | + +------------------------------------------------------+ 152 = 134 + DS_SLOT_MAX(6) * DRAGON_SOUL_DECK_RESERVED_MAX_NUM(3) + | Ʈ κ丮 (Ʈ ÿ Ʈ Ȱ)| + +------------------------------------------------------+ 168 = 152 + BELT_INVENTORY_SLOT_COUNT(16) = INVENTORY_AND_EQUIP_CELL_MAX + | ̻ | + +------------------------------------------------------+ ?? +*/ +}; + +enum EMatrixCard +{ + MATRIX_CODE_MAX_LEN = 192, + MATRIX_ANSWER_MAX_LEN = 8, +}; + +enum EWearPositions +{ + WEAR_BODY, // 0 + WEAR_HEAD, // 1 + WEAR_FOOTS, // 2 + WEAR_WRIST, // 3 + WEAR_WEAPON, // 4 + WEAR_NECK, // 5 + WEAR_EAR, // 6 + WEAR_UNIQUE1, // 7 + WEAR_UNIQUE2, // 8 + WEAR_ARROW, // 9 + WEAR_SHIELD, // 10 + WEAR_ABILITY1, // 11 + WEAR_ABILITY2, // 12 + WEAR_ABILITY3, // 13 + WEAR_ABILITY4, // 14 + WEAR_ABILITY5, // 15 + WEAR_ABILITY6, // 16 + WEAR_ABILITY7, // 17 + WEAR_ABILITY8, // 18 + WEAR_COSTUME_BODY, // 19 + WEAR_COSTUME_HAIR, // 20 + + WEAR_RING1, // 21 : ű 1 () + WEAR_RING2, // 22 : ű 2 () + + WEAR_BELT, // 23 : ű Ʈ + + WEAR_MAX = 32 // +}; + +enum EDragonSoulDeckType +{ + DRAGON_SOUL_DECK_0, + DRAGON_SOUL_DECK_1, + DRAGON_SOUL_DECK_MAX_NUM = 2, + + DRAGON_SOUL_DECK_RESERVED_MAX_NUM = 3, // NOTE: ߿! , 3 з . DS DECK ø ݵ ŭ RESERVED ؾ ! +}; + +enum ESex +{ + SEX_MALE, + SEX_FEMALE +}; + +enum EDirection +{ + DIR_NORTH, + DIR_NORTHEAST, + DIR_EAST, + DIR_SOUTHEAST, + DIR_SOUTH, + DIR_SOUTHWEST, + DIR_WEST, + DIR_NORTHWEST, + DIR_MAX_NUM +}; + +#define ABILITY_MAX_LEVEL 10 /* ִ */ + +enum EAbilityDifficulty +{ + DIFFICULTY_EASY, + DIFFICULTY_NORMAL, + DIFFICULTY_HARD, + DIFFICULTY_VERY_HARD, + DIFFICULTY_NUM_TYPES +}; + +enum EAbilityCategory +{ + CATEGORY_PHYSICAL, /* ü Ƽ */ + CATEGORY_MENTAL, /* Ƽ */ + CATEGORY_ATTRIBUTE, /* ɷ Ƽ */ + CATEGORY_NUM_TYPES +}; + +enum EJobs +{ + JOB_WARRIOR, + JOB_ASSASSIN, + JOB_SURA, + JOB_SHAMAN, + JOB_MAX_NUM +}; + +enum ESkillGroups +{ + SKILL_GROUP_MAX_NUM = 2, +}; + +enum ERaceFlags +{ + RACE_FLAG_ANIMAL = (1 << 0), + RACE_FLAG_UNDEAD = (1 << 1), + RACE_FLAG_DEVIL = (1 << 2), + RACE_FLAG_HUMAN = (1 << 3), + RACE_FLAG_ORC = (1 << 4), + RACE_FLAG_MILGYO = (1 << 5), + RACE_FLAG_INSECT = (1 << 6), + RACE_FLAG_FIRE = (1 << 7), + RACE_FLAG_ICE = (1 << 8), + RACE_FLAG_DESERT = (1 << 9), + RACE_FLAG_TREE = (1 << 10), + RACE_FLAG_ATT_ELEC = (1 << 11), + RACE_FLAG_ATT_FIRE = (1 << 12), + RACE_FLAG_ATT_ICE = (1 << 13), + RACE_FLAG_ATT_WIND = (1 << 14), + RACE_FLAG_ATT_EARTH = (1 << 15), + RACE_FLAG_ATT_DARK = (1 << 16), +}; + +enum ELoads +{ + LOAD_NONE, + LOAD_LIGHT, + LOAD_NORMAL, + LOAD_HEAVY, + LOAD_MASSIVE +}; + +enum +{ + QUICKSLOT_TYPE_NONE, + QUICKSLOT_TYPE_ITEM, + QUICKSLOT_TYPE_SKILL, + QUICKSLOT_TYPE_COMMAND, + QUICKSLOT_TYPE_MAX_NUM, +}; + +enum EParts +{ + PART_MAIN, + PART_WEAPON, + PART_HEAD, + PART_HAIR, + + PART_MAX_NUM, + PART_WEAPON_SUB, +}; + +enum EChatType +{ + CHAT_TYPE_TALKING, /* ׳ ä */ + CHAT_TYPE_INFO, /* ( , ġ . ) */ + CHAT_TYPE_NOTICE, /* */ + CHAT_TYPE_PARTY, /* Ƽ */ + CHAT_TYPE_GUILD, /* 帻 */ + CHAT_TYPE_COMMAND, /* Ϲ */ + CHAT_TYPE_SHOUT, /* ġ */ + CHAT_TYPE_WHISPER, + CHAT_TYPE_BIG_NOTICE, + CHAT_TYPE_MONARCH_NOTICE, + CHAT_TYPE_MAX_NUM +}; + +enum EWhisperType +{ + WHISPER_TYPE_NORMAL = 0, + WHISPER_TYPE_NOT_EXIST = 1, + WHISPER_TYPE_TARGET_BLOCKED = 2, + WHISPER_TYPE_SENDER_BLOCKED = 3, + WHISPER_TYPE_ERROR = 4, + WHISPER_TYPE_GM = 5, + WHISPER_TYPE_SYSTEM = 0xFF +}; + +enum ECharacterPosition +{ + POSITION_GENERAL, + POSITION_BATTLE, + POSITION_DYING, + POSITION_SITTING_CHAIR, + POSITION_SITTING_GROUND, + POSITION_INTRO, + POSITION_MAX_NUM +}; + +enum EGMLevels +{ + GM_PLAYER, + GM_LOW_WIZARD, + GM_WIZARD, + GM_HIGH_WIZARD, + GM_GOD, + GM_IMPLEMENTOR +}; + +enum EMobRank +{ + MOB_RANK_PAWN, + MOB_RANK_S_PAWN, + MOB_RANK_KNIGHT, + MOB_RANK_S_KNIGHT, + MOB_RANK_BOSS, + MOB_RANK_KING, + MOB_RANK_MAX_NUM +}; + +enum ECharType +{ + CHAR_TYPE_MONSTER, + CHAR_TYPE_NPC, + CHAR_TYPE_STONE, + CHAR_TYPE_WARP, + CHAR_TYPE_DOOR, + CHAR_TYPE_BUILDING, + CHAR_TYPE_PC, + CHAR_TYPE_POLYMORPH_PC, + CHAR_TYPE_HORSE, + CHAR_TYPE_GOTO +}; + +enum EBattleType +{ + BATTLE_TYPE_MELEE, + BATTLE_TYPE_RANGE, + BATTLE_TYPE_MAGIC, + BATTLE_TYPE_SPECIAL, + BATTLE_TYPE_POWER, + BATTLE_TYPE_TANKER, + BATTLE_TYPE_SUPER_POWER, + BATTLE_TYPE_SUPER_TANKER, + BATTLE_TYPE_MAX_NUM +}; + +enum EApplyTypes +{ + APPLY_NONE, // 0 + APPLY_MAX_HP, // 1 + APPLY_MAX_SP, // 2 + APPLY_CON, // 3 + APPLY_INT, // 4 + APPLY_STR, // 5 + APPLY_DEX, // 6 + APPLY_ATT_SPEED, // 7 + APPLY_MOV_SPEED, // 8 + APPLY_CAST_SPEED, // 9 + APPLY_HP_REGEN, // 10 + APPLY_SP_REGEN, // 11 + APPLY_POISON_PCT, // 12 + APPLY_STUN_PCT, // 13 + APPLY_SLOW_PCT, // 14 + APPLY_CRITICAL_PCT, // 15 + APPLY_PENETRATE_PCT, // 16 + APPLY_ATTBONUS_HUMAN, // 17 + APPLY_ATTBONUS_ANIMAL, // 18 + APPLY_ATTBONUS_ORC, // 19 + APPLY_ATTBONUS_MILGYO, // 20 + APPLY_ATTBONUS_UNDEAD, // 21 + APPLY_ATTBONUS_DEVIL, // 22 + APPLY_STEAL_HP, // 23 + APPLY_STEAL_SP, // 24 + APPLY_MANA_BURN_PCT, // 25 + APPLY_DAMAGE_SP_RECOVER, // 26 + APPLY_BLOCK, // 27 + APPLY_DODGE, // 28 + APPLY_RESIST_SWORD, // 29 + APPLY_RESIST_TWOHAND, // 30 + APPLY_RESIST_DAGGER, // 31 + APPLY_RESIST_BELL, // 32 + APPLY_RESIST_FAN, // 33 + APPLY_RESIST_BOW, // 34 + APPLY_RESIST_FIRE, // 35 + APPLY_RESIST_ELEC, // 36 + APPLY_RESIST_MAGIC, // 37 + APPLY_RESIST_WIND, // 38 + APPLY_REFLECT_MELEE, // 39 + APPLY_REFLECT_CURSE, // 40 + APPLY_POISON_REDUCE, // 41 + APPLY_KILL_SP_RECOVER, // 42 + APPLY_EXP_DOUBLE_BONUS, // 43 + APPLY_GOLD_DOUBLE_BONUS, // 44 + APPLY_ITEM_DROP_BONUS, // 45 + APPLY_POTION_BONUS, // 46 + APPLY_KILL_HP_RECOVER, // 47 + APPLY_IMMUNE_STUN, // 48 + APPLY_IMMUNE_SLOW, // 49 + APPLY_IMMUNE_FALL, // 50 + APPLY_SKILL, // 51 + APPLY_BOW_DISTANCE, // 52 + APPLY_ATT_GRADE_BONUS, // 53 + APPLY_DEF_GRADE_BONUS, // 54 + APPLY_MAGIC_ATT_GRADE, // 55 + APPLY_MAGIC_DEF_GRADE, // 56 + APPLY_CURSE_PCT, // 57 + APPLY_MAX_STAMINA, // 58 + APPLY_ATTBONUS_WARRIOR, // 59 + APPLY_ATTBONUS_ASSASSIN, // 60 + APPLY_ATTBONUS_SURA, // 61 + APPLY_ATTBONUS_SHAMAN, // 62 + APPLY_ATTBONUS_MONSTER, // 63 + APPLY_MALL_ATTBONUS, // 64 ݷ +x% + APPLY_MALL_DEFBONUS, // 65 +x% + APPLY_MALL_EXPBONUS, // 66 ġ +x% + APPLY_MALL_ITEMBONUS, // 67 x/10 + APPLY_MALL_GOLDBONUS, // 68 x/10 + APPLY_MAX_HP_PCT, // 69 ִ +x% + APPLY_MAX_SP_PCT, // 70 ִ ŷ +x% + APPLY_SKILL_DAMAGE_BONUS, // 71 ų * (100+x)% + APPLY_NORMAL_HIT_DAMAGE_BONUS, // 72 Ÿ * (100+x)% + APPLY_SKILL_DEFEND_BONUS, // 73 ų * (100-x)% + APPLY_NORMAL_HIT_DEFEND_BONUS, // 74 Ÿ * (100-x)% + APPLY_PC_BANG_EXP_BONUS, // 75 PC EXP ʽ + APPLY_PC_BANG_DROP_BONUS, // 76 PC ʽ + + APPLY_EXTRACT_HP_PCT, // 77 HP Ҹ + + APPLY_RESIST_WARRIOR, // 78 翡 + APPLY_RESIST_ASSASSIN, // 79 ڰ + APPLY_RESIST_SURA, // 80 󿡰 + APPLY_RESIST_SHAMAN, // 81 翡 + APPLY_ENERGY, // 82 + APPLY_DEF_GRADE, // 83 . DEF_GRADE_BONUS Ŭ󿡼 ι ǵ (...) ִ. + APPLY_COSTUME_ATTR_BONUS, // 84 ڽƬ ۿ Ӽġ ʽ + APPLY_MAGIC_ATTBONUS_PER, // 85 ݷ +x% + APPLY_MELEE_MAGIC_ATTBONUS_PER, // 86 + и ݷ +x% + + APPLY_RESIST_ICE, // 87 ñ + APPLY_RESIST_EARTH, // 88 + APPLY_RESIST_DARK, // 89 + + APPLY_ANTI_CRITICAL_PCT, //90 ũƼ + APPLY_ANTI_PENETRATE_PCT, //91 Ÿ + + + MAX_APPLY_NUM, // +}; + +enum EOnClickEvents +{ + ON_CLICK_NONE, + ON_CLICK_SHOP, + ON_CLICK_TALK, + ON_CLICK_MAX_NUM +}; + +enum EOnIdleEvents +{ + ON_IDLE_NONE, + ON_IDLE_GENERAL, + ON_IDLE_MAX_NUM +}; + +enum EWindows +{ + RESERVED_WINDOW, + INVENTORY, + EQUIPMENT, + SAFEBOX, + MALL, + DRAGON_SOUL_INVENTORY, + BELT_INVENTORY, +#ifdef __AUCTION__ + AUCTION, +#endif + GROUND +}; + +enum EMobSizes +{ + MOBSIZE_RESERVED, + MOBSIZE_SMALL, + MOBSIZE_MEDIUM, + MOBSIZE_BIG +}; + +enum EAIFlags +{ + AIFLAG_AGGRESSIVE = (1 << 0), + AIFLAG_NOMOVE = (1 << 1), + AIFLAG_COWARD = (1 << 2), + AIFLAG_NOATTACKSHINSU = (1 << 3), + AIFLAG_NOATTACKJINNO = (1 << 4), + AIFLAG_NOATTACKCHUNJO = (1 << 5), + AIFLAG_ATTACKMOB = (1 << 6 ), + AIFLAG_BERSERK = (1 << 7), + AIFLAG_STONESKIN = (1 << 8), + AIFLAG_GODSPEED = (1 << 9), + AIFLAG_DEATHBLOW = (1 << 10), + AIFLAG_REVIVE = (1 << 11), +}; + +enum EMobStatType +{ + MOB_STATTYPE_POWER, + MOB_STATTYPE_TANKER, + MOB_STATTYPE_SUPER_POWER, + MOB_STATTYPE_SUPER_TANKER, + MOB_STATTYPE_RANGE, + MOB_STATTYPE_MAGIC, + MOB_STATTYPE_MAX_NUM +}; + +enum EImmuneFlags +{ + IMMUNE_STUN = (1 << 0), + IMMUNE_SLOW = (1 << 1), + IMMUNE_FALL = (1 << 2), + IMMUNE_CURSE = (1 << 3), + IMMUNE_POISON = (1 << 4), + IMMUNE_TERROR = (1 << 5), + IMMUNE_REFLECT = (1 << 6), +}; + +enum EMobEnchants +{ + MOB_ENCHANT_CURSE, + MOB_ENCHANT_SLOW, + MOB_ENCHANT_POISON, + MOB_ENCHANT_STUN, + MOB_ENCHANT_CRITICAL, + MOB_ENCHANT_PENETRATE, + MOB_ENCHANTS_MAX_NUM +}; + +enum EMobResists +{ + MOB_RESIST_SWORD, + MOB_RESIST_TWOHAND, + MOB_RESIST_DAGGER, + MOB_RESIST_BELL, + MOB_RESIST_FAN, + MOB_RESIST_BOW, + MOB_RESIST_FIRE, + MOB_RESIST_ELECT, + MOB_RESIST_MAGIC, + MOB_RESIST_WIND, + MOB_RESIST_POISON, + MOB_RESISTS_MAX_NUM +}; + +enum +{ + SKILL_ATTR_TYPE_NORMAL = 1, + SKILL_ATTR_TYPE_MELEE, + SKILL_ATTR_TYPE_RANGE, + SKILL_ATTR_TYPE_MAGIC + /* + SKILL_ATTR_TYPE_FIRE, + SKILL_ATTR_TYPE_ICE, + SKILL_ATTR_TYPE_ELEC, + SKILL_ATTR_TYPE_DARK, + */ +}; + +enum +{ + SKILL_NORMAL, + SKILL_MASTER, + SKILL_GRAND_MASTER, + SKILL_PERFECT_MASTER, +}; + +enum EGuildWarType +{ + GUILD_WAR_TYPE_FIELD, + GUILD_WAR_TYPE_BATTLE, + GUILD_WAR_TYPE_FLAG, + GUILD_WAR_TYPE_MAX_NUM +}; + +enum EGuildWarState +{ + GUILD_WAR_NONE, + GUILD_WAR_SEND_DECLARE, + GUILD_WAR_REFUSE, + GUILD_WAR_RECV_DECLARE, + GUILD_WAR_WAIT_START, + GUILD_WAR_CANCEL, + GUILD_WAR_ON_WAR, + GUILD_WAR_END, + GUILD_WAR_OVER, + GUILD_WAR_RESERVE, + + GUILD_WAR_DURATION = 30*60, // 1ð + GUILD_WAR_WIN_POINT = 1000, + GUILD_WAR_LADDER_HALF_PENALTY_TIME = 12*60*60, +}; + +enum EAttributeSet +{ + ATTRIBUTE_SET_WEAPON, + ATTRIBUTE_SET_BODY, + ATTRIBUTE_SET_WRIST, + ATTRIBUTE_SET_FOOTS, + ATTRIBUTE_SET_NECK, + ATTRIBUTE_SET_HEAD, + ATTRIBUTE_SET_SHIELD, + ATTRIBUTE_SET_EAR, + ATTRIBUTE_SET_MAX_NUM +}; + +enum EPrivType +{ + PRIV_NONE, + PRIV_ITEM_DROP, + PRIV_GOLD_DROP, + PRIV_GOLD10_DROP, + PRIV_EXP_PCT, + MAX_PRIV_NUM, +}; + +enum EMoneyLogType +{ + MONEY_LOG_RESERVED, + MONEY_LOG_MONSTER, + MONEY_LOG_SHOP, + MONEY_LOG_REFINE, + MONEY_LOG_QUEST, + MONEY_LOG_GUILD, + MONEY_LOG_MISC, + MONEY_LOG_MONSTER_KILL, + MONEY_LOG_DROP, + MONEY_LOG_TYPE_MAX_NUM, +}; + +enum EPremiumTypes +{ + PREMIUM_EXP, // ġ 1.2 + PREMIUM_ITEM, // 2 + PREMIUM_SAFEBOX, // â 1ĭ 3ĭ + PREMIUM_AUTOLOOT, // ڵ ݱ + PREMIUM_FISH_MIND, // Ȯ + PREMIUM_MARRIAGE_FAST, // ݽ մϴ. + PREMIUM_GOLD, // 1.5 + PREMIUM_MAX_NUM = 9 +}; + +enum SPECIAL_EFFECT +{ + SE_NONE, + + SE_HPUP_RED, + SE_SPUP_BLUE, + SE_SPEEDUP_GREEN, + SE_DXUP_PURPLE, + SE_CRITICAL, + SE_PENETRATE, + SE_BLOCK, + SE_DODGE, + SE_CHINA_FIREWORK, + SE_SPIN_TOP, + SE_SUCCESS, + SE_FAIL, + SE_FR_SUCCESS, + SE_LEVELUP_ON_14_FOR_GERMANY, + SE_LEVELUP_UNDER_15_FOR_GERMANY, + SE_PERCENT_DAMAGE1, + SE_PERCENT_DAMAGE2, + SE_PERCENT_DAMAGE3, + + SE_AUTO_HPUP, + SE_AUTO_SPUP, + + SE_EQUIP_RAMADAN_RING, // 󸶴 ʽ´ (71135) Ʈ (ߵƮ, Ʈ ƴ) + SE_EQUIP_HALLOWEEN_CANDY, // ҷ (-_-;) ߵϴ Ʈ + SE_EQUIP_HAPPINESS_RING, // ũ ູ (71143) Ʈ (ߵƮ, Ʈ ƴ) + SE_EQUIP_LOVE_PENDANT, // ߷Ÿ ҴƮ(71145) Ʈ (ߵƮ, Ʈ ƴ) +} ; + +enum ETeenFlags +{ + TEENFLAG_NONE = 0, + TEENFLAG_1HOUR, + TEENFLAG_2HOUR, + TEENFLAG_3HOUR, + TEENFLAG_4HOUR, + TEENFLAG_5HOUR, +}; + +#include "item_length.h" + +// inventory position Ÿ ü +// int Ͻ ȯ ִ , +// κ õ Լ window_type ʰ, cell ϳ ޾ұ ,( κ ϳ ̾ inventory type̶ ʿ ,) +// κ Լ ȣκ ϴ ϱ ̴. + +enum EDragonSoulRefineWindowSize +{ + DRAGON_SOUL_REFINE_GRID_MAX = 15, +}; + +enum EMisc2 +{ + DRAGON_SOUL_EQUIP_SLOT_START = INVENTORY_MAX_NUM + WEAR_MAX_NUM, + DRAGON_SOUL_EQUIP_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_START + (DS_SLOT_MAX * DRAGON_SOUL_DECK_MAX_NUM), + DRAGON_SOUL_EQUIP_RESERVED_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_END + (DS_SLOT_MAX * DRAGON_SOUL_DECK_RESERVED_MAX_NUM), + + BELT_INVENTORY_SLOT_START = DRAGON_SOUL_EQUIP_RESERVED_SLOT_END, + BELT_INVENTORY_SLOT_END = BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT, + + INVENTORY_AND_EQUIP_SLOT_MAX = BELT_INVENTORY_SLOT_END, +}; + +#pragma pack(push, 1) + +typedef struct SItemPos +{ + BYTE window_type; + WORD cell; + SItemPos () + { + window_type = INVENTORY; + cell = WORD_MAX; + } + + SItemPos (BYTE _window_type, WORD _cell) + { + window_type = _window_type; + cell = _cell; + } + + bool IsValidItemPosition() const + { + switch (window_type) + { + case RESERVED_WINDOW: + return false; + case INVENTORY: + case EQUIPMENT: + case BELT_INVENTORY: + return cell < INVENTORY_AND_EQUIP_SLOT_MAX; + case DRAGON_SOUL_INVENTORY: + return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM); + // ũⰡ window valid üũ . + case SAFEBOX: + case MALL: + return false; + default: + return false; + } + return false; + } + + bool IsEquipPosition() const + { + return ((INVENTORY == window_type || EQUIPMENT == window_type) && cell >= INVENTORY_MAX_NUM && cell < INVENTORY_MAX_NUM + WEAR_MAX_NUM) + || IsDragonSoulEquipPosition(); + } + + bool IsDragonSoulEquipPosition() const + { + return (DRAGON_SOUL_EQUIP_SLOT_START <= cell) && (DRAGON_SOUL_EQUIP_SLOT_END > cell); + } + + bool IsBeltInventoryPosition() const + { + return (BELT_INVENTORY_SLOT_START <= cell) && (BELT_INVENTORY_SLOT_END > cell); + } + + bool IsDefaultInventoryPosition() const + { + return INVENTORY == window_type && cell < INVENTORY_MAX_NUM; + } + + bool operator==(const struct SItemPos& rhs) const + { + return (window_type == rhs.window_type) && (cell == rhs.cell); + } + bool operator<(const struct SItemPos& rhs) const + { + return (window_type < rhs.window_type) || ((window_type == rhs.window_type) && (cell < rhs.cell)); + } +} TItemPos; + +const TItemPos NPOS (RESERVED_WINDOW, WORD_MAX); + +typedef enum +{ + SHOP_COIN_TYPE_GOLD, // DEFAULT VALUE + SHOP_COIN_TYPE_SECONDARY_COIN, +} EShopCoinType; + +#pragma pack(pop) + +#endif diff --git a/common/noncopyable.h b/common/noncopyable.h new file mode 100644 index 0000000..62e0ead --- /dev/null +++ b/common/noncopyable.h @@ -0,0 +1,15 @@ +#ifndef INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE +#define INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE + +class noncopyable +{ + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable &); + noncopyable& operator = (const noncopyable &); +}; + +#endif diff --git a/common/pool.h b/common/pool.h new file mode 100644 index 0000000..f53959c --- /dev/null +++ b/common/pool.h @@ -0,0 +1,185 @@ +#ifndef __INC_METIN_II_COMMON_POOL_H__ +#define __INC_METIN_II_COMMON_POOL_H__ + +#include +#include + +template +class CPoolNode : public T +{ + public: + CPoolNode() + { + m_pNext = NULL; + m_pPrev = NULL; + } + + virtual ~CPoolNode() + { + } + + public: + CPoolNode * m_pNext; + CPoolNode * m_pPrev; +}; + +template +class CDynamicPool +{ + public: + typedef CPoolNode TNode; + + public: + CDynamicPool() + { + Initialize(); + } + + virtual ~CDynamicPool() + { + assert(m_pFreeList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear"); + assert(m_pUsedList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear"); + Clear(); + } + + void Initialize() + { + m_nodes = NULL; + m_nodeCount = 0; + + m_pFreeList = NULL; + m_pUsedList = NULL; + } + + void SetName(const char* c_szName) + { + m_stName = c_szName; + } + + DWORD GetCapacity() + { + return m_nodeCount; + } + + T* Alloc() + { + TNode* pnewNode; + + if (m_pFreeList) + { + pnewNode = m_pFreeList; + m_pFreeList = m_pFreeList->m_pNext; + } + else + { + pnewNode = AllocNode(); + } + + if (!pnewNode) + return NULL; + + if (!m_pUsedList) + { + m_pUsedList = pnewNode; + m_pUsedList->m_pPrev = m_pUsedList->m_pNext = NULL; + } + else + { + m_pUsedList->m_pPrev = pnewNode; + pnewNode->m_pNext = m_pUsedList; + pnewNode->m_pPrev = NULL; + m_pUsedList = pnewNode; + } + //Tracef("%s Pool Alloc %p\n", m_stName.c_str(), pnewNode); + return (T*) pnewNode; + } + + void Free(T * pdata) + { + TNode* pfreeNode = (TNode*) pdata; + + if (pfreeNode == m_pUsedList) + { + if (NULL != (m_pUsedList = m_pUsedList->m_pNext)) + m_pUsedList->m_pPrev = NULL; + } + else + { + if (pfreeNode->m_pNext) + pfreeNode->m_pNext->m_pPrev = pfreeNode->m_pPrev; + + if (pfreeNode->m_pPrev) + pfreeNode->m_pPrev->m_pNext = pfreeNode->m_pNext; + } + + pfreeNode->m_pPrev = NULL; + pfreeNode->m_pNext = m_pFreeList; + m_pFreeList = pfreeNode; + //Tracef("%s Pool Free\n", m_stName.c_str()); + } + + void FreeAll() + { + TNode * pcurNode; + TNode * pnextNode; + + pcurNode = m_pUsedList; + + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + Free(pcurNode); + pcurNode = pnextNode; + } + } + + void Clear() + { + TNode* pcurNode; + TNode* pnextNode; + + DWORD count = 0; + + pcurNode = m_pFreeList; + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + delete pcurNode; + pcurNode = pnextNode; + ++count; + } + m_pFreeList = NULL; + + pcurNode = m_pUsedList; + while (pcurNode) + { + pnextNode = pcurNode->m_pNext; + delete pcurNode; + pcurNode = pnextNode; + ++count; + } + + m_pUsedList = NULL; + + assert(count==m_nodeCount && "CDynamicPool::Clear()"); + + m_nodeCount=0; + } + + protected: + TNode* AllocNode() + { + ++m_nodeCount; + return new TNode; + } + + protected: + TNode * m_nodes; + TNode * m_pFreeList; + TNode * m_pUsedList; + + DWORD m_nodeCount; + std::string m_stName; +}; + +#endif diff --git a/common/service.h b/common/service.h new file mode 100644 index 0000000..d35e14a --- /dev/null +++ b/common/service.h @@ -0,0 +1,8 @@ +#ifndef __INC_SERVICE_H__ +#define __INC_SERVICE_H__ + +#define _IMPROVED_PACKET_ENCRYPTION_ // Ŷ ȣȭ +//#define __AUCTION__ +#define __PET_SYSTEM__ +#define __UDP_BLOCK__ +#endif diff --git a/common/singleton.h b/common/singleton.h new file mode 100644 index 0000000..da4e135 --- /dev/null +++ b/common/singleton.h @@ -0,0 +1,44 @@ +#ifndef __INC_SINGLETON_H__ +#define __INC_SINGLETON_H__ + +#include + +template class singleton +{ + public: + static T * ms_singleton; + + singleton() + { + assert(!ms_singleton); + long offset = (long) (T*) 1 - (long) (singleton *) (T*) 1; + ms_singleton = (T*) ((long) this + offset); + } + + virtual ~singleton() + { + assert(ms_singleton); + ms_singleton = 0; + } + + static T & instance() + { + assert(ms_singleton); + return (*ms_singleton); + } + + static T & Instance() + { + assert(ms_singleton); + return (*ms_singleton); + } + + static T * instance_ptr() + { + return (ms_singleton); + } +}; + +template T * singleton ::ms_singleton = NULL; + +#endif diff --git a/common/stl.h b/common/stl.h new file mode 100644 index 0000000..32ee18f --- /dev/null +++ b/common/stl.h @@ -0,0 +1,154 @@ +#ifndef __INC_METIN_II_STL_H__ +#define __INC_METIN_II_STL_H__ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __GNUC__ +#include +#endif + +#ifndef itertype +#define itertype(v) typeof((v).begin()) +#endif + +inline void stl_lowers(std::string& rstRet) +{ + for (size_t i = 0; i < rstRet.length(); ++i) + rstRet[i] = tolower(rstRet[i]); +} + +struct stringhash +{ + size_t operator () (const std::string & str) const + { + const unsigned char * s = (const unsigned char*) str.c_str(); + const unsigned char * end = s + str.size(); + size_t h = 0; + + while (s < end) + { + h *= 16777619; + h ^= *(s++); + } + + return h; + } +}; + +// code from tr1/functional_hash.h +template +struct hash; + +template +struct hash<_Tp*> +: public std::unary_function<_Tp*, std::size_t> +{ + std::size_t + operator()(_Tp* __p) const + { return reinterpret_cast(__p); } +}; + +namespace std +{ + template + void erase_if (container & a, typename container::iterator first, typename container::iterator past, Pred pred) + { + while (first != past) + if (pred(*first)) + a.erase(first++); + else + ++first; + } + + template + void wipe(container & a) + { + typename container::iterator first, past; + + first = a.begin(); + past = a.end(); + + while (first != past) + delete *(first++); + + a.clear(); + } + + template + void wipe_second(container & a) + { + typename container::iterator first, past; + + first = a.begin(); + past = a.end(); + + while (first != past) + { + delete first->second; + ++first; + } + + a.clear(); + } + + template T MIN(T a, T b) + { + return a < b ? a : b; + } + + template T MAX(T a, T b) + { + return a > b ? a : b; + } + + template T MINMAX(T min, T value, T max) + { + T tv; + + tv = (min > value ? min : value); + return (max < tv) ? max : tv; + } + + template + class void_mem_fun_t : public unary_function<_Ty *, void> + { + public: + explicit void_mem_fun_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) + { + } + + void operator()(_Ty* p) const + { + ((p->*_Ptr)()); + } + + private: + void (_Ty::*_Ptr)(); + }; + + template inline + void_mem_fun_t<_Ty> void_mem_fun(void (_Ty::*_Pm)()) + { return (void_mem_fun_t<_Ty>(_Pm)); } + + template + class void_mem_fun_ref_t : public unary_function<_Ty, void> + { + public: + explicit void_mem_fun_ref_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) {} + void operator()(_Ty& x) const + { return ((x.*_Ptr)()); } + private: + void (_Ty::*_Ptr)(); + }; + + template inline + void_mem_fun_ref_t<_Ty> void_mem_fun_ref(void (_Ty::*_Pm)()) + { return (void_mem_fun_ref_t< _Ty>(_Pm)); } +}; + +#endif diff --git a/common/tables.h b/common/tables.h new file mode 100644 index 0000000..74d439f --- /dev/null +++ b/common/tables.h @@ -0,0 +1,1481 @@ +#ifndef __INC_TABLES_H__ +#define __INC_TABLES_H__ + +#include "length.h" + +typedef DWORD IDENT; + +/** + * @version 05/06/10 Bang2ni - Myshop Pricelist Ŷ HEADER_XX_MYSHOP_PRICELIST_XXX ߰ + */ +enum +{ + HEADER_GD_LOGIN = 1, + HEADER_GD_LOGOUT = 2, + + HEADER_GD_PLAYER_LOAD = 3, + HEADER_GD_PLAYER_SAVE = 4, + HEADER_GD_PLAYER_CREATE = 5, + HEADER_GD_PLAYER_DELETE = 6, + + HEADER_GD_LOGIN_KEY = 7, + // 8 empty + HEADER_GD_BOOT = 9, + HEADER_GD_PLAYER_COUNT = 10, + HEADER_GD_QUEST_SAVE = 11, + HEADER_GD_SAFEBOX_LOAD = 12, + HEADER_GD_SAFEBOX_SAVE = 13, + HEADER_GD_SAFEBOX_CHANGE_SIZE = 14, + HEADER_GD_EMPIRE_SELECT = 15, + + HEADER_GD_SAFEBOX_CHANGE_PASSWORD = 16, + HEADER_GD_SAFEBOX_CHANGE_PASSWORD_SECOND = 17, // Not really a packet, used internal + HEADER_GD_DIRECT_ENTER = 18, + + HEADER_GD_GUILD_SKILL_UPDATE = 19, + HEADER_GD_GUILD_EXP_UPDATE = 20, + HEADER_GD_GUILD_ADD_MEMBER = 21, + HEADER_GD_GUILD_REMOVE_MEMBER = 22, + HEADER_GD_GUILD_CHANGE_GRADE = 23, + HEADER_GD_GUILD_CHANGE_MEMBER_DATA = 24, + HEADER_GD_GUILD_DISBAND = 25, + HEADER_GD_GUILD_WAR = 26, + HEADER_GD_GUILD_WAR_SCORE = 27, + HEADER_GD_GUILD_CREATE = 28, + + HEADER_GD_ITEM_SAVE = 30, + HEADER_GD_ITEM_DESTROY = 31, + + HEADER_GD_ADD_AFFECT = 32, + HEADER_GD_REMOVE_AFFECT = 33, + + HEADER_GD_HIGHSCORE_REGISTER = 34, + HEADER_GD_ITEM_FLUSH = 35, + + HEADER_GD_PARTY_CREATE = 36, + HEADER_GD_PARTY_DELETE = 37, + HEADER_GD_PARTY_ADD = 38, + HEADER_GD_PARTY_REMOVE = 39, + HEADER_GD_PARTY_STATE_CHANGE = 40, + HEADER_GD_PARTY_HEAL_USE = 41, + + HEADER_GD_FLUSH_CACHE = 42, + HEADER_GD_RELOAD_PROTO = 43, + + HEADER_GD_CHANGE_NAME = 44, + HEADER_GD_SMS = 45, + + HEADER_GD_GUILD_CHANGE_LADDER_POINT = 46, + HEADER_GD_GUILD_USE_SKILL = 47, + + HEADER_GD_REQUEST_EMPIRE_PRIV = 48, + HEADER_GD_REQUEST_GUILD_PRIV = 49, + + HEADER_GD_MONEY_LOG = 50, + + HEADER_GD_GUILD_DEPOSIT_MONEY = 51, + HEADER_GD_GUILD_WITHDRAW_MONEY = 52, + HEADER_GD_GUILD_WITHDRAW_MONEY_GIVE_REPLY = 53, + + HEADER_GD_REQUEST_CHARACTER_PRIV = 54, + + HEADER_GD_SET_EVENT_FLAG = 55, + + HEADER_GD_PARTY_SET_MEMBER_LEVEL = 56, + + HEADER_GD_GUILD_WAR_BET = 57, + + HEADER_GD_CREATE_OBJECT = 60, + HEADER_GD_DELETE_OBJECT = 61, + HEADER_GD_UPDATE_LAND = 62, + + HEADER_GD_MARRIAGE_ADD = 70, + HEADER_GD_MARRIAGE_UPDATE = 71, + HEADER_GD_MARRIAGE_REMOVE = 72, + + HEADER_GD_WEDDING_REQUEST = 73, + HEADER_GD_WEDDING_READY = 74, + HEADER_GD_WEDDING_END = 75, + + HEADER_GD_AUTH_LOGIN = 100, + HEADER_GD_LOGIN_BY_KEY = 101, + HEADER_GD_BILLING_EXPIRE = 104, + HEADER_GD_VCARD = 105, + HEADER_GD_BILLING_CHECK = 106, + HEADER_GD_MALL_LOAD = 107, + + HEADER_GD_MYSHOP_PRICELIST_UPDATE = 108, ///< û + HEADER_GD_MYSHOP_PRICELIST_REQ = 109, ///< Ʈ û + + HEADER_GD_BLOCK_CHAT = 110, + + // PCBANG_IP_LIST_BY_AUTH + HEADER_GD_PCBANG_REQUEST_IP_LIST = 111, + HEADER_GD_PCBANG_CLEAR_IP_LIST = 112, + HEADER_GD_PCBANG_INSERT_IP = 113, + // END_OF_PCBANG_IP_LIST_BY_AUTH + + HEADER_GD_HAMMER_OF_TOR = 114, + HEADER_GD_RELOAD_ADMIN = 115, ///< û + HEADER_GD_BREAK_MARRIAGE = 116, ///< ȥ ı + HEADER_GD_ELECT_MONARCH = 117, ///< ǥ + HEADER_GD_CANDIDACY = 118, ///< + HEADER_GD_ADD_MONARCH_MONEY = 119, ///< + HEADER_GD_TAKE_MONARCH_MONEY = 120, ///< + HEADER_GD_COME_TO_VOTE = 121, ///< ǥ + HEADER_GD_RMCANDIDACY = 122, ///< ĺ () + HEADER_GD_SETMONARCH = 123, ///<ּ () + HEADER_GD_RMMONARCH = 124, ///<ֻ + HEADER_GD_DEC_MONARCH_MONEY = 125, + + HEADER_GD_CHANGE_MONARCH_LORD = 126, + HEADER_GD_BLOCK_COUNTRY_IP = 127, // 뿪 IP-Block + HEADER_GD_BLOCK_EXCEPTION = 128, // 뿪 IP-Block + + HEADER_GD_REQ_CHANGE_GUILD_MASTER = 129, + + HEADER_GD_REQ_SPARE_ITEM_ID_RANGE = 130, + + HEADER_GD_UPDATE_HORSE_NAME = 131, + HEADER_GD_REQ_HORSE_NAME = 132, + + HEADER_GD_DC = 133, // Login Key + + HEADER_GD_VALID_LOGOUT = 134, + + // AUCTION +#ifdef __AUCTION__ + HEADER_GD_GET_AUCTION_LIST = 135, + HEADER_GD_COMMAND_AUCTION = 136, +#endif + + HEADER_GD_REQUEST_CHARGE_CASH = 137, + + HEADER_GD_DELETE_AWARDID = 138, // delete gift notify icon + + HEADER_GD_UPDATE_CHANNELSTATUS = 139, + HEADER_GD_REQUEST_CHANNELSTATUS = 140, + + HEADER_GD_SETUP = 0xff, + + /////////////////////////////////////////////// + HEADER_DG_NOTICE = 1, + + HEADER_DG_LOGIN_SUCCESS = 30, + HEADER_DG_LOGIN_NOT_EXIST = 31, + HEADER_DG_LOGIN_WRONG_PASSWD = 33, + HEADER_DG_LOGIN_ALREADY = 34, + + HEADER_DG_PLAYER_LOAD_SUCCESS = 35, + HEADER_DG_PLAYER_LOAD_FAILED = 36, + HEADER_DG_PLAYER_CREATE_SUCCESS = 37, + HEADER_DG_PLAYER_CREATE_ALREADY = 38, + HEADER_DG_PLAYER_CREATE_FAILED = 39, + HEADER_DG_PLAYER_DELETE_SUCCESS = 40, + HEADER_DG_PLAYER_DELETE_FAILED = 41, + + HEADER_DG_ITEM_LOAD = 42, + + HEADER_DG_BOOT = 43, + HEADER_DG_QUEST_LOAD = 44, + + HEADER_DG_SAFEBOX_LOAD = 45, + HEADER_DG_SAFEBOX_CHANGE_SIZE = 46, + HEADER_DG_SAFEBOX_WRONG_PASSWORD = 47, + HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER = 48, + + HEADER_DG_EMPIRE_SELECT = 49, + + HEADER_DG_AFFECT_LOAD = 50, + HEADER_DG_MALL_LOAD = 51, + + HEADER_DG_DIRECT_ENTER = 55, + + HEADER_DG_GUILD_SKILL_UPDATE = 56, + HEADER_DG_GUILD_SKILL_RECHARGE = 57, + HEADER_DG_GUILD_EXP_UPDATE = 58, + + HEADER_DG_PARTY_CREATE = 59, + HEADER_DG_PARTY_DELETE = 60, + HEADER_DG_PARTY_ADD = 61, + HEADER_DG_PARTY_REMOVE = 62, + HEADER_DG_PARTY_STATE_CHANGE = 63, + HEADER_DG_PARTY_HEAL_USE = 64, + HEADER_DG_PARTY_SET_MEMBER_LEVEL = 65, + + HEADER_DG_TIME = 90, + HEADER_DG_ITEM_ID_RANGE = 91, + + HEADER_DG_GUILD_ADD_MEMBER = 92, + HEADER_DG_GUILD_REMOVE_MEMBER = 93, + HEADER_DG_GUILD_CHANGE_GRADE = 94, + HEADER_DG_GUILD_CHANGE_MEMBER_DATA = 95, + HEADER_DG_GUILD_DISBAND = 96, + HEADER_DG_GUILD_WAR = 97, + HEADER_DG_GUILD_WAR_SCORE = 98, + HEADER_DG_GUILD_TIME_UPDATE = 99, + HEADER_DG_GUILD_LOAD = 100, + HEADER_DG_GUILD_LADDER = 101, + HEADER_DG_GUILD_SKILL_USABLE_CHANGE = 102, + HEADER_DG_GUILD_MONEY_CHANGE = 103, + HEADER_DG_GUILD_WITHDRAW_MONEY_GIVE = 104, + + HEADER_DG_SET_EVENT_FLAG = 105, + + HEADER_DG_GUILD_WAR_RESERVE_ADD = 106, + HEADER_DG_GUILD_WAR_RESERVE_DEL = 107, + HEADER_DG_GUILD_WAR_BET = 108, + + HEADER_DG_RELOAD_PROTO = 120, + HEADER_DG_CHANGE_NAME = 121, + + HEADER_DG_AUTH_LOGIN = 122, + + HEADER_DG_CHANGE_EMPIRE_PRIV = 124, + HEADER_DG_CHANGE_GUILD_PRIV = 125, + + HEADER_DG_MONEY_LOG = 126, + + HEADER_DG_CHANGE_CHARACTER_PRIV = 127, + + HEADER_DG_BILLING_REPAIR = 128, + HEADER_DG_BILLING_EXPIRE = 129, + HEADER_DG_BILLING_LOGIN = 130, + HEADER_DG_VCARD = 131, + HEADER_DG_BILLING_CHECK = 132, + + HEADER_DG_CREATE_OBJECT = 140, + HEADER_DG_DELETE_OBJECT = 141, + HEADER_DG_UPDATE_LAND = 142, + + HEADER_DG_MARRIAGE_ADD = 150, + HEADER_DG_MARRIAGE_UPDATE = 151, + HEADER_DG_MARRIAGE_REMOVE = 152, + + HEADER_DG_WEDDING_REQUEST = 153, + HEADER_DG_WEDDING_READY = 154, + HEADER_DG_WEDDING_START = 155, + HEADER_DG_WEDDING_END = 156, + + HEADER_DG_MYSHOP_PRICELIST_RES = 157, ///< Ʈ + HEADER_DG_RELOAD_ADMIN = 158, ///<  ε + HEADER_DG_BREAK_MARRIAGE = 159, ///< ȥ ı + HEADER_DG_ELECT_MONARCH = 160, ///< ǥ + HEADER_DG_CANDIDACY = 161, ///< + HEADER_DG_ADD_MONARCH_MONEY = 162, ///< + HEADER_DG_TAKE_MONARCH_MONEY = 163, ///< + HEADER_DG_COME_TO_VOTE = 164, ///< ǥ + HEADER_DG_RMCANDIDACY = 165, ///< ĺ () + HEADER_DG_SETMONARCH = 166, ///<ּ () + HEADER_DG_RMMONARCH = 167, ///<ֻ + HEADER_DG_DEC_MONARCH_MONEY = 168, + + HEADER_DG_CHANGE_MONARCH_LORD_ACK = 169, + HEADER_DG_UPDATE_MONARCH_INFO = 170, + HEADER_DG_BLOCK_COUNTRY_IP = 171, // 뿪 IP-Block + HEADER_DG_BLOCK_EXCEPTION = 172, // 뿪 IP-Block account + + HEADER_DG_ACK_CHANGE_GUILD_MASTER = 173, + + HEADER_DG_ACK_SPARE_ITEM_ID_RANGE = 174, + + HEADER_DG_UPDATE_HORSE_NAME = 175, + HEADER_DG_ACK_HORSE_NAME = 176, + + HEADER_DG_NEED_LOGIN_LOG = 177, +#ifdef __AUCTION__ + HEADER_DG_AUCTION_RESULT = 178, +#endif + HEADER_DG_RESULT_CHARGE_CASH = 179, + HEADER_DG_ITEMAWARD_INFORMER = 180, //gift notify + HEADER_DG_RESPOND_CHANNELSTATUS = 181, + + HEADER_DG_MAP_LOCATIONS = 0xfe, + HEADER_DG_P2P = 0xff, + + HEADER_GP_CONFIRM_PASSPOD = 1, + HEADER_PG_CONFIRM_PASSPOD = 2, + +}; + +enum E_PASSPOD +{ + E_PASSPOD_SUCCESS = 0, + E_PASSPOD_FAILED_PASSPOD_ERROR, + E_PASSPOD_FAILED_USER_NOT_FOUND, + E_PASSPOD_FAILED_SYSTEM_NOT_FOUND, + E_PASSPOD_FAILED_TOKEN_DISABLED, + E_PASSPOD_FAILED_EMPTY, +}; + + +typedef struct SRequestConfirmPasspod +{ + int pid; + char passpod[MAX_PASSPOD + 1]; + char login[LOGIN_MAX_LEN + 1]; + +} RequestConfirmPasspod; + +typedef struct SResultConfirmPasspod +{ + int pid; + int ret_code; + char login[LOGIN_MAX_LEN + 1]; +} ResultConfirmPasspod; +/* ---------------------------------------------- + * table + * ---------------------------------------------- + */ + +/* game Server -> DB Server */ +#pragma pack(1) +enum ERequestChargeType +{ + ERequestCharge_Cash = 0, + ERequestCharge_Mileage, +}; + +typedef struct SRequestChargeCash +{ + DWORD dwAID; // id(primary key) - Account Table + DWORD dwAmount; + ERequestChargeType eChargeType; + +} TRequestChargeCash; + +typedef struct SSimplePlayer +{ + DWORD dwID; + char szName[CHARACTER_NAME_MAX_LEN + 1]; + BYTE byJob; + BYTE byLevel; + DWORD dwPlayMinutes; + BYTE byST, byHT, byDX, byIQ; + WORD wMainPart; + BYTE bChangeName; + WORD wHairPart; + BYTE bDummy[4]; + long x, y; + long lAddr; + WORD wPort; + BYTE skill_group; +} TSimplePlayer; + +typedef struct SAccountTable +{ + DWORD id; + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + char social_id[SOCIAL_ID_MAX_LEN + 1]; + char status[ACCOUNT_STATUS_MAX_LEN + 1]; + BYTE bEmpire; + TSimplePlayer players[PLAYER_PER_ACCOUNT]; +} TAccountTable; + +typedef struct SPacketDGCreateSuccess +{ + BYTE bAccountCharacterIndex; + TSimplePlayer player; +} TPacketDGCreateSuccess; + +typedef struct TPlayerItemAttribute +{ + BYTE bType; + short sValue; +} TPlayerItemAttribute; + +typedef struct SPlayerItem +{ + DWORD id; + BYTE window; + WORD pos; + DWORD count; + + DWORD vnum; + long alSockets[ITEM_SOCKET_MAX_NUM]; // Ϲȣ + + TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM]; + + DWORD owner; +} TPlayerItem; + +typedef struct SQuickslot +{ + BYTE type; + BYTE pos; +} TQuickslot; + +typedef struct SPlayerSkill +{ + BYTE bMasterType; + BYTE bLevel; + time_t tNextRead; +} TPlayerSkill; + +struct THorseInfo +{ + BYTE bLevel; + BYTE bRiding; + short sStamina; + short sHealth; + DWORD dwHorseHealthDropTime; +}; + +typedef struct SPlayerTable +{ + DWORD id; + + char name[CHARACTER_NAME_MAX_LEN + 1]; + char ip[IP_ADDRESS_LENGTH + 1]; + + WORD job; + BYTE voice; + + BYTE level; + BYTE level_step; + short st, ht, dx, iq; + + DWORD exp; + INT gold; + + BYTE dir; + INT x, y, z; + INT lMapIndex; + + long lExitX, lExitY; + long lExitMapIndex; + + short hp; + short sp; + + short sRandomHP; + short sRandomSP; + + int playtime; + + short stat_point; + short skill_point; + short sub_skill_point; + short horse_skill_point; + + TPlayerSkill skills[SKILL_MAX_NUM]; + + TQuickslot quickslot[QUICKSLOT_MAX_NUM]; + + BYTE part_base; + WORD parts[PART_MAX_NUM]; + + short stamina; + + BYTE skill_group; + long lAlignment; + char szMobile[MOBILE_MAX_LEN + 1]; + + short stat_reset_count; + + THorseInfo horse; + + DWORD logoff_interval; + + int aiPremiumTimes[PREMIUM_MAX_NUM]; +} TPlayerTable; + +typedef struct SMobSkillLevel +{ + DWORD dwVnum; + BYTE bLevel; +} TMobSkillLevel; + +typedef struct SEntityTable +{ + DWORD dwVnum; +} TEntityTable; + +typedef struct SMobTable : public SEntityTable +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + char szLocaleName[CHARACTER_NAME_MAX_LEN + 1]; + + BYTE bType; // Monster, NPC + BYTE bRank; // PAWN, KNIGHT, KING + BYTE bBattleType; // MELEE, etc.. + BYTE bLevel; // Level + BYTE bSize; + + DWORD dwGoldMin; + DWORD dwGoldMax; + DWORD dwExp; + DWORD dwMaxHP; + BYTE bRegenCycle; + BYTE bRegenPercent; + WORD wDef; + + DWORD dwAIFlag; + DWORD dwRaceFlag; + DWORD dwImmuneFlag; + + BYTE bStr, bDex, bCon, bInt; + DWORD dwDamageRange[2]; + + short sAttackSpeed; + short sMovingSpeed; + BYTE bAggresiveHPPct; + WORD wAggressiveSight; + WORD wAttackRange; + + char cEnchants[MOB_ENCHANTS_MAX_NUM]; + char cResists[MOB_RESISTS_MAX_NUM]; + + DWORD dwResurrectionVnum; + DWORD dwDropItemVnum; + + BYTE bMountCapacity; + BYTE bOnClickType; + + BYTE bEmpire; + char szFolder[64 + 1]; + + float fDamMultiply; + + DWORD dwSummonVnum; + DWORD dwDrainSP; + DWORD dwMobColor; + DWORD dwPolymorphItemVnum; + + TMobSkillLevel Skills[MOB_SKILL_MAX_NUM]; + + BYTE bBerserkPoint; + BYTE bStoneSkinPoint; + BYTE bGodSpeedPoint; + BYTE bDeathBlowPoint; + BYTE bRevivePoint; +} TMobTable; + +typedef struct SSkillTable +{ + DWORD dwVnum; + char szName[32 + 1]; + BYTE bType; + BYTE bMaxLevel; + DWORD dwSplashRange; + + char szPointOn[64]; + char szPointPoly[100 + 1]; + char szSPCostPoly[100 + 1]; + char szDurationPoly[100 + 1]; + char szDurationSPCostPoly[100 + 1]; + char szCooldownPoly[100 + 1]; + char szMasterBonusPoly[100 + 1]; + //char szAttackGradePoly[100 + 1]; + char szGrandMasterAddSPCostPoly[100 + 1]; + DWORD dwFlag; + DWORD dwAffectFlag; + + // Data for secondary skill + char szPointOn2[64]; + char szPointPoly2[100 + 1]; + char szDurationPoly2[100 + 1]; + DWORD dwAffectFlag2; + + // Data for grand master point + char szPointOn3[64]; + char szPointPoly3[100 + 1]; + char szDurationPoly3[100 + 1]; + + BYTE bLevelStep; + BYTE bLevelLimit; + DWORD preSkillVnum; + BYTE preSkillLevel; + + long lMaxHit; + char szSplashAroundDamageAdjustPoly[100 + 1]; + + BYTE bSkillAttrType; + + DWORD dwTargetRange; +} TSkillTable; + +typedef struct SShopItemTable +{ + DWORD vnum; + BYTE count; + + TItemPos pos; // PC ̿ + DWORD price; // PC, shop_table_ex.txt ̿ + BYTE display_pos; // PC, shop_table_ex.txt ̿, ġ. +} TShopItemTable; + +typedef struct SShopTable +{ + DWORD dwVnum; + DWORD dwNPCVnum; + + BYTE byItemCount; + TShopItemTable items[SHOP_HOST_ITEM_MAX_NUM]; +} TShopTable; + +#define QUEST_NAME_MAX_LEN 32 +#define QUEST_STATE_MAX_LEN 64 + +typedef struct SQuestTable +{ + DWORD dwPID; + char szName[QUEST_NAME_MAX_LEN + 1]; + char szState[QUEST_STATE_MAX_LEN + 1]; + long lValue; +} TQuestTable; + +typedef struct SItemLimit +{ + BYTE bType; + long lValue; +} TItemLimit; + +typedef struct SItemApply +{ + BYTE bType; + long lValue; +} TItemApply; + +typedef struct SItemTable : public SEntityTable +{ + DWORD dwVnumRange; + char szName[ITEM_NAME_MAX_LEN + 1]; + char szLocaleName[ITEM_NAME_MAX_LEN + 1]; + BYTE bType; + BYTE bSubType; + + BYTE bWeight; + BYTE bSize; + + DWORD dwAntiFlags; + DWORD dwFlags; + DWORD dwWearFlags; + DWORD dwImmuneFlag; + + DWORD dwGold; + DWORD dwShopBuyPrice; + + TItemLimit aLimits[ITEM_LIMIT_MAX_NUM]; + TItemApply aApplies[ITEM_APPLY_MAX_NUM]; + long alValues[ITEM_VALUES_MAX_NUM]; + long alSockets[ITEM_SOCKET_MAX_NUM]; + DWORD dwRefinedVnum; + WORD wRefineSet; + BYTE bAlterToMagicItemPct; + BYTE bSpecular; + BYTE bGainSocketPct; + + short int sAddonType; // ⺻ Ӽ + + // Ʒ limit flag realtime üũ , VNUM ε, + // Ź ۸ ʿ 쿡 LIMIT_MAX_NUM 鼭 üũϴ ϰ Ŀ ̸ . + char cLimitRealTimeFirstUseIndex; // limit ʵ尪 ߿ LIMIT_REAL_TIME_FIRST_USE ÷ ġ ( -1) + char cLimitTimerBasedOnWearIndex; // limit ʵ尪 ߿ LIMIT_TIMER_BASED_ON_WEAR ÷ ġ ( -1) + +} TItemTable; + +struct TItemAttrTable +{ + TItemAttrTable() : + dwApplyIndex(0), + dwProb(0) + { + szApply[0] = 0; + memset(&lValues, 0, sizeof(lValues)); + memset(&bMaxLevelBySet, 0, sizeof(bMaxLevelBySet)); + } + + char szApply[APPLY_NAME_MAX_LEN + 1]; + DWORD dwApplyIndex; + DWORD dwProb; + long lValues[5]; + BYTE bMaxLevelBySet[ATTRIBUTE_SET_MAX_NUM]; +}; + +typedef struct SConnectTable +{ + char login[LOGIN_MAX_LEN + 1]; + IDENT ident; +} TConnectTable; + +typedef struct SLoginPacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; +} TLoginPacket; + +typedef struct SPlayerLoadPacket +{ + DWORD account_id; + DWORD player_id; + BYTE account_index; /* account ġ */ +} TPlayerLoadPacket; + +typedef struct SPlayerCreatePacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + DWORD account_id; + BYTE account_index; + TPlayerTable player_table; +} TPlayerCreatePacket; + +typedef struct SPlayerDeletePacket +{ + char login[LOGIN_MAX_LEN + 1]; + DWORD player_id; + BYTE account_index; + //char name[CHARACTER_NAME_MAX_LEN + 1]; + char private_code[8]; +} TPlayerDeletePacket; + +typedef struct SLogoutPacket +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; +} TLogoutPacket; + +typedef struct SPlayerCountPacket +{ + DWORD dwCount; +} TPlayerCountPacket; + +#define SAFEBOX_MAX_NUM 135 +#define SAFEBOX_PASSWORD_MAX_LEN 6 + +typedef struct SSafeboxTable +{ + DWORD dwID; + BYTE bSize; + DWORD dwGold; + WORD wItemCount; +} TSafeboxTable; + +typedef struct SSafeboxChangeSizePacket +{ + DWORD dwID; + BYTE bSize; +} TSafeboxChangeSizePacket; + +typedef struct SSafeboxLoadPacket +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN + 1]; + char szPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; +} TSafeboxLoadPacket; + +typedef struct SSafeboxChangePasswordPacket +{ + DWORD dwID; + char szOldPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; + char szNewPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; +} TSafeboxChangePasswordPacket; + +typedef struct SSafeboxChangePasswordPacketAnswer +{ + BYTE flag; +} TSafeboxChangePasswordPacketAnswer; + +typedef struct SEmpireSelectPacket +{ + DWORD dwAccountID; + BYTE bEmpire; +} TEmpireSelectPacket; + +typedef struct SPacketGDSetup +{ + char szPublicIP[16]; // Public IP which listen to users + BYTE bChannel; // ä + WORD wListenPort; // Ŭ̾Ʈ ϴ Ʈ ȣ + WORD wP2PPort; // Ű P2P Ʈ ȣ + long alMaps[32]; + DWORD dwLoginCount; + BYTE bAuthServer; +} TPacketGDSetup; + +typedef struct SPacketDGMapLocations +{ + BYTE bCount; +} TPacketDGMapLocations; + +typedef struct SMapLocation +{ + long alMaps[32]; + char szHost[MAX_HOST_LENGTH + 1]; + WORD wPort; +} TMapLocation; + +typedef struct SPacketDGP2P +{ + char szHost[MAX_HOST_LENGTH + 1]; + WORD wPort; + BYTE bChannel; +} TPacketDGP2P; + +typedef struct SPacketGDDirectEnter +{ + char login[LOGIN_MAX_LEN + 1]; + char passwd[PASSWD_MAX_LEN + 1]; + BYTE index; +} TPacketGDDirectEnter; + +typedef struct SPacketDGDirectEnter +{ + TAccountTable accountTable; + TPlayerTable playerTable; +} TPacketDGDirectEnter; + +typedef struct SPacketGuildSkillUpdate +{ + DWORD guild_id; + int amount; + BYTE skill_levels[12]; + BYTE skill_point; + BYTE save; +} TPacketGuildSkillUpdate; + +typedef struct SPacketGuildExpUpdate +{ + DWORD guild_id; + int amount; +} TPacketGuildExpUpdate; + +typedef struct SPacketGuildChangeMemberData +{ + DWORD guild_id; + DWORD pid; + DWORD offer; + BYTE level; + BYTE grade; +} TPacketGuildChangeMemberData; + + +typedef struct SPacketDGLoginAlready +{ + char szLogin[LOGIN_MAX_LEN + 1]; +} TPacketDGLoginAlready; + +typedef struct TPacketAffectElement +{ + DWORD dwType; + BYTE bApplyOn; + long lApplyValue; + DWORD dwFlag; + long lDuration; + long lSPCost; +} TPacketAffectElement; + +typedef struct SPacketGDAddAffect +{ + DWORD dwPID; + TPacketAffectElement elem; +} TPacketGDAddAffect; + +typedef struct SPacketGDRemoveAffect +{ + DWORD dwPID; + DWORD dwType; + BYTE bApplyOn; +} TPacketGDRemoveAffect; + +typedef struct SPacketGDHighscore +{ + DWORD dwPID; + long lValue; + char cDir; + char szBoard[21]; +} TPacketGDHighscore; + +typedef struct SPacketPartyCreate +{ + DWORD dwLeaderPID; +} TPacketPartyCreate; + +typedef struct SPacketPartyDelete +{ + DWORD dwLeaderPID; +} TPacketPartyDelete; + +typedef struct SPacketPartyAdd +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bState; +} TPacketPartyAdd; + +typedef struct SPacketPartyRemove +{ + DWORD dwLeaderPID; + DWORD dwPID; +} TPacketPartyRemove; + +typedef struct SPacketPartyStateChange +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bRole; + BYTE bFlag; +} TPacketPartyStateChange; + +typedef struct SPacketPartySetMemberLevel +{ + DWORD dwLeaderPID; + DWORD dwPID; + BYTE bLevel; +} TPacketPartySetMemberLevel; + +typedef struct SPacketGDBoot +{ + DWORD dwItemIDRange[2]; + char szIP[16]; +} TPacketGDBoot; + +typedef struct SPacketGuild +{ + DWORD dwGuild; + DWORD dwInfo; +} TPacketGuild; + +typedef struct SPacketGDGuildAddMember +{ + DWORD dwPID; + DWORD dwGuild; + BYTE bGrade; +} TPacketGDGuildAddMember; + +typedef struct SPacketDGGuildMember +{ + DWORD dwPID; + DWORD dwGuild; + BYTE bGrade; + BYTE isGeneral; + BYTE bJob; + BYTE bLevel; + DWORD dwOffer; + char szName[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketDGGuildMember; + +typedef struct SPacketGuildWar +{ + BYTE bType; + BYTE bWar; + DWORD dwGuildFrom; + DWORD dwGuildTo; + long lWarPrice; + long lInitialScore; +} TPacketGuildWar; + +// Game -> DB : ȭ +// DB -> Game : Ż +typedef struct SPacketGuildWarScore +{ + DWORD dwGuildGainPoint; + DWORD dwGuildOpponent; + long lScore; + long lBetScore; +} TPacketGuildWarScore; + +typedef struct SRefineMaterial +{ + DWORD vnum; + int count; +} TRefineMaterial; + +typedef struct SRefineTable +{ + //DWORD src_vnum; + //DWORD result_vnum; + DWORD id; + BYTE material_count; + int cost; // ҿ + int prob; // Ȯ + TRefineMaterial materials[REFINE_MATERIAL_MAX_NUM]; +} TRefineTable; + +typedef struct SBanwordTable +{ + char szWord[BANWORD_MAX_LEN + 1]; +} TBanwordTable; + +typedef struct SPacketGDChangeName +{ + DWORD pid; + char name[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketGDChangeName; + +typedef struct SPacketDGChangeName +{ + DWORD pid; + char name[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketDGChangeName; + +typedef struct SPacketGuildLadder +{ + DWORD dwGuild; + long lLadderPoint; + long lWin; + long lDraw; + long lLoss; +} TPacketGuildLadder; + +typedef struct SPacketGuildLadderPoint +{ + DWORD dwGuild; + long lChange; +} TPacketGuildLadderPoint; + +typedef struct SPacketGDSMS +{ + char szFrom[CHARACTER_NAME_MAX_LEN + 1]; + char szTo[CHARACTER_NAME_MAX_LEN + 1]; + char szMobile[MOBILE_MAX_LEN + 1]; + char szMsg[SMS_MAX_LEN + 1]; +} TPacketGDSMS; + +typedef struct SPacketGuildUseSkill +{ + DWORD dwGuild; + DWORD dwSkillVnum; + DWORD dwCooltime; +} TPacketGuildUseSkill; + +typedef struct SPacketGuildSkillUsableChange +{ + DWORD dwGuild; + DWORD dwSkillVnum; + BYTE bUsable; +} TPacketGuildSkillUsableChange; + +typedef struct SPacketGDLoginKey +{ + DWORD dwAccountID; + DWORD dwLoginKey; +} TPacketGDLoginKey; + +typedef struct SPacketGDAuthLogin +{ + DWORD dwID; + DWORD dwLoginKey; + char szLogin[LOGIN_MAX_LEN + 1]; + char szSocialID[SOCIAL_ID_MAX_LEN + 1]; + DWORD adwClientKey[4]; + BYTE bBillType; + DWORD dwBillID; + int iPremiumTimes[PREMIUM_MAX_NUM]; +} TPacketGDAuthLogin; + +typedef struct SPacketGDLoginByKey +{ + char szLogin[LOGIN_MAX_LEN + 1]; + DWORD dwLoginKey; + DWORD adwClientKey[4]; + char szIP[MAX_HOST_LENGTH + 1]; +} TPacketGDLoginByKey; + +/** + * @version 05/06/08 Bang2ni - ӽð ߰ + */ +typedef struct SPacketGiveGuildPriv +{ + BYTE type; + int value; + DWORD guild_id; + time_t duration_sec; ///< ӽð +} TPacketGiveGuildPriv; +typedef struct SPacketGiveEmpirePriv +{ + BYTE type; + int value; + BYTE empire; + time_t duration_sec; +} TPacketGiveEmpirePriv; +typedef struct SPacketGiveCharacterPriv +{ + BYTE type; + int value; + DWORD pid; +} TPacketGiveCharacterPriv; +typedef struct SPacketRemoveGuildPriv +{ + BYTE type; + DWORD guild_id; +} TPacketRemoveGuildPriv; +typedef struct SPacketRemoveEmpirePriv +{ + BYTE type; + BYTE empire; +} TPacketRemoveEmpirePriv; + +typedef struct SPacketDGChangeCharacterPriv +{ + BYTE type; + int value; + DWORD pid; + BYTE bLog; +} TPacketDGChangeCharacterPriv; + +/** + * @version 05/06/08 Bang2ni - ӽð ߰ + */ +typedef struct SPacketDGChangeGuildPriv +{ + BYTE type; + int value; + DWORD guild_id; + BYTE bLog; + time_t end_time_sec; ///< ӽð +} TPacketDGChangeGuildPriv; + +typedef struct SPacketDGChangeEmpirePriv +{ + BYTE type; + int value; + BYTE empire; + BYTE bLog; + time_t end_time_sec; +} TPacketDGChangeEmpirePriv; + +typedef struct SPacketMoneyLog +{ + BYTE type; + DWORD vnum; + INT gold; +} TPacketMoneyLog; + +typedef struct SPacketGDGuildMoney +{ + DWORD dwGuild; + INT iGold; +} TPacketGDGuildMoney; + +typedef struct SPacketDGGuildMoneyChange +{ + DWORD dwGuild; + INT iTotalGold; +} TPacketDGGuildMoneyChange; + +typedef struct SPacketDGGuildMoneyWithdraw +{ + DWORD dwGuild; + INT iChangeGold; +} TPacketDGGuildMoneyWithdraw; + +typedef struct SPacketGDGuildMoneyWithdrawGiveReply +{ + DWORD dwGuild; + INT iChangeGold; + BYTE bGiveSuccess; +} TPacketGDGuildMoneyWithdrawGiveReply; + +typedef struct SPacketSetEventFlag +{ + char szFlagName[EVENT_FLAG_NAME_MAX_LEN + 1]; + long lValue; +} TPacketSetEventFlag; + +typedef struct SPacketBillingLogin +{ + DWORD dwLoginKey; + BYTE bLogin; +} TPacketBillingLogin; + +typedef struct SPacketBillingRepair +{ + DWORD dwLoginKey; + char szLogin[LOGIN_MAX_LEN + 1]; + char szHost[MAX_HOST_LENGTH + 1]; +} TPacketBillingRepair; + +typedef struct SPacketBillingExpire +{ + char szLogin[LOGIN_MAX_LEN + 1]; + BYTE bBillType; + DWORD dwRemainSeconds; +} TPacketBillingExpire; + +typedef struct SPacketLoginOnSetup +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN + 1]; + char szSocialID[SOCIAL_ID_MAX_LEN + 1]; + char szHost[MAX_HOST_LENGTH + 1]; + DWORD dwLoginKey; + DWORD adwClientKey[4]; +} TPacketLoginOnSetup; + +typedef struct SPacketGDCreateObject +{ + DWORD dwVnum; + DWORD dwLandID; + INT lMapIndex; + INT x, y; + float xRot; + float yRot; + float zRot; +} TPacketGDCreateObject; + +typedef struct SPacketGDHammerOfTor +{ + DWORD key; + DWORD delay; +} TPacketGDHammerOfTor; + +typedef struct SPacketGDVCard +{ + DWORD dwID; + char szSellCharacter[CHARACTER_NAME_MAX_LEN + 1]; + char szSellAccount[LOGIN_MAX_LEN + 1]; + char szBuyCharacter[CHARACTER_NAME_MAX_LEN + 1]; + char szBuyAccount[LOGIN_MAX_LEN + 1]; +} TPacketGDVCard; + +typedef struct SGuildReserve +{ + DWORD dwID; + DWORD dwGuildFrom; + DWORD dwGuildTo; + DWORD dwTime; + BYTE bType; + long lWarPrice; + long lInitialScore; + bool bStarted; + DWORD dwBetFrom; + DWORD dwBetTo; + long lPowerFrom; + long lPowerTo; + long lHandicap; +} TGuildWarReserve; + +typedef struct +{ + DWORD dwWarID; + char szLogin[LOGIN_MAX_LEN + 1]; + DWORD dwGold; + DWORD dwGuild; +} TPacketGDGuildWarBet; + +// Marriage + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; + time_t tMarryTime; + char szName1[CHARACTER_NAME_MAX_LEN + 1]; + char szName2[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketMarriageAdd; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; + INT iLovePoint; + BYTE byMarried; +} TPacketMarriageUpdate; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketMarriageRemove; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingRequest; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; + DWORD dwMapIndex; +} TPacketWeddingReady; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingStart; + +typedef struct +{ + DWORD dwPID1; + DWORD dwPID2; +} TPacketWeddingEnd; + +/// λ . Ŷ ڿ byCount ŭ TItemPriceInfo ´. +typedef struct SPacketMyshopPricelistHeader +{ + DWORD dwOwnerID; ///< ÷̾ ID + BYTE byCount; ///< +} TPacketMyshopPricelistHeader; + +/// λ ۿ +typedef struct SItemPriceInfo +{ + DWORD dwVnum; ///< vnum + DWORD dwPrice; ///< +} TItemPriceInfo; + +/// λ Ʈ ̺ +typedef struct SItemPriceListTable +{ + DWORD dwOwnerID; ///< ÷̾ ID + BYTE byCount; ///< Ʈ + + TItemPriceInfo aPriceInfo[SHOP_PRICELIST_MAX_NUM]; ///< Ʈ +} TItemPriceListTable; + +typedef struct +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + long lDuration; +} TPacketBlockChat; + +// PCBANG_IP_LIST +typedef struct SPacketPCBangIP +{ + DWORD id; + DWORD ip; +} TPacketPCBangIP; +// END_OF_PCBANG_IP_LIST + + +//ADMIN_MANAGER +typedef struct TAdminInfo +{ + int m_ID; //ID + char m_szAccount[32]; // + char m_szName[32]; //ij̸ + char m_szContactIP[16]; //پ + char m_szServerIP[16]; // + int m_Authority; // +} tAdminInfo; +//END_ADMIN_MANAGER + +//BOOT_LOCALIZATION +struct tLocale +{ + char szValue[32]; + char szKey[32]; +}; +//BOOT_LOCALIZATION + +//RELOAD_ADMIN +typedef struct SPacketReloadAdmin +{ + char szIP[16]; +} TPacketReloadAdmin; +//END_RELOAD_ADMIN + +typedef struct TMonarchInfo +{ + DWORD pid[4]; // PID + int64_t money[4]; // + char name[4][32]; // ̸ + char date[4][32]; // ¥ +} MonarchInfo; + +typedef struct TMonarchElectionInfo +{ + DWORD pid; // ǥ ѻ PID + DWORD selectedpid; // ǥ PID ( ) + char date[32]; // ǥ ¥ +} MonarchElectionInfo; + +// ⸶ +typedef struct tMonarchCandidacy +{ + DWORD pid; + char name[32]; + char date[32]; +} MonarchCandidacy; + +typedef struct tChangeMonarchLord +{ + BYTE bEmpire; + DWORD dwPID; +} TPacketChangeMonarchLord; + +typedef struct tChangeMonarchLordACK +{ + BYTE bEmpire; + DWORD dwPID; + char szName[32]; + char szDate[32]; +} TPacketChangeMonarchLordACK; + +// Block Country Ip +typedef struct tBlockCountryIp +{ + DWORD ip_from; + DWORD ip_to; +} TPacketBlockCountryIp; + +enum EBlockExceptionCommand +{ + BLOCK_EXCEPTION_CMD_ADD = 1, + BLOCK_EXCEPTION_CMD_DEL = 2, +}; + +// Block Exception Account +typedef struct tBlockException +{ + BYTE cmd; // 1 == add, 2 == delete + char login[LOGIN_MAX_LEN + 1]; +}TPacketBlockException; + +typedef struct tChangeGuildMaster +{ + DWORD dwGuildID; + DWORD idFrom; + DWORD idTo; +} TPacketChangeGuildMaster; + +typedef struct tItemIDRange +{ + DWORD dwMin; + DWORD dwMax; + DWORD dwUsableItemIDMin; +} TItemIDRangeTable; + +typedef struct tUpdateHorseName +{ + DWORD dwPlayerID; + char szHorseName[CHARACTER_NAME_MAX_LEN + 1]; +} TPacketUpdateHorseName; + +typedef struct tDC +{ + char login[LOGIN_MAX_LEN + 1]; +} TPacketDC; + +typedef struct tNeedLoginLogInfo +{ + DWORD dwPlayerID; +} TPacketNeedLoginLogInfo; + +// ˸ ׽Ʈ Ŷ +typedef struct tItemAwardInformer +{ + char login[LOGIN_MAX_LEN + 1]; + char command[20]; //ɾ + unsigned int vnum; // +} TPacketItemAwardInfromer; +// ˸ Ŷ +typedef struct tDeleteAwardID +{ + DWORD dwID; +} TPacketDeleteAwardID; + +typedef struct SChannelStatus +{ + short nPort; + BYTE bStatus; +} TChannelStatus; + +#pragma pack() +#endif diff --git a/common/teen_packet.h b/common/teen_packet.h new file mode 100644 index 0000000..0c85886 --- /dev/null +++ b/common/teen_packet.h @@ -0,0 +1,20 @@ +/********************************************************************* + * date : 2007.06.07 + * file : teen_packet.h + * author : mhh + * description : + */ + +#ifndef _teen_packet_h_ +#define _teen_packet_h_ + +#define HEADER_GT_LOGIN 0x10 +#define HEADER_GT_LOGOUT 0x11 + + +#define HEADER_TG_TEEN_NOTICE 0x12 +#define HEADER_TG_FORCE_LOGOUT 0x13 +#define HEADER_TG_LOGIN_NOTICE 0x14 + +#endif /* _teen_packet_h_ */ + diff --git a/common/utils.h b/common/utils.h new file mode 100644 index 0000000..5a32c42 --- /dev/null +++ b/common/utils.h @@ -0,0 +1,109 @@ +/*----- atoi function -----*/ +inline bool str_to_number (bool& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (strtol(in, NULL, 10) != 0); + return true; +} + +inline bool str_to_number (char& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (char) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned char& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned char) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (short& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (short) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned short& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned short) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (int& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (int) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned int& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned int) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long) strtol(in, NULL, 10); + return true; +} + +inline bool str_to_number (unsigned long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (unsigned long) strtoul(in, NULL, 10); + return true; +} + +inline bool str_to_number (long long& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long long) strtoull(in, NULL, 10); + return true; +} + +inline bool str_to_number (float& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (float) strtof(in, NULL); + return true; +} + +inline bool str_to_number (double& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (double) strtod(in, NULL); + return true; +} + +#ifdef __FreeBSD__ +inline bool str_to_number (long double& out, const char *in) +{ + if (0==in || 0==in[0]) return false; + + out = (long double) strtold(in, NULL); + return true; +} +#endif + + +/*----- atoi function -----*/ diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt new file mode 100644 index 0000000..0f131e5 --- /dev/null +++ b/db/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.8) + +project(db CXX) + +file(GLOB_RECURSE sources + src/*.cpp src/*.h +) + + +include_directories(${PROJECT_BINARY_DIR}/src/) + +include_directories(src/) + +# Find dependencies +find_package(libmysql REQUIRED) +find_package(Boost REQUIRED) + +add_executable(${PROJECT_NAME} ${sources}) + +# Link dependencies if found +if (libmysql_FOUND) + target_link_libraries (${PROJECT_NAME} ${MYSQL_LIBRARIES}) +endif (libmysql_FOUND) + +if (Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES} ${Boost_SYSTEM_LIBRARY}) +endif (Boost_FOUND) + +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + # Pthreads + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package (Threads REQUIRED) + target_link_libraries (${PROJECT_NAME} Threads::Threads) +endif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + +target_link_libraries(${PROJECT_NAME} libpoly libsql libthecore) diff --git a/db/src/AuctionManager.cpp b/db/src/AuctionManager.cpp new file mode 100644 index 0000000..a82a24f --- /dev/null +++ b/db/src/AuctionManager.cpp @@ -0,0 +1,719 @@ +#include "stdafx.h" +#ifdef __AUCTION__ + +#include "DBManager.h" +#include "Peer.h" +#include "AuctionManager.h" + +void MyBidBoard::Boot (CPeer* peer) +{ + peer->EncodeWORD(sizeof(DWORD) + sizeof(DWORD) + sizeof(int)); + peer->EncodeWORD(Size()); + + for (TMyBidBoard::iterator pc_it = pc_map.begin(); pc_it != pc_map.end(); pc_it++) + { + TItemMap* item_map = pc_it->second; + for (TItemMap::iterator it = item_map->begin(); it != item_map->end(); it++) + { + peer->Encode(&(pc_it->first), sizeof(DWORD)); + peer->Encode(&(it->first), sizeof(DWORD)); + peer->Encode(&(it->second), sizeof(int)); + } + } +} + +size_t MyBidBoard::Size () +{ + size_t size = 0; + for (TMyBidBoard::iterator it = pc_map.begin(); it != pc_map.end(); it++) + { + size += it->second->size(); + } + return size; +} + +int MyBidBoard::GetMoney (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return -1; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return -1; + else + return it->second; +} + +bool MyBidBoard::Delete (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return false; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return false; + else + { + item_map->erase(it); + } + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM my_bid WHERE player_id = %d and item_id = %d", player_id, item_id); + CDBManager::instance().AsyncQuery(szQuery); + return true; +} + +void MyBidBoard::Insert (DWORD player_id, DWORD item_id, int money) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + TItemMap* item_map; + if (pc_it == pc_map.end()) + { + item_map = new TItemMap(); + pc_map.insert (TMyBidBoard::value_type (player_id, item_map)); + } + else + item_map = pc_it->second; + + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + { + item_map->insert (TItemMap::value_type (item_id, money)); + } + else + { + it->second = money; + } + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO my_bid VALUES (%d, %d, %d)", player_id, item_id, money); + CDBManager::instance().AsyncQuery(szQuery); +} + +AuctionManager::AuctionManager() +{ +} + +AuctionManager::~AuctionManager() +{ +} + +void AuctionManager::Initialize() +{ auction_item_cache_map.clear(); + LoadAuctionItem(); + LoadAuctionInfo(); + LoadSaleInfo(); + LoadWishInfo(); + LoadMyBidInfo(); +} + +void AuctionManager::LoadAuctionItem() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "SELECT id, owner_id, count, vnum, socket0, socket1, socket2, " + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6 " + "FROM item WHERE window = 'AUCTION'"); + + SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery); + + MYSQL_RES *res = msg->Get()->pSQLResult; + + if (!res) + { + return; + } + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + return; + } + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TPlayerItem item; + + int cur = 0; + + str_to_number(item.id, row[cur++]); + str_to_number(item.owner, row[cur++]); + item.window = AUCTION; + str_to_number(item.count, row[cur++]); + str_to_number(item.vnum, row[cur++]); + str_to_number(item.alSockets[0], row[cur++]); + str_to_number(item.alSockets[1], row[cur++]); + str_to_number(item.alSockets[2], row[cur++]); + + for (int j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) + { + str_to_number(item.aAttr[j].bType, row[cur++]); + str_to_number(item.aAttr[j].sValue, row[cur++]); + } + InsertItemCache(&item, true); + } + return; +} + +void AuctionManager::LoadAuctionInfo() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "select * from auction"); + + SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery); + + MYSQL_RES *res = msg->Get()->pSQLResult; + + if (!res) + { + return; + } + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + return; + } + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TAuctionItemInfo auctionItemInfo; + + int cur = 0; + + str_to_number(auctionItemInfo.item_num, row[cur++]); + str_to_number(auctionItemInfo.offer_price, row[cur++]); + str_to_number(auctionItemInfo.price, row[cur++]); + str_to_number(auctionItemInfo.offer_id, row[cur++]); + thecore_memcpy (auctionItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1); + cur++; + str_to_number(auctionItemInfo.empire, row[cur++]); + str_to_number(auctionItemInfo.expired_time, row[cur++]); + str_to_number(auctionItemInfo.item_id, row[cur++]); + str_to_number(auctionItemInfo.bidder_id, row[cur++]); + + InsertAuctionItemInfoCache(&auctionItemInfo, true); + } + return; +} + +void AuctionManager::LoadSaleInfo() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "select * from sale"); + + SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery); + + MYSQL_RES *res = msg->Get()->pSQLResult; + + if (!res) + { + return; + } + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + return; + } + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TSaleItemInfo saleItemInfo; + + int cur = 0; + + str_to_number(saleItemInfo.item_num, row[cur++]); + str_to_number(saleItemInfo.offer_price, row[cur++]); + str_to_number(saleItemInfo.price, row[cur++]); + str_to_number(saleItemInfo.offer_id, row[cur++]); + thecore_memcpy (saleItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1); + cur++; + str_to_number(saleItemInfo.empire, row[cur++]); + str_to_number(saleItemInfo.expired_time, row[cur++]); + str_to_number(saleItemInfo.item_id, row[cur++]); + str_to_number(saleItemInfo.wisher_id, row[cur++]); + + InsertSaleItemInfoCache(&saleItemInfo, true); + } + return; +} +void AuctionManager::LoadWishInfo() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "select * from wish"); + + SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery); + + MYSQL_RES *res = msg->Get()->pSQLResult; + + if (!res) + { + return; + } + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + return; + } + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TWishItemInfo wishItemInfo; + + int cur = 0; + + str_to_number(wishItemInfo.item_num, row[cur++]); + str_to_number(wishItemInfo.offer_price, row[cur++]); + str_to_number(wishItemInfo.price, row[cur++]); + str_to_number(wishItemInfo.offer_id, row[cur++]); + thecore_memcpy (wishItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1); + cur++; + str_to_number(wishItemInfo.empire, row[cur++]); + str_to_number(wishItemInfo.expired_time, row[cur++]); + + InsertWishItemInfoCache(&wishItemInfo, true); + } + return; +} + +void AuctionManager::LoadMyBidInfo () +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "select * from my_bid"); + + SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery); + + MYSQL_RES *res = msg->Get()->pSQLResult; + + if (!res) + { + return; + } + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + return; + } + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + + int cur = 0; + DWORD player_id; + DWORD item_id; + int money; + + str_to_number(player_id, row[cur++]); + str_to_number(item_id, row[cur++]); + str_to_number(money, row[cur++]); + + InsertMyBid (player_id, item_id, money); + } + return; +} + +inline CItemCache* AuctionManager::GetItemCache(DWORD item_id) +{ + TItemCacheMap::iterator it = auction_item_cache_map.find (item_id); + if (it == auction_item_cache_map.end()) + return NULL; + else + return it->second; +} + +void AuctionManager::Boot(CPeer* peer) +{ + peer->EncodeWORD(sizeof(TPlayerItem)); + peer->EncodeWORD(auction_item_cache_map.size()); + + itertype(auction_item_cache_map) auction_item_cache_map_it = auction_item_cache_map.begin(); + + while (auction_item_cache_map_it != auction_item_cache_map.end()) + peer->Encode((auction_item_cache_map_it++)->second->Get(), sizeof(TPlayerItem)); + + Auction.Boot(peer); + Sale.Boot(peer); + Wish.Boot(peer); + MyBid.Boot(peer); +} + +bool AuctionManager::InsertItemCache(CItemCache *item_cache, bool bSkipQuery) +{ + CItemCache* c = GetItemCache (item_cache->Get(false)->id); + if (c != NULL) + { + return false; + } + auction_item_cache_map.insert(TItemCacheMap::value_type(item_cache->Get(true)->id, item_cache)); + item_cache->OnFlush(); + return true; +} + +bool AuctionManager::InsertItemCache(TPlayerItem * pNew, bool bSkipQuery) +{ + CItemCache* c = GetItemCache (pNew->id); + if (c != NULL) + { + return false; + } + + c = new CItemCache(); + + c->Put(pNew, bSkipQuery); + + auction_item_cache_map.insert(TItemCacheMap::value_type(pNew->id, c)); + c->Flush(); + return true; +} + +bool AuctionManager::DeleteItemCache(DWORD item_id) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + return false; + } + + c->Delete(); + + return true; +} + +AuctionResult AuctionManager::EnrollInAuction(CItemCache* item_cache, TAuctionItemInfo &item_info) +{ + CItemCache* c = GetItemCache (item_info.item_id); + if (c != NULL) + { + sys_err ("item id : %d is already in AuctionManager", item_info.item_id); + return AUCTION_FAIL; + } + + if (!Auction.InsertItemInfo (&item_info)) + { + sys_err ("item id : %d is already in AuctionBoard", item_info.item_id); + return AUCTION_FAIL; + } + + item_cache->Get()->window = AUCTION; + item_cache->Get()->pos = 9999999; + + InsertItemCache (item_cache); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::EnrollInSale(CItemCache* item_cache, TSaleItemInfo &item_info) +{ + CItemCache* c = GetItemCache (item_info.item_id); + if (c != NULL) + { + sys_err ("item id : %d is already in AuctionManager", item_info.item_id); + return AUCTION_FAIL; + } + + if (!Wish.GetItemInfoCache (WishBoard::Key (item_info.item_num, item_info.wisher_id))) + { + sys_err ("item_num : %d, wisher_id : %d is not in wish auction.", item_info.item_num, item_info.wisher_id); + return AUCTION_FAIL; + } + + if (!Sale.InsertItemInfo (&item_info)) + { + sys_err ("item id : %d is already in SaleBoard", item_info.item_id); + return AUCTION_FAIL; + } + + item_cache->Get()->window = AUCTION; + item_cache->Get()->pos = 999999; + + InsertItemCache (item_cache); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::EnrollInWish(TWishItemInfo &item_info) +{ + if (!Wish.InsertItemInfo (&item_info)) + { + sys_err ("wisher_id : %d, item_num : %d is already in WishBoard", item_info.offer_id, item_info.item_num); + return AUCTION_FAIL; + } + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::Bid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + if (MyBid.GetMoney (bidder_id, item_id) != 0) + { + return AUCTION_ALREADY_IN; + } + + CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id); + TAuctionItemInfo* item_info = item_cache->Get(false); + + if (item_info->is_expired()) + { + return AUCTION_EXPIRED; + } + + if ((double)bid_price < (double)item_info->get_bid_price() * 1.05) + { + return AUCTION_NOT_ENOUGH_MONEY; + } + + item_info->set_bid_price(bid_price); + item_info->bidder_id = bidder_id; + item_info->set_bidder_name (bidder_name); + item_cache->OnFlush(); + + InsertMyBid (bidder_id, item_id, bid_price); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::Impur(DWORD purchaser_id, const char* purchaser_name, DWORD item_id) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id); + TAuctionItemInfo* item_info = item_cache->Get(false); + + if (item_info->is_expired()) + { + return AUCTION_EXPIRED; + } + + // ﱸ عǷ, Ŵ . + item_info->expired_time = 0; + item_info->bidder_id = purchaser_id; + item_info->set_bidder_name (purchaser_name); + item_info->set_bid_price (item_info->get_impur_price()); + item_cache->OnFlush(); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::GetAuctionedItem (DWORD actor_id, DWORD item_id, TPlayerItem& item) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + CAuctionItemInfoCache* item_info_cache = Auction.GetItemInfoCache(item_id); + if (item_info_cache == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + + TAuctionItemInfo* item_info = item_info_cache->Get(false); + + if (!item_info->is_expired()) + { + return AUCTION_NOT_EXPIRED; + } + + thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem)); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::BuySoldItem (DWORD actor_id, DWORD item_id, TPlayerItem& item) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + CSaleItemInfoCache* item_info_cache = Sale.GetItemInfoCache(item_id); + if (item_info_cache == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + + TSaleItemInfo* item_info = item_info_cache->Get(false); + + thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem)); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::CancelAuction (DWORD actor_id, DWORD item_id, TPlayerItem& item) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + CAuctionItemInfoCache* item_info_cache = Auction.GetItemInfoCache(item_id); + if (item_info_cache == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + TAuctionItemInfo* item_info = item_info_cache->Get(false); + + thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem)); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::CancelWish (DWORD actor_id, DWORD item_num) +{ + if (!Wish.DeleteItemInfoCache (WishBoard::Key (actor_id, item_num))) + { + return AUCTION_FAIL; + } + else + { + return AUCTION_SUCCESS; + } +} + +AuctionResult AuctionManager::CancelSale (DWORD actor_id, DWORD item_id, TPlayerItem& item) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + CSaleItemInfoCache* item_info_cache = Sale.GetItemInfoCache(item_id); + if (item_info_cache == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + TSaleItemInfo* item_info = item_info_cache->Get(false); + + thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem)); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::DeleteAuctionItem (DWORD actor_id, DWORD item_id) +{ + if (DeleteItemCache (item_id) == false) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + if (Auction.DeleteItemInfoCache (item_id) == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::DeleteSaleItem (DWORD actor_id, DWORD item_id) +{ + if (DeleteItemCache (item_id) == false) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + if (Sale.DeleteItemInfoCache (item_id) == NULL) + { + sys_err ("how can this accident happen?"); + return AUCTION_FAIL; + } + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::ReBid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price) +{ + CItemCache* c = GetItemCache (item_id); + if (c == NULL) + { + sys_err ("item id : %d does not exist in auction.", item_id); + return AUCTION_FAIL; + } + + int money = MyBid.GetMoney (bidder_id, item_id); + if (money == -1) + { + return AUCTION_NOT_IN; + } + + CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id); + TAuctionItemInfo* item_info = item_cache->Get(false); + + if (item_info->is_expired()) + { + return AUCTION_EXPIRED; + } + + if ((double)(bid_price + money) < (double)item_info->get_bid_price() * 1.05) + { + return AUCTION_NOT_ENOUGH_MONEY; + } + + item_info->set_bid_price(bid_price + money); + item_info->bidder_id = bidder_id; + item_info->set_bidder_name (bidder_name); + item_cache->OnFlush(); + + InsertMyBid (bidder_id, item_id, money + bid_price); + + return AUCTION_SUCCESS; +} + +AuctionResult AuctionManager::BidCancel (DWORD bidder_id, DWORD item_id) +{ + if (MyBid.Delete (bidder_id, item_id)) + { + return AUCTION_SUCCESS; + } + else + { + return AUCTION_NOT_IN; + } +} +#endif \ No newline at end of file diff --git a/db/src/AuctionManager.h b/db/src/AuctionManager.h new file mode 100644 index 0000000..76f65f6 --- /dev/null +++ b/db/src/AuctionManager.h @@ -0,0 +1,370 @@ +#ifdef __AUCTION__ + +#ifndef __INC_AUCTION_MANAGER_H__ +#define __INC_AUCTION_MANAGER_H__ + +#include +#include "Cache.h" +#include "../../common/auction_table.h" + +class CItemCache; +class CAuctionItemInfoCache; +class CSaleItemInfoCache; +class CWishItemInfoCache; + +template<> +class hash > +{ // hash functor +public: + typedef std::pair _Kty; + + size_t operator()(const _Kty& _Keyval) const + { // hash _Keyval to size_t value by pseudorandomizing transform + ldiv_t _Qrem = ldiv((size_t)_Keyval.first + (size_t)_Keyval.second, 127773); + + _Qrem.rem = 16807 * _Qrem.rem - 2836 * _Qrem.quot; + if (_Qrem.rem < 0) + _Qrem.rem += 2147483647; + return ((size_t)_Qrem.rem); + } +}; + + +class AuctionBoard +{ +public: + typedef DWORD Key; + typedef CAuctionItemInfoCache ItemInfoCache; + typedef TAuctionItemInfo ItemInfo; + + +public: + AuctionBoard() {} + ~AuctionBoard() {} + + void Boot(CPeer* peer) + { + peer->EncodeWORD(sizeof(ItemInfo)); + peer->EncodeWORD(item_cache_map.size()); + + TItemInfoCacheMap::iterator it = item_cache_map.begin(); + + while (it != item_cache_map.end()) + peer->Encode((it++)->second->Get(), sizeof(ItemInfo)); + } + + size_t Size() + { + return item_cache_map.size(); + } + + ItemInfoCache* GetItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return NULL; + else + return it->second; + } + + bool DeleteItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return false; + else + { + it->second->Delete(); + item_cache_map.erase(it); + return true; + } + } + + bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false) + { + ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_id)); + if (c != NULL) + { + return false; + } + + c = new ItemInfoCache(); + + c->Put(pNew, bSkipQuery); + + item_cache_map.insert(TItemInfoCacheMap::value_type(pNew->item_id, c)); + c->Flush(); + + return true; + } +private: + typedef boost::unordered_map TItemInfoCacheMap; + TItemInfoCacheMap item_cache_map; +}; + +class SaleBoard +{ +public: + typedef DWORD Key; + typedef CSaleItemInfoCache ItemInfoCache; + typedef TSaleItemInfo ItemInfo; + + SaleBoard() {} + ~SaleBoard() {} + + void Boot(CPeer* peer) + { + peer->EncodeWORD(sizeof(ItemInfo)); + peer->EncodeWORD(item_cache_map.size()); + + TItemInfoCacheMap::iterator it = item_cache_map.begin(); + + while (it != item_cache_map.end()) + peer->Encode((it++)->second->Get(), sizeof(ItemInfo)); + } + + size_t Size() + { + return item_cache_map.size(); + } + + ItemInfoCache* GetItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return NULL; + else + return it->second; + } + + bool DeleteItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return false; + else + { + it->second->Delete(); + item_cache_map.erase(it); + return true; + } + } + + bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false) + { + ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_id)); + if (c != NULL) + { + return false; + } + + c = new ItemInfoCache(); + + c->Put(pNew, bSkipQuery); + + item_cache_map.insert(TItemInfoCacheMap::value_type(pNew->item_id, c)); + c->Flush(); + + return true; + } + +private: + typedef boost::unordered_map TItemInfoCacheMap; + TItemInfoCacheMap item_cache_map; +}; + +class WishBoard +{ +public: + typedef std::pair Key; + typedef CWishItemInfoCache ItemInfoCache; + typedef TWishItemInfo ItemInfo; + + WishBoard() {} + virtual ~WishBoard() {} + + void Boot(CPeer* peer) + { + peer->EncodeWORD(sizeof(ItemInfo)); + peer->EncodeWORD(item_cache_map.size()); + + TItemInfoCacheMap::iterator it = item_cache_map.begin(); + + while (it != item_cache_map.end()) + peer->Encode((it++)->second->Get(), sizeof(ItemInfo)); + } + + size_t Size() + { + return item_cache_map.size(); + } + + ItemInfoCache* GetItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return NULL; + else + return it->second; + } + + bool DeleteItemInfoCache (Key key) + { + TItemInfoCacheMap::iterator it = item_cache_map.find (key); + if (it == item_cache_map.end()) + return false; + else + { + it->second->Delete(); + item_cache_map.erase(it); + return true; + } + } + + bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false) + { + ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_num, pNew->offer_id)); + if (c != NULL) + { + return false; + } + + c = new ItemInfoCache(); + + c->Put(pNew, bSkipQuery); + + item_cache_map.insert(TItemInfoCacheMap::value_type(Key (pNew->item_num, pNew->offer_id), c)); + c->Flush(); + + return true; + } +private: + typedef boost::unordered_map TItemInfoCacheMap; + TItemInfoCacheMap item_cache_map; +}; + +// pc ߴ Ÿ . +class MyBidBoard +{ +public: + MyBidBoard() {} + ~MyBidBoard() {} + + void Boot(CPeer* peer); + size_t Size(); + + int GetMoney (DWORD player_id, DWORD item_id); + bool Delete (DWORD player_id, DWORD item_id); + // ̹ . + void Insert (DWORD player_id, DWORD item_id, int money); + +private: + typedef std::map TItemMap; + typedef boost::unordered_map TMyBidBoard; + TMyBidBoard pc_map; +}; + +class AuctionManager : public singleton +{ +private: + // auction ϵ ۵. + typedef boost::unordered_map TItemCacheMap; + TItemCacheMap auction_item_cache_map; + + // auction ϵ , ̺ Ե ʴ ϴ ͵ + AuctionBoard Auction; + SaleBoard Sale; + WishBoard Wish; + MyBidBoard MyBid; + +public: + AuctionManager(); + ~AuctionManager(); + + void Initialize (); + void LoadAuctionItem (); + + void LoadAuctionInfo (); + void LoadSaleInfo (); + void LoadWishInfo (); + void LoadMyBidInfo (); + + void Boot(CPeer* peer); + + bool InsertItemCache (CItemCache *item_cache, bool bSkipQuery = false); + bool InsertItemCache (TPlayerItem * pNew, bool bSkipQuery = false); + bool DeleteItemCache (DWORD item_id); + CItemCache* GetItemCache (DWORD item_id); + + size_t GetAuctionItemSize() + { + return auction_item_cache_map.size(); + } + size_t GetAuctionSize() + { + return Auction.Size(); + } + size_t GetSaleSize() + { + return Sale.Size(); + } + size_t GetWishSize() + { + return Wish.Size(); + } + size_t GetMyBidSize() + { + return MyBid.Size(); + } + + void InsertAuctionItemInfoCache (TAuctionItemInfo *pNew, bool bSkipQuery = false) + { + Auction.InsertItemInfo (pNew, bSkipQuery); + } + CAuctionItemInfoCache* GetAuctionItemInfoCache (DWORD item_id) + { + return Auction.GetItemInfoCache(item_id); + } + + void InsertSaleItemInfoCache (TSaleItemInfo *pNew, bool bSkipQuery = false) + { + Sale.InsertItemInfo (pNew, bSkipQuery); + } + CSaleItemInfoCache* GetSaleItemInfoCache (DWORD item_id) + { + return Sale.GetItemInfoCache(item_id); + } + + void InsertWishItemInfoCache (TWishItemInfo *pNew, bool bSkipQuery = false) + { + Wish.InsertItemInfo (pNew, bSkipQuery); + } + CWishItemInfoCache* GetWishItemInfoCache (DWORD item_id, DWORD wisher_id) + { + return Wish.GetItemInfoCache(WishBoard::Key (item_id, wisher_id)); + } + + void InsertMyBid (DWORD player_id, DWORD item_id, DWORD money) + { + MyBid.Insert (player_id, item_id, money); + } + + AuctionResult EnrollInAuction(CItemCache* item_cache, TAuctionItemInfo &item_info); + AuctionResult EnrollInSale(CItemCache* item_cache, TSaleItemInfo &item_info); + AuctionResult EnrollInWish(TWishItemInfo &item_info); + AuctionResult Bid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price); + AuctionResult Impur(DWORD purchaser_id, const char* purchaser_name, DWORD item_id); + AuctionResult GetAuctionedItem (DWORD actor_id, DWORD item_id, TPlayerItem& item); + AuctionResult BuySoldItem (DWORD actor_id, DWORD item_id, TPlayerItem& item); + AuctionResult CancelAuction (DWORD actor_id, DWORD item_id, TPlayerItem& item); + AuctionResult CancelWish (DWORD actor_id, DWORD item_num); + AuctionResult CancelSale (DWORD actor_id, DWORD item_id, TPlayerItem& item); + AuctionResult DeleteAuctionItem (DWORD actor_id, DWORD item_id); + AuctionResult DeleteSaleItem (DWORD actor_id, DWORD item_id); + AuctionResult ReBid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price); + AuctionResult BidCancel (DWORD bidder_id, DWORD item_id); +}; + +#endif + +#endif \ No newline at end of file diff --git a/db/src/BlockCountry.cpp b/db/src/BlockCountry.cpp new file mode 100644 index 0000000..d28cc52 --- /dev/null +++ b/db/src/BlockCountry.cpp @@ -0,0 +1,214 @@ +// vim:ts=4 sw=4 +/********************************************************************* + * date : 2007.05.31 + * file : BlockCountry.cpp + * author : mhh + * description : + */ + +#include "stdafx.h" + +#include "BlockCountry.h" + +#include "DBManager.h" + +#define DO_ALL_BLOCK_IP(iter) \ + for ((iter) = m_block_ip.begin(); (iter) != m_block_ip.end(); ++(iter)) + +#define DO_ALL_BLOCK_EXCEPTION(iter) \ + for ((iter) = m_block_exception.begin(); (iter) != m_block_exception.end(); ++(iter)) + +CBlockCountry::CBlockCountry() +{ + + + +} + +CBlockCountry::~CBlockCountry() +{ + BLOCK_IP *block_ip; + BLOCK_IP_VECTOR::iterator iter; + + DO_ALL_BLOCK_IP(iter) + { + block_ip = *iter; + delete block_ip; + } + + m_block_ip.clear(); +} + + +bool CBlockCountry::Load() +{ + // load blocked ip + { + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT IP_FROM, IP_TO, COUNTRY_NAME FROM iptocountry"); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_ACCOUNT); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err(" DirectQuery failed(%s)", szQuery); + delete pMsg; + return false; + } + + MYSQL_ROW row; + for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + { + BLOCK_IP *block_ip = new BLOCK_IP; + block_ip->ip_from = strtoul(row[0], NULL, 10); + block_ip->ip_to = strtoul(row[1], NULL, 10); + strlcpy(block_ip->country, row[2], sizeof(block_ip->country)); + + m_block_ip.push_back(block_ip); + sys_log(0, "BLOCKED_IP : %u - %u", block_ip->ip_from, block_ip->ip_to); + + } + delete pMsg; + } + + + // load block exception account + { + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT login FROM block_exception"); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_ACCOUNT); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err(" DirectQuery failed(%s)", szQuery); + delete pMsg; + return true; + } + + MYSQL_ROW row; + for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + { + const char *login = row[0]; + + m_block_exception.push_back(strdup(login)); + + sys_log(0, "BLOCK_EXCEPTION = %s", login); + + } + delete pMsg; + } + + return true; +} + +bool CBlockCountry::IsBlockedCountryIp(const char *user_ip) +{ + BLOCK_IP* block_ip; + BLOCK_IP_VECTOR::iterator iter; + struct in_addr st_addr; + +#ifndef __WIN32__ + if (0 == inet_aton(user_ip, &st_addr)) +#else + unsigned long in_address; + in_address = inet_addr(user_ip); + st_addr.s_addr = in_address; + if (INADDR_NONE == in_address) +#endif + return true; // ǰ ϴ ϴ ó + + DO_ALL_BLOCK_IP(iter) + { + block_ip = *iter; + + if (st_addr.s_addr >= block_ip->ip_from && st_addr.s_addr <= block_ip->ip_to) + return true; + } + + return false; +} + +void CBlockCountry::SendBlockedCountryIp(CPeer *peer) +{ + sys_log(0, "SendBlockedCountryIp start"); + BLOCK_IP *block_ip; + BLOCK_IP_VECTOR::iterator iter; + TPacketBlockCountryIp packet; + + DO_ALL_BLOCK_IP(iter) + { + block_ip = *iter; + + packet.ip_from = block_ip->ip_from; + packet.ip_to = block_ip->ip_to; + + peer->EncodeHeader(HEADER_DG_BLOCK_COUNTRY_IP, 0, sizeof(TPacketBlockCountryIp)); + peer->Encode(&packet, sizeof(packet)); + } + + sys_log(0, "[DONE] CBlockCountry::SendBlockedCountryIp() : count = %d", + m_block_ip.size()); + sys_log(0, "SendBlockedCountryIp end"); +} /* end of CBlockCountry::SendBlockedCountryIp() */ + + +void CBlockCountry::SendBlockException(CPeer *peer) +{ + BLOCK_EXCEPTION_VECTOR::iterator iter; + + DO_ALL_BLOCK_EXCEPTION(iter) + { + const char *login = *iter; + + this->SendBlockExceptionOne(peer, login, BLOCK_EXCEPTION_CMD_ADD); + } +} /* end of CBlockCountry::SendBlockException() */ + +void CBlockCountry::SendBlockExceptionOne(CPeer *peer, const char *login, BYTE cmd) +{ + if (NULL == peer || NULL == login) + return; + + if (BLOCK_EXCEPTION_CMD_ADD != cmd && BLOCK_EXCEPTION_CMD_DEL != cmd) + return; + + TPacketBlockException packet; + + packet.cmd = cmd; + strlcpy(packet.login, login, sizeof(packet.login)); + + peer->EncodeHeader(HEADER_DG_BLOCK_EXCEPTION, 0, sizeof(TPacketBlockException)); + peer->Encode(&packet, sizeof(packet)); +} + +void CBlockCountry::AddBlockException(const char *login) +{ + BLOCK_EXCEPTION_VECTOR::iterator iter; + DO_ALL_BLOCK_EXCEPTION(iter) + { + const char *saved_login = *iter; + + if (!strcmp(saved_login, login)) + return; + } + + m_block_exception.push_back(strdup(login)); + return; +} + +void CBlockCountry::DelBlockException(const char *login) +{ + BLOCK_EXCEPTION_VECTOR::iterator iter; + DO_ALL_BLOCK_EXCEPTION(iter) + { + const char *saved_login = *iter; + + if (!strcmp(saved_login, login)) + { + ::free((void*)saved_login); + m_block_exception.erase(iter); + return; + } + } + return; +} + diff --git a/db/src/BlockCountry.h b/db/src/BlockCountry.h new file mode 100644 index 0000000..7e41e41 --- /dev/null +++ b/db/src/BlockCountry.h @@ -0,0 +1,44 @@ +// vim: ts=4 sw=4 +// Date : 2007.05.31 +// File : BlockCountry.h +// Author : mhh +// Description : + +#ifndef __INC_METIN_II_BLOCKCOUNTRY_H__ +#define __INC_METIN_II_BLOCKCOUNTRY_H__ + +#include "Peer.h" + +#define MAX_COUNTRY_NAME_LENGTH 50 + +class CBlockCountry : public singleton +{ + private: + struct BLOCK_IP + { + DWORD ip_from; + DWORD ip_to; + char country[MAX_COUNTRY_NAME_LENGTH + 1]; + }; + + typedef std::vector BLOCK_IP_VECTOR; + BLOCK_IP_VECTOR m_block_ip; + + typedef std::vector BLOCK_EXCEPTION_VECTOR; + BLOCK_EXCEPTION_VECTOR m_block_exception; + + public: + CBlockCountry(); + ~CBlockCountry(); + + public: + bool Load(); + bool IsBlockedCountryIp(const char *user_ip); + void SendBlockedCountryIp(CPeer *peer); + void SendBlockException(CPeer *peer); + void SendBlockExceptionOne(CPeer *peer, const char *login, BYTE cmd); + void AddBlockException(const char *login); + void DelBlockException(const char *login); +}; + +#endif diff --git a/db/src/Cache.cpp b/db/src/Cache.cpp new file mode 100644 index 0000000..3420e54 --- /dev/null +++ b/db/src/Cache.cpp @@ -0,0 +1,365 @@ + +#include "stdafx.h" +#include "Cache.h" + +#include "QID.h" +#include "ClientManager.h" +#ifdef __AUCTION__ +#include "AuctionManager.h" +#endif +#include "Main.h" + +extern CPacketInfo g_item_info; +extern int g_iPlayerCacheFlushSeconds; +extern int g_iItemCacheFlushSeconds; +extern int g_test_server; +// MYSHOP_PRICE_LIST +extern int g_iItemPriceListTableCacheFlushSeconds; +// END_OF_MYSHOP_PRICE_LIST +// +extern int g_item_count; +const int auctionMinFlushSec = 1800; + +CItemCache::CItemCache() +{ + m_expireTime = MIN(1800, g_iItemCacheFlushSeconds); +} + +CItemCache::~CItemCache() +{ +} + +// ̰ ̻ѵ... +// Delete , Cache ؾ ϴ° ƴѰ??? +// ٵ Cache ϴ κ . +// ã ǰ? +// ̷ س, ð ... +// ̹ ε... Ȯλ?????? +// fixme +// by rtsummit +void CItemCache::Delete() +{ + if (m_data.vnum == 0) + return; + + //char szQuery[QUERY_MAX_LEN]; + //szQuery[QUERY_MAX_LEN] = '\0'; + if (g_test_server) + sys_log(0, "ItemCache::Delete : DELETE %u", m_data.id); + + m_data.vnum = 0; + m_bNeedQuery = true; + m_lastUpdateTime = time(0); + OnFlush(); + + //m_bNeedQuery = false; + //m_lastUpdateTime = time(0) - m_expireTime; // ٷ ŸӾƿ ǵ . +} + +void CItemCache::OnFlush() +{ + if (m_data.vnum == 0) // vnum 0̸ ϶ ǥõ ̴. + { + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM item%s WHERE id=%u", GetTablePostfix(), m_data.id); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, 0, NULL); + + if (g_test_server) + sys_log(0, "ItemCache::Flush : DELETE %u %s", m_data.id, szQuery); + } + else + { + long alSockets[ITEM_SOCKET_MAX_NUM]; + TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM]; + bool isSocket = false, isAttr = false; + + memset(&alSockets, 0, sizeof(long) * ITEM_SOCKET_MAX_NUM); + memset(&aAttr, 0, sizeof(TPlayerItemAttribute) * ITEM_ATTRIBUTE_MAX_NUM); + + TPlayerItem * p = &m_data; + + if (memcmp(alSockets, p->alSockets, sizeof(long) * ITEM_SOCKET_MAX_NUM)) + isSocket = true; + + if (memcmp(aAttr, p->aAttr, sizeof(TPlayerItemAttribute) * ITEM_ATTRIBUTE_MAX_NUM)) + isAttr = true; + + char szColumns[QUERY_MAX_LEN]; + char szValues[QUERY_MAX_LEN]; + char szUpdate[QUERY_MAX_LEN]; + + int iLen = snprintf(szColumns, sizeof(szColumns), "id, owner_id, window, pos, count, vnum"); + + int iValueLen = snprintf(szValues, sizeof(szValues), "%u, %u, %d, %d, %u, %u", + p->id, p->owner, p->window, p->pos, p->count, p->vnum); + + int iUpdateLen = snprintf(szUpdate, sizeof(szUpdate), "owner_id=%u, window=%d, pos=%d, count=%u, vnum=%u", + p->owner, p->window, p->pos, p->count, p->vnum); + + if (isSocket) + { + iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen, ", socket0, socket1, socket2"); + iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen, + ", %lu, %lu, %lu", p->alSockets[0], p->alSockets[1], p->alSockets[2]); + iUpdateLen += snprintf(szUpdate + iUpdateLen, sizeof(szUpdate) - iUpdateLen, + ", socket0=%lu, socket1=%lu, socket2=%lu", p->alSockets[0], p->alSockets[1], p->alSockets[2]); + } + + if (isAttr) + { + iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen, + ", attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3" + ", attrtype4, attrvalue4, attrtype5, attrvalue5, attrtype6, attrvalue6"); + + iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen, + ", %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + p->aAttr[0].bType, p->aAttr[0].sValue, + p->aAttr[1].bType, p->aAttr[1].sValue, + p->aAttr[2].bType, p->aAttr[2].sValue, + p->aAttr[3].bType, p->aAttr[3].sValue, + p->aAttr[4].bType, p->aAttr[4].sValue, + p->aAttr[5].bType, p->aAttr[5].sValue, + p->aAttr[6].bType, p->aAttr[6].sValue); + + iUpdateLen += snprintf(szUpdate + iUpdateLen, sizeof(szUpdate) - iUpdateLen, + ", attrtype0=%d, attrvalue0=%d" + ", attrtype1=%d, attrvalue1=%d" + ", attrtype2=%d, attrvalue2=%d" + ", attrtype3=%d, attrvalue3=%d" + ", attrtype4=%d, attrvalue4=%d" + ", attrtype5=%d, attrvalue5=%d" + ", attrtype6=%d, attrvalue6=%d", + p->aAttr[0].bType, p->aAttr[0].sValue, + p->aAttr[1].bType, p->aAttr[1].sValue, + p->aAttr[2].bType, p->aAttr[2].sValue, + p->aAttr[3].bType, p->aAttr[3].sValue, + p->aAttr[4].bType, p->aAttr[4].sValue, + p->aAttr[5].bType, p->aAttr[5].sValue, + p->aAttr[6].bType, p->aAttr[6].sValue); + } + + char szItemQuery[QUERY_MAX_LEN + QUERY_MAX_LEN]; + snprintf(szItemQuery, sizeof(szItemQuery), "REPLACE INTO item%s (%s) VALUES(%s)", GetTablePostfix(), szColumns, szValues); + + if (g_test_server) + sys_log(0, "ItemCache::Flush :REPLACE (%s)", szItemQuery); + + CDBManager::instance().ReturnQuery(szItemQuery, QID_ITEM_SAVE, 0, NULL); + + //g_item_info.Add(p->vnum); + ++g_item_count; + } + + m_bNeedQuery = false; +} + +// +// CPlayerTableCache +// +CPlayerTableCache::CPlayerTableCache() +{ + m_expireTime = MIN(1800, g_iPlayerCacheFlushSeconds); +} + +CPlayerTableCache::~CPlayerTableCache() +{ +} + +void CPlayerTableCache::OnFlush() +{ + if (g_test_server) + sys_log(0, "PlayerTableCache::Flush : %s", m_data.name); + + char szQuery[QUERY_MAX_LEN]; + CreatePlayerSaveQuery(szQuery, sizeof(szQuery), &m_data); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_SAVE, 0, NULL); +} + +// MYSHOP_PRICE_LIST +// +// CItemPriceListTableCache class implementation +// + +const int CItemPriceListTableCache::s_nMinFlushSec = 1800; + +CItemPriceListTableCache::CItemPriceListTableCache() +{ + m_expireTime = MIN(s_nMinFlushSec, g_iItemPriceListTableCacheFlushSeconds); +} + +void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList) +{ + // + // ̹ ij̵ ۰ ߺ ã ߺ ʴ tmpvec ִ´. + // + + std::vector tmpvec; + + for (uint idx = 0; idx < m_data.byCount; ++idx) + { + const TItemPriceInfo* pos = pUpdateList->aPriceInfo; + for (; pos != pUpdateList->aPriceInfo + pUpdateList->byCount && m_data.aPriceInfo[idx].dwVnum != pos->dwVnum; ++pos) + ; + + if (pos == pUpdateList->aPriceInfo + pUpdateList->byCount) + tmpvec.push_back(m_data.aPriceInfo[idx]); + } + + // + // pUpdateList m_data ϰ tmpvec տ ŭ Ѵ. + // + + if (pUpdateList->byCount > SHOP_PRICELIST_MAX_NUM) + { + sys_err("Count overflow!"); + return; + } + + m_data.byCount = pUpdateList->byCount; + + thecore_memcpy(m_data.aPriceInfo, pUpdateList->aPriceInfo, sizeof(TItemPriceInfo) * pUpdateList->byCount); + + int nDeletedNum; // + + if (pUpdateList->byCount < SHOP_PRICELIST_MAX_NUM) + { + size_t sizeAddOldDataSize = SHOP_PRICELIST_MAX_NUM - pUpdateList->byCount; + + if (tmpvec.size() < sizeAddOldDataSize) + sizeAddOldDataSize = tmpvec.size(); + + thecore_memcpy(m_data.aPriceInfo + pUpdateList->byCount, &tmpvec[0], sizeof(TItemPriceInfo) * sizeAddOldDataSize); + m_data.byCount += sizeAddOldDataSize; + + nDeletedNum = tmpvec.size() - sizeAddOldDataSize; + } + else + nDeletedNum = tmpvec.size(); + + m_bNeedQuery = true; + + sys_log(0, + "ItemPriceListTableCache::UpdateList : OwnerID[%u] Update [%u] Items, Delete [%u] Items, Total [%u] Items", + m_data.dwOwnerID, pUpdateList->byCount, nDeletedNum, m_data.byCount); +} + +void CItemPriceListTableCache::OnFlush() +{ + char szQuery[QUERY_MAX_LEN]; + + // + // ij ڿ DB Ѵ. + // + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM myshop_pricelist%s WHERE owner_id = %u", GetTablePostfix(), m_data.dwOwnerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_DESTROY, 0, NULL); + + // + // ij DB . + // + + for (int idx = 0; idx < m_data.byCount; ++idx) + { + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO myshop_pricelist%s(owner_id, item_vnum, price) VALUES(%u, %u, %u)", + GetTablePostfix(), m_data.dwOwnerID, m_data.aPriceInfo[idx].dwVnum, m_data.aPriceInfo[idx].dwPrice); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_SAVE, 0, NULL); + } + + sys_log(0, "ItemPriceListTableCache::Flush : OwnerID[%u] Update [%u]Items", m_data.dwOwnerID, m_data.byCount); + + m_bNeedQuery = false; +} +// END_OF_MYSHOP_PRICE_LIST +#ifdef __AUCTION__ +CAuctionItemInfoCache::CAuctionItemInfoCache() +{ + m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds); +} + +CAuctionItemInfoCache::~CAuctionItemInfoCache() +{ + +} + +void CAuctionItemInfoCache::Delete() +{ + if (m_data.item_num == 0) + return; + + if (g_test_server) + sys_log(0, "CAuctionItemInfoCache::Delete : DELETE %u", m_data.item_id); + + m_data.item_num = 0; + m_bNeedQuery = true; + m_lastUpdateTime = time(0); + OnFlush(); + delete this; +} + +void CAuctionItemInfoCache::OnFlush() +{ + char szQuery[QUERY_MAX_LEN]; + + if (m_data.item_num == 0) + { + snprintf(szQuery, sizeof(szQuery), "DELETE FROM auction where item_id = %d", m_data.item_id); + CDBManager::instance().AsyncQuery(szQuery); + } + else + { + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO auction VALUES (%u, %d, %d, %u, \"%s\", %u, %u, %u, %u)", + m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time, + m_data.item_id, m_data.bidder_id); + + CDBManager::instance().AsyncQuery(szQuery); + } +} + +CSaleItemInfoCache::CSaleItemInfoCache() +{ + m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds); +} + +CSaleItemInfoCache::~CSaleItemInfoCache() +{ +} + +void CSaleItemInfoCache::Delete() +{ +} + +void CSaleItemInfoCache::OnFlush() +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO sale VALUES (%u, %d, %d, %u, \"%s\", %u, %u, %u, %u)", + m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time, + m_data.item_id, m_data.wisher_id); + + CDBManager::instance().AsyncQuery(szQuery); +} + +CWishItemInfoCache::CWishItemInfoCache() +{ + m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds); +} + +CWishItemInfoCache::~CWishItemInfoCache() +{ +} + +void CWishItemInfoCache::Delete() +{ +} + +void CWishItemInfoCache::OnFlush() +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO wish VALUES (%u, %d, %d, %u, \"%s\", %u, %d)", + m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time); + + CDBManager::instance().AsyncQuery(szQuery); +} +#endif \ No newline at end of file diff --git a/db/src/Cache.h b/db/src/Cache.h new file mode 100644 index 0000000..2548143 --- /dev/null +++ b/db/src/Cache.h @@ -0,0 +1,97 @@ +// vim:ts=8 sw=4 +#ifndef __INC_DB_CACHE_H__ +#define __INC_DB_CACHE_H__ + +#include "../../common/cache.h" +#include "../../common/auction_table.h" + +class CItemCache : public cache +{ + public: + CItemCache(); + virtual ~CItemCache(); + + void Delete(); + virtual void OnFlush(); +}; + +class CPlayerTableCache : public cache +{ + public: + CPlayerTableCache(); + virtual ~CPlayerTableCache(); + + virtual void OnFlush(); + + DWORD GetLastUpdateTime() { return m_lastUpdateTime; } +}; + +// MYSHOP_PRICE_LIST +/** + * @class CItemPriceListTableCache + * @brief λ Ʈ ij class + * @version 05/06/10 Bang2ni - First release. + */ +class CItemPriceListTableCache : public cache< TItemPriceListTable > +{ + public: + + /// Constructor + /** + * ij ð Ѵ. + */ + CItemPriceListTableCache(void); + + /// Ʈ + /** + * @param [in] pUpdateList Ʈ + * + * ijõ Ѵ. + * Ʈ á ij̵ ڿ Ѵ. + */ + void UpdateList(const TItemPriceListTable* pUpdateList); + + /// DB Ѵ. + virtual void OnFlush(void); + + private: + + static const int s_nMinFlushSec; ///< Minimum cache expire time +}; +// END_OF_MYSHOP_PRICE_LIST +#ifdef __AUCTION__ + +class CAuctionItemInfoCache : public cache +{ +public: + typedef TWishItemInfo value_type; + CAuctionItemInfoCache(); + virtual ~CAuctionItemInfoCache(); + + void Delete(); + virtual void OnFlush(); +}; + +class CSaleItemInfoCache : public cache +{ +public: + typedef TWishItemInfo value_type; + CSaleItemInfoCache(); + virtual ~CSaleItemInfoCache(); + + void Delete(); + virtual void OnFlush(); +}; + +class CWishItemInfoCache : public cache +{ +public: + typedef TWishItemInfo value_type; + CWishItemInfoCache(); + virtual ~CWishItemInfoCache(); + + void Delete(); + virtual void OnFlush(); +}; +#endif +#endif diff --git a/db/src/ClientManager.cpp b/db/src/ClientManager.cpp new file mode 100644 index 0000000..223e53f --- /dev/null +++ b/db/src/ClientManager.cpp @@ -0,0 +1,4984 @@ + +#include "stdafx.h" + +#include "../../common/billing.h" +#include "../../common/building.h" +#include "../../common/VnumHelper.h" +#include "../../libgame/include/grid.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" +#include "GuildManager.h" +#include "PrivManager.h" +#include "MoneyLog.h" +#include "ItemAwardManager.h" +#include "Marriage.h" +#include "Monarch.h" +#include "BlockCountry.h" +#include "ItemIDRangeManager.h" +#include "Cache.h" +#ifdef __AUCTION__ +#include "AuctionManager.h" +#endif +extern int g_iPlayerCacheFlushSeconds; +extern int g_iItemCacheFlushSeconds; +extern int g_test_server; +extern int g_log; +extern std::string g_stLocale; +extern std::string g_stLocaleNameColumn; +bool CreateItemTableFromRes(MYSQL_RES * res, std::vector * pVec, DWORD dwPID); + +DWORD g_dwUsageMax = 0; +DWORD g_dwUsageAvg = 0; + +CPacketInfo g_query_info; +CPacketInfo g_item_info; + +int g_item_count = 0; +int g_query_count[2]; + +CClientManager::CClientManager() : + m_pkAuthPeer(NULL), + m_iPlayerIDStart(0), + m_iPlayerDeleteLevelLimit(0), + m_iPlayerDeleteLevelLimitLower(0), + m_bChinaEventServer(false), + m_iShopTableSize(0), + m_pShopTable(NULL), + m_iRefineTableSize(0), + m_pRefineTable(NULL), + m_bShutdowned(FALSE), + m_iCacheFlushCount(0), + m_iCacheFlushCountLimit(200) +{ + m_itemRange.dwMin = 0; + m_itemRange.dwMax = 0; + m_itemRange.dwUsableItemIDMin = 0; + + memset(g_query_count, 0, sizeof(g_query_count)); +} + +CClientManager::~CClientManager() +{ + Destroy(); +} + +void CClientManager::SetPlayerIDStart(int iIDStart) +{ + m_iPlayerIDStart = iIDStart; +} + +void CClientManager::Destroy() +{ + m_mChannelStatus.clear(); + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end(); ++i) + (*i)->Destroy(); + + m_peerList.clear(); + + if (m_fdAccept > 0) + { + socket_close(m_fdAccept); + m_fdAccept = -1; + } +} + +bool CClientManager::Initialize() +{ + int tmpValue; + + //BOOT_LOCALIZATION + if (!InitializeLocalization()) + { + fprintf(stderr, "Failed Localization Infomation so exit\n"); + return false; + } + + //END_BOOT_LOCALIZATION + //ITEM_UNIQUE_ID + + if (!InitializeNowItemID()) + { + fprintf(stderr, " Item range Initialize Failed. Exit DBCache Server\n"); + return false; + } + //END_ITEM_UNIQUE_ID + + if (!InitializeTables()) + { + sys_err("Table Initialize FAILED"); + return false; + } + + CGuildManager::instance().BootReserveWar(); + + if (!CConfig::instance().GetValue("BIND_PORT", &tmpValue)) + tmpValue = 5300; + + char szBindIP[128]; + + if (!CConfig::instance().GetValue("BIND_IP", szBindIP, 128)) + strlcpy(szBindIP, "0", sizeof(szBindIP)); + + m_fdAccept = socket_tcp_bind(szBindIP, tmpValue); + + if (m_fdAccept < 0) + { + perror("socket"); + return false; + } + + sys_log(0, "ACCEPT_HANDLE: %u", m_fdAccept); + fdwatch_add_fd(m_fdWatcher, m_fdAccept, NULL, FDW_READ, false); + + if (!CConfig::instance().GetValue("BACKUP_LIMIT_SEC", &tmpValue)) + tmpValue = 600; + + m_looping = true; + + if (!CConfig::instance().GetValue("PLAYER_DELETE_LEVEL_LIMIT", &m_iPlayerDeleteLevelLimit)) + { + sys_err("conf.txt: Cannot find PLAYER_DELETE_LEVEL_LIMIT, use default level %d", PLAYER_MAX_LEVEL_CONST + 1); + m_iPlayerDeleteLevelLimit = PLAYER_MAX_LEVEL_CONST + 1; + } + + if (!CConfig::instance().GetValue("PLAYER_DELETE_LEVEL_LIMIT_LOWER", &m_iPlayerDeleteLevelLimitLower)) + { + m_iPlayerDeleteLevelLimitLower = 0; + } + + sys_log(0, "PLAYER_DELETE_LEVEL_LIMIT set to %d", m_iPlayerDeleteLevelLimit); + sys_log(0, "PLAYER_DELETE_LEVEL_LIMIT_LOWER set to %d", m_iPlayerDeleteLevelLimitLower); + + m_bChinaEventServer = false; + + int iChinaEventServer = 0; + + if (CConfig::instance().GetValue("CHINA_EVENT_SERVER", &iChinaEventServer)) + m_bChinaEventServer = (iChinaEventServer); + + sys_log(0, "CHINA_EVENT_SERVER %s", CClientManager::instance().IsChinaEventServer()?"true":"false"); + + + LoadEventFlag(); + + // database character-set + if (g_stLocale == "big5" || g_stLocale == "sjis") + CDBManager::instance().QueryLocaleSet(); + + return true; +} + +void CClientManager::MainLoop() +{ + SQLMsg * tmp; + + sys_log(0, "ClientManager pointer is %p", this); + + // η + while (!m_bShutdowned) + { + while ((tmp = CDBManager::instance().PopResult())) + { + AnalyzeQueryResult(tmp); + delete tmp; + } + + if (!Process()) + break; + + log_rotate(); + } + + // + // η ó + // + sys_log(0, "MainLoop exited, Starting cache flushing"); + + signal_timer_disable(); + + itertype(m_map_playerCache) it = m_map_playerCache.begin(); + + //÷̾ ̺ ij ÷ + while (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = (it++)->second; + + c->Flush(); + delete c; + } + m_map_playerCache.clear(); + + + itertype(m_map_itemCache) it2 = m_map_itemCache.begin(); + // ÷ + while (it2 != m_map_itemCache.end()) + { + CItemCache * c = (it2++)->second; + + c->Flush(); + delete c; + } + m_map_itemCache.clear(); + + // MYSHOP_PRICE_LIST + // + // λ Ʈ Flush + // + for (itertype(m_mapItemPriceListCache) itPriceList = m_mapItemPriceListCache.begin(); itPriceList != m_mapItemPriceListCache.end(); ++itPriceList) + { + CItemPriceListTableCache* pCache = itPriceList->second; + pCache->Flush(); + delete pCache; + } + + m_mapItemPriceListCache.clear(); + // END_OF_MYSHOP_PRICE_LIST +} + +void CClientManager::Quit() +{ + m_bShutdowned = TRUE; +} + +void CClientManager::QUERY_BOOT(CPeer* peer, TPacketGDBoot * p) +{ + const BYTE bPacketVersion = 6; // BOOT Ŷ ٲ𶧸 ȣ ø Ѵ. + + std::vector vAdmin; + std::vector vHost; + + __GetHostInfo(vHost); + __GetAdminInfo(p->szIP, vAdmin); + + sys_log(0, "QUERY_BOOT : AdminInfo (Request ServerIp %s) ", p->szIP); + + DWORD dwPacketSize = + sizeof(DWORD) + + sizeof(BYTE) + + sizeof(WORD) + sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TShopTable) * m_iShopTableSize + + sizeof(WORD) + sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TRefineTable) * m_iRefineTableSize + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemAttrTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemRareTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TLand) * m_vec_kLandTable.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TObjectProto) * m_vec_kObjectProto.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(building::TObject) * m_map_pkObjectTable.size() + +#ifdef __AUCTION__ + sizeof(WORD) + sizeof(WORD) + sizeof(TPlayerItem) * AuctionManager::instance().GetAuctionItemSize() + + sizeof(WORD) + sizeof(WORD) + sizeof(TAuctionItemInfo) * AuctionManager::instance().GetAuctionSize() + + sizeof(WORD) + sizeof(WORD) + sizeof(TSaleItemInfo) * AuctionManager::instance().GetSaleSize() + + sizeof(WORD) + sizeof(WORD) + sizeof(TWishItemInfo) * AuctionManager::instance().GetWishSize() + + sizeof(WORD) + sizeof(WORD) + (sizeof(DWORD) + sizeof(DWORD) + sizeof(int)) * AuctionManager::instance().GetMyBidSize() + +#endif + sizeof(time_t) + + sizeof(WORD) + sizeof(WORD) + sizeof(TItemIDRangeTable)*2 + + //ADMIN_MANAGER + sizeof(WORD) + sizeof(WORD) + 16 * vHost.size() + + sizeof(WORD) + sizeof(WORD) + sizeof(tAdminInfo) * vAdmin.size() + + //END_ADMIN_MANAGER + sizeof(WORD) + sizeof(WORD) + sizeof(TMonarchInfo) + + sizeof(WORD) + sizeof(WORD) + sizeof(MonarchCandidacy)* CMonarch::instance().MonarchCandidacySize() + + sizeof(WORD); + + peer->EncodeHeader(HEADER_DG_BOOT, 0, dwPacketSize); + peer->Encode(&dwPacketSize, sizeof(DWORD)); + peer->Encode(&bPacketVersion, sizeof(BYTE)); + + sys_log(0, "BOOT: PACKET: %d", dwPacketSize); + sys_log(0, "BOOT: VERSION: %d", bPacketVersion); + + sys_log(0, "sizeof(TMobTable) = %d", sizeof(TMobTable)); + sys_log(0, "sizeof(TItemTable) = %d", sizeof(TItemTable)); + sys_log(0, "sizeof(TShopTable) = %d", sizeof(TShopTable)); + sys_log(0, "sizeof(TSkillTable) = %d", sizeof(TSkillTable)); + sys_log(0, "sizeof(TRefineTable) = %d", sizeof(TRefineTable)); + sys_log(0, "sizeof(TItemAttrTable) = %d", sizeof(TItemAttrTable)); + sys_log(0, "sizeof(TItemRareTable) = %d", sizeof(TItemAttrTable)); + sys_log(0, "sizeof(TBanwordTable) = %d", sizeof(TBanwordTable)); + sys_log(0, "sizeof(TLand) = %d", sizeof(building::TLand)); + sys_log(0, "sizeof(TObjectProto) = %d", sizeof(building::TObjectProto)); + sys_log(0, "sizeof(TObject) = %d", sizeof(building::TObject)); + //ADMIN_MANAGER + sys_log(0, "sizeof(tAdminInfo) = %d * %d ", sizeof(tAdminInfo) * vAdmin.size()); + //END_ADMIN_MANAGER + sys_log(0, "sizeof(TMonarchInfo) = %d * %d", sizeof(TMonarchInfo)); + + peer->EncodeWORD(sizeof(TMobTable)); + peer->EncodeWORD(m_vec_mobTable.size()); + peer->Encode(&m_vec_mobTable[0], sizeof(TMobTable) * m_vec_mobTable.size()); + + peer->EncodeWORD(sizeof(TItemTable)); + peer->EncodeWORD(m_vec_itemTable.size()); + peer->Encode(&m_vec_itemTable[0], sizeof(TItemTable) * m_vec_itemTable.size()); + + peer->EncodeWORD(sizeof(TShopTable)); + peer->EncodeWORD(m_iShopTableSize); + peer->Encode(m_pShopTable, sizeof(TShopTable) * m_iShopTableSize); + + peer->EncodeWORD(sizeof(TSkillTable)); + peer->EncodeWORD(m_vec_skillTable.size()); + peer->Encode(&m_vec_skillTable[0], sizeof(TSkillTable) * m_vec_skillTable.size()); + + peer->EncodeWORD(sizeof(TRefineTable)); + peer->EncodeWORD(m_iRefineTableSize); + peer->Encode(m_pRefineTable, sizeof(TRefineTable) * m_iRefineTableSize); + + peer->EncodeWORD(sizeof(TItemAttrTable)); + peer->EncodeWORD(m_vec_itemAttrTable.size()); + peer->Encode(&m_vec_itemAttrTable[0], sizeof(TItemAttrTable) * m_vec_itemAttrTable.size()); + + peer->EncodeWORD(sizeof(TItemAttrTable)); + peer->EncodeWORD(m_vec_itemRareTable.size()); + peer->Encode(&m_vec_itemRareTable[0], sizeof(TItemAttrTable) * m_vec_itemRareTable.size()); + + peer->EncodeWORD(sizeof(TBanwordTable)); + peer->EncodeWORD(m_vec_banwordTable.size()); + peer->Encode(&m_vec_banwordTable[0], sizeof(TBanwordTable) * m_vec_banwordTable.size()); + + peer->EncodeWORD(sizeof(building::TLand)); + peer->EncodeWORD(m_vec_kLandTable.size()); + peer->Encode(&m_vec_kLandTable[0], sizeof(building::TLand) * m_vec_kLandTable.size()); + + peer->EncodeWORD(sizeof(building::TObjectProto)); + peer->EncodeWORD(m_vec_kObjectProto.size()); + peer->Encode(&m_vec_kObjectProto[0], sizeof(building::TObjectProto) * m_vec_kObjectProto.size()); + + peer->EncodeWORD(sizeof(building::TObject)); + peer->EncodeWORD(m_map_pkObjectTable.size()); + + itertype(m_map_pkObjectTable) it = m_map_pkObjectTable.begin(); + + while (it != m_map_pkObjectTable.end()) + peer->Encode((it++)->second, sizeof(building::TObject)); + + // Auction Boot +#ifdef __AUCTION__ + AuctionManager::instance().Boot (peer); +#endif + time_t now = time(0); + peer->Encode(&now, sizeof(time_t)); + + TItemIDRangeTable itemRange = CItemIDRangeManager::instance().GetRange(); + TItemIDRangeTable itemRangeSpare = CItemIDRangeManager::instance().GetRange(); + + peer->EncodeWORD(sizeof(TItemIDRangeTable)); + peer->EncodeWORD(1); + peer->Encode(&itemRange, sizeof(TItemIDRangeTable)); + peer->Encode(&itemRangeSpare, sizeof(TItemIDRangeTable)); + + peer->SetItemIDRange(itemRange); + peer->SetSpareItemIDRange(itemRangeSpare); + + //ADMIN_MANAGER + peer->EncodeWORD(16); + peer->EncodeWORD(vHost.size()); + + for (size_t n = 0; n < vHost.size(); ++n) + { + peer->Encode(vHost[n].c_str(), 16); + sys_log(0, "GMHosts %s", vHost[n].c_str()); + } + + peer->EncodeWORD(sizeof(tAdminInfo)); + peer->EncodeWORD(vAdmin.size()); + + for (size_t n = 0; n < vAdmin.size(); ++n) + { + peer->Encode(&vAdmin[n], sizeof(tAdminInfo)); + sys_log(0, "Admin name %s ConntactIP %s", vAdmin[n].m_szName, vAdmin[n].m_szContactIP); + } + //END_ADMIN_MANAGER + + //MONARCH + peer->EncodeWORD(sizeof(TMonarchInfo)); + peer->EncodeWORD(1); + peer->Encode(CMonarch::instance().GetMonarch(), sizeof(TMonarchInfo)); + + CMonarch::VEC_MONARCHCANDIDACY & rVecMonarchCandidacy = CMonarch::instance().GetVecMonarchCandidacy(); + + size_t num_monarch_candidacy = CMonarch::instance().MonarchCandidacySize(); + peer->EncodeWORD(sizeof(MonarchCandidacy)); + peer->EncodeWORD(num_monarch_candidacy); + if (num_monarch_candidacy != 0) { + peer->Encode(&rVecMonarchCandidacy[0], sizeof(MonarchCandidacy) * num_monarch_candidacy); + } + //END_MONARCE + + if (g_test_server) + sys_log(0, "MONARCHCandidacy Size %d", CMonarch::instance().MonarchCandidacySize()); + + peer->EncodeWORD(0xffff); +} + +void CClientManager::SendPartyOnSetup(CPeer* pkPeer) +{ + TPartyMap & pm = m_map_pkChannelParty[pkPeer->GetChannel()]; + + for (itertype(pm) it_party = pm.begin(); it_party != pm.end(); ++it_party) + { + sys_log(0, "PARTY SendPartyOnSetup Party [%u]", it_party->first); + pkPeer->EncodeHeader(HEADER_DG_PARTY_CREATE, 0, sizeof(TPacketPartyCreate)); + pkPeer->Encode(&it_party->first, sizeof(DWORD)); + + for (itertype(it_party->second) it_member = it_party->second.begin(); it_member != it_party->second.end(); ++it_member) + { + sys_log(0, "PARTY SendPartyOnSetup Party [%u] Member [%u]", it_party->first, it_member->first); + pkPeer->EncodeHeader(HEADER_DG_PARTY_ADD, 0, sizeof(TPacketPartyAdd)); + pkPeer->Encode(&it_party->first, sizeof(DWORD)); + pkPeer->Encode(&it_member->first, sizeof(DWORD)); + pkPeer->Encode(&it_member->second.bRole, sizeof(BYTE)); + + pkPeer->EncodeHeader(HEADER_DG_PARTY_SET_MEMBER_LEVEL, 0, sizeof(TPacketPartySetMemberLevel)); + pkPeer->Encode(&it_party->first, sizeof(DWORD)); + pkPeer->Encode(&it_member->first, sizeof(DWORD)); + pkPeer->Encode(&it_member->second.bLevel, sizeof(BYTE)); + } + } +} + +void CClientManager::QUERY_PLAYER_COUNT(CPeer * pkPeer, TPlayerCountPacket * pPacket) +{ + pkPeer->SetUserCount(pPacket->dwCount); +} + +void CClientManager::QUERY_QUEST_SAVE(CPeer * pkPeer, TQuestTable * pTable, DWORD dwLen) +{ + if (0 != (dwLen % sizeof(TQuestTable))) + { + sys_err("invalid packet size %d, sizeof(TQuestTable) == %d", dwLen, sizeof(TQuestTable)); + return; + } + + int iSize = dwLen / sizeof(TQuestTable); + + char szQuery[1024]; + + for (int i = 0; i < iSize; ++i, ++pTable) + { + if (pTable->lValue == 0) + { + snprintf(szQuery, sizeof(szQuery), + "DELETE FROM quest%s WHERE dwPID=%d AND szName='%s' AND szState='%s'", + GetTablePostfix(), pTable->dwPID, pTable->szName, pTable->szState); + } + else + { + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(%d, '%s', '%s', %ld)", + GetTablePostfix(), pTable->dwPID, pTable->szName, pTable->szState, pTable->lValue); + } + + CDBManager::instance().ReturnQuery(szQuery, QID_QUEST_SAVE, pkPeer->GetHandle(), NULL); + } +} + +void CClientManager::QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafeboxLoadPacket * packet, bool bMall) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + strlcpy(pi->safebox_password, packet->szPassword, sizeof(pi->safebox_password)); + pi->account_id = packet->dwID; + pi->account_index = 0; + pi->ip[0] = bMall ? 1 : 0; + strlcpy(pi->login, packet->szLogin, sizeof(pi->login)); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), + "SELECT account_id, size, password FROM safebox%s WHERE account_id=%u", + GetTablePostfix(), packet->dwID); + + if (g_log) + sys_log(0, "HEADER_GD_SAFEBOX_LOAD (handle: %d account.id %u is_mall %d)", dwHandle, packet->dwID, bMall ? 1 : 0); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = pi->dwHandle; + + // ⿡ ϴ account_index Ѵ. + // ù° н ˾Ƴ ϴ 0 + // ι° ͸ 1 + + if (pi->account_index == 0) + { + char szSafeboxPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; + strlcpy(szSafeboxPassword, pi->safebox_password, sizeof(szSafeboxPassword)); + + TSafeboxTable * pSafebox = new TSafeboxTable; + memset(pSafebox, 0, sizeof(TSafeboxTable)); + + SQLResult * res = msg->Get(); + + if (res->uiNumRows == 0) + { + if (strcmp("000000", szSafeboxPassword)) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_WRONG_PASSWORD, dwHandle, 0); + delete pi; + return; + } + } + else + { + MYSQL_ROW row = mysql_fetch_row(res->pSQLResult); + + // йȣ Ʋ.. + if (((!row[2] || !*row[2]) && strcmp("000000", szSafeboxPassword)) || + ((row[2] && *row[2]) && strcmp(row[2], szSafeboxPassword))) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_WRONG_PASSWORD, dwHandle, 0); + delete pi; + return; + } + + if (!row[0]) + pSafebox->dwID = 0; + else + str_to_number(pSafebox->dwID, row[0]); + + if (!row[1]) + pSafebox->bSize = 0; + else + str_to_number(pSafebox->bSize, row[1]); + /* + if (!row[3]) + pSafebox->dwGold = 0; + else + pSafebox->dwGold = atoi(row[3]); + */ + if (pi->ip[0] == 1) + { + pSafebox->bSize = 1; + sys_log(0, "MALL id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); + } + else + sys_log(0, "SAFEBOX id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); + } + + if (0 == pSafebox->dwID) + pSafebox->dwID = pi->account_id; + + pi->pSafebox = pSafebox; + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "SELECT id, window+0, pos, count, vnum, socket0, socket1, socket2, " + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6 " + "FROM item%s WHERE owner_id=%d AND window='%s'", + GetTablePostfix(), pi->account_id, pi->ip[0] == 0 ? "SAFEBOX" : "MALL"); + + pi->account_index = 1; + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); + } + else + { + + if (!pi->pSafebox) + { + sys_err("null safebox pointer!"); + delete pi; + return; + } + + + // ־Ƿ â ִ ó + // ̱ â ƾ ȿ° + if (!msg->Get()->pSQLResult) + { + sys_err("null safebox result"); + delete pi; + return; + } + + static std::vector s_items; + CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id); + + std::set * pSet = ItemAwardManager::instance().GetByLogin(pi->login); + + if (pSet && !m_vec_itemTable.empty()) + { + + CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9); + bool bEscape = false; + + for (DWORD i = 0; i < s_items.size(); ++i) + { + TPlayerItem & r = s_items[i]; + + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum); + + if (it == m_map_itemTableByVnum.end()) + { + bEscape = true; + sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login); + break; + } + + grid.Put(r.pos, 1, it->second->bSize); + } + + if (!bEscape) + { + std::vector > vec_dwFinishedAwardID; + + typeof(pSet->begin()) it = pSet->begin(); + + char szQuery[512]; + + while (it != pSet->end()) + { + TItemAward * pItemAward = *(it++); + const DWORD& dwItemVnum = pItemAward->dwVnum; + + if (pItemAward->bTaken) + continue; + + if (pi->ip[0] == 0 && pItemAward->bMall) + continue; + + if (pi->ip[0] == 1 && !pItemAward->bMall) + continue; + + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum); + + if (it == m_map_itemTableByVnum.end()) + { + sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login); + continue; + } + + TItemTable * pItemTable = it->second; + + int iPos; + + if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1) + break; + + TPlayerItem item; + memset(&item, 0, sizeof(TPlayerItem)); + + DWORD dwSocket2 = 0; + + if (pItemTable->bType == ITEM_UNIQUE) + { + if (pItemAward->dwSocket2 != 0) + dwSocket2 = pItemAward->dwSocket2; + else + dwSocket2 = pItemTable->alValues[0]; + } + else if ((dwItemVnum == 50300 || dwItemVnum == 70037) && pItemAward->dwSocket0 == 0) + { + DWORD dwSkillIdx; + DWORD dwSkillVnum; + + do + { + dwSkillIdx = number(0, m_vec_skillTable.size()-1); + + dwSkillVnum = m_vec_skillTable[dwSkillIdx].dwVnum; + + if (!dwSkillVnum > 120) + continue; + + break; + } while (1); + + pItemAward->dwSocket0 = dwSkillVnum; + } + else + { + switch (dwItemVnum) + { + case 72723: case 72724: case 72725: case 72726: + case 72727: case 72728: case 72729: case 72730: + // ù ϴ ġ ... + // ׷ ׳ ϵ ڵ. ڿ ڵ ۵. + case 76004: case 76005: case 76021: case 76022: + case 79012: case 79013: + if (pItemAward->dwSocket2 == 0) + { + dwSocket2 = pItemTable->alValues[0]; + } + else + { + dwSocket2 = pItemAward->dwSocket2; + } + break; + } + } + + if (GetItemID () > m_itemRange.dwMax) + { + sys_err("UNIQUE ID OVERFLOW!!"); + break; + } + + { + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find (dwItemVnum); + if (it == m_map_itemTableByVnum.end()) + { + sys_err ("Invalid item(vnum : %d). It is not in m_map_itemTableByVnum.", dwItemVnum); + continue; + } + TItemTable* item_table = it->second; + if (item_table == NULL) + { + sys_err ("Invalid item_table (vnum : %d). It's value is NULL in m_map_itemTableByVnum.", dwItemVnum); + continue; + } + if (0 == pItemAward->dwSocket0) + { + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; i++) + { + if (LIMIT_REAL_TIME == item_table->aLimits[i].bType) + { + if (0 == item_table->aLimits[i].lValue) + pItemAward->dwSocket0 = time(0) + 60 * 60 * 24 * 7; + else + pItemAward->dwSocket0 = time(0) + item_table->aLimits[i].lValue; + + break; + } + else if (LIMIT_REAL_TIME_START_FIRST_USE == item_table->aLimits[i].bType || LIMIT_TIMER_BASED_ON_WEAR == item_table->aLimits[i].bType) + { + if (0 == item_table->aLimits[i].lValue) + pItemAward->dwSocket0 = 60 * 60 * 24 * 7; + else + pItemAward->dwSocket0 = item_table->aLimits[i].lValue; + + break; + } + } + } + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO item%s (id, owner_id, window, pos, vnum, count, socket0, socket1, socket2) " + "VALUES(%u, %u, '%s', %d, %u, %u, %u, %u, %u)", + GetTablePostfix(), + GainItemID(), + pi->account_id, + pi->ip[0] == 0 ? "SAFEBOX" : "MALL", + iPos, + pItemAward->dwVnum, pItemAward->dwCount, pItemAward->dwSocket0, pItemAward->dwSocket1, dwSocket2); + } + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult * pRes = pmsg->Get(); + sys_log(0, "SAFEBOX Query : [%s]", szQuery); + + if (pRes->uiAffectedRows == 0 || pRes->uiInsertID == 0 || pRes->uiAffectedRows == (uint32_t)-1) + break; + + item.id = pmsg->Get()->uiInsertID; + item.window = pi->ip[0] == 0 ? SAFEBOX : MALL, + item.pos = iPos; + item.count = pItemAward->dwCount; + item.vnum = pItemAward->dwVnum; + item.alSockets[0] = pItemAward->dwSocket0; + item.alSockets[1] = pItemAward->dwSocket1; + item.alSockets[2] = dwSocket2; + s_items.push_back(item); + + vec_dwFinishedAwardID.push_back(std::make_pair(pItemAward->dwID, item.id)); + grid.Put(iPos, 1, it->second->bSize); + } + + for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i) + ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second); + } + } + + pi->pSafebox->wItemCount = s_items.size(); + + pkPeer->EncodeHeader(pi->ip[0] == 0 ? HEADER_DG_SAFEBOX_LOAD : HEADER_DG_MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size()); + + pkPeer->Encode(pi->pSafebox, sizeof(TSafeboxTable)); + + if (!s_items.empty()) + pkPeer->Encode(&s_items[0], sizeof(TPlayerItem) * s_items.size()); + + delete pi; + } +} + +void CClientManager::QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + pi->account_index = p->bSize; // account_index ӽ÷ + + char szQuery[QUERY_MAX_LEN]; + + if (p->bSize == 1) + snprintf(szQuery, sizeof(szQuery), "INSERT INTO safebox%s (account_id, size) VALUES(%u, %u)", GetTablePostfix(), p->dwID, p->bSize); + else + snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET size=%u WHERE account_id=%u", GetTablePostfix(), p->bSize, p->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_SIZE, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + BYTE bSize = p->account_index; + + delete p; + + if (msg->Get()->uiNumRows > 0) + { + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_SIZE, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bSize); + } +} + +void CClientManager::QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p) +{ + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); + strlcpy(pi->safebox_password, p->szNewPassword, sizeof(pi->safebox_password)); + strlcpy(pi->login, p->szOldPassword, sizeof(pi->login)); + pi->account_id = p->dwID; + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT password FROM safebox%s WHERE account_id=%u", GetTablePostfix(), p->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD, pkPeer->GetHandle(), pi); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + + if (msg->Get()->uiNumRows > 0) + { + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + if (row[0] && *row[0] && !strcasecmp(row[0], p->login) || (!row[0] || !*row[0]) && !strcmp("000000", p->login)) + { + char szQuery[QUERY_MAX_LEN]; + char escape_pwd[64]; + CDBManager::instance().EscapeString(escape_pwd, p->safebox_password, strlen(p->safebox_password)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET password='%s' WHERE account_id=%u", GetTablePostfix(), escape_pwd, p->account_id); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD_SECOND, pkPeer->GetHandle(), p); + return; + } + } + + delete p; + + // Wrong old password + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(0); +} + +void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; + DWORD dwHandle = p->dwHandle; + delete p; + + pkPeer->EncodeHeader(HEADER_DG_SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(1); +} + +// MYSHOP_PRICE_LIST +void CClientManager::RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg) +{ + TItemPricelistReqInfo* pReqInfo = (TItemPricelistReqInfo*)static_cast(pMsg->pvUserData)->pvData; + + // + // DB ε Cache + // + + TItemPriceListTable table; + table.dwOwnerID = pReqInfo->second; + table.byCount = 0; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + str_to_number(table.aPriceInfo[table.byCount].dwVnum, row[0]); + str_to_number(table.aPriceInfo[table.byCount].dwPrice, row[1]); + table.byCount++; + } + + PutItemPriceListCache(&table); + + // + // ε ͸ Game server + // + + TPacketMyshopPricelistHeader header; + + header.dwOwnerID = pReqInfo->second; + header.byCount = table.byCount; + + size_t sizePriceListSize = sizeof(TItemPriceInfo) * header.byCount; + + peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, pReqInfo->first, sizeof(header) + sizePriceListSize); + peer->Encode(&header, sizeof(header)); + peer->Encode(table.aPriceInfo, sizePriceListSize); + + sys_log(0, "Load MyShopPricelist handle[%d] pid[%d] count[%d]", pReqInfo->first, pReqInfo->second, header.byCount); + + delete pReqInfo; +} + +void CClientManager::RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg) +{ + TItemPriceListTable* pUpdateTable = (TItemPriceListTable*)static_cast(pMsg->pvUserData)->pvData; + + // + // DB ε Cache + // + + TItemPriceListTable table; + table.dwOwnerID = pUpdateTable->dwOwnerID; + table.byCount = 0; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + str_to_number(table.aPriceInfo[table.byCount].dwVnum, row[0]); + str_to_number(table.aPriceInfo[table.byCount].dwPrice, row[1]); + table.byCount++; + } + + PutItemPriceListCache(&table); + + // Update cache + GetItemPriceListCache(pUpdateTable->dwOwnerID)->UpdateList(pUpdateTable); + + delete pUpdateTable; +} +// END_OF_MYSHOP_PRICE_LIST + +void CClientManager::QUERY_SAFEBOX_SAVE(CPeer * pkPeer, TSafeboxTable * pTable) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "UPDATE safebox%s SET gold='%u' WHERE account_id=%u", + GetTablePostfix(), pTable->dwGold, pTable->dwID); + + CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_SAVE, pkPeer->GetHandle(), NULL); +} + +void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE id=%u", GetTablePostfix(), p->bEmpire, p->dwAccountID); + delete CDBManager::instance().DirectQuery(szQuery); + + sys_log(0, "EmpireSelect: %s", szQuery); + { + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, pid3, pid4 FROM player_index%s WHERE id=%u", GetTablePostfix(), p->dwAccountID); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult * pRes = pmsg->Get(); + + if (pRes->uiNumRows) + { + sys_log(0, "EMPIRE %lu", pRes->uiNumRows); + + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + DWORD pids[3]; + + UINT g_start_map[4] = + { + 0, // reserved + 1, // ż + 21, // õ + 41 // 뱹 + }; + + // FIXME share with game + DWORD g_start_position[4][2]= + { + { 0, 0 }, + { 469300, 964200 }, // ż + { 55700, 157900 }, // õ + { 969600, 278400 } // 뱹 + }; + + for (int i = 0; i < 3; ++i) + { + str_to_number(pids[i], row[i]); + sys_log(0, "EMPIRE PIDS[%d]", pids[i]); + + if (pids[i]) + { + sys_log(0, "EMPIRE move to pid[%d] to villiage of %u, map_index %d", + pids[i], p->bEmpire, g_start_map[p->bEmpire]); + + snprintf(szQuery, sizeof(szQuery), "UPDATE player%s SET map_index=%u,x=%u,y=%u WHERE id=%u", + GetTablePostfix(), + g_start_map[p->bEmpire], + g_start_position[p->bEmpire][0], + g_start_position[p->bEmpire][1], + pids[i]); + + std::auto_ptr pmsg2(CDBManager::instance().DirectQuery(szQuery)); + } + } + } + } + + pkPeer->EncodeHeader(HEADER_DG_EMPIRE_SELECT, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(p->bEmpire); +} + +void CClientManager::QUERY_SETUP(CPeer * peer, DWORD dwHandle, const char * c_pData) +{ + TPacketGDSetup * p = (TPacketGDSetup *) c_pData; + c_pData += sizeof(TPacketGDSetup); + + if (p->bAuthServer) + { + sys_log(0, "AUTH_PEER ptr %p", peer); + + m_pkAuthPeer = peer; + SendAllLoginToBilling(); + return; + } + + peer->SetPublicIP(p->szPublicIP); + peer->SetChannel(p->bChannel); + peer->SetListenPort(p->wListenPort); + peer->SetP2PPort(p->wP2PPort); + peer->SetMaps(p->alMaps); + + // + //   ִ + // + TMapLocation kMapLocations; + + strlcpy(kMapLocations.szHost, peer->GetPublicIP(), sizeof(kMapLocations.szHost)); + kMapLocations.wPort = peer->GetListenPort(); + thecore_memcpy(kMapLocations.alMaps, peer->GetMaps(), sizeof(kMapLocations.alMaps)); + + BYTE bMapCount; + + std::vector vec_kMapLocations; + + if (peer->GetChannel() == 1) + { + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end(); ++i) + { + CPeer * tmp = *i; + + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == GUILD_WARP_WAR_CHANNEL || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2; + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.push_back(kMapLocation2); + + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + bMapCount = 1; + tmp->EncodeBYTE(bMapCount); + tmp->Encode(&kMapLocations, sizeof(TMapLocation)); + } + } + } + else if (peer->GetChannel() == GUILD_WARP_WAR_CHANNEL) + { + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end(); ++i) + { + CPeer * tmp = *i; + + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == 1 || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2; + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + vec_kMapLocations.push_back(kMapLocation2); + } + + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + bMapCount = 1; + tmp->EncodeBYTE(bMapCount); + tmp->Encode(&kMapLocations, sizeof(TMapLocation)); + } + } + else + { + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end(); ++i) + { + CPeer * tmp = *i; + + if (tmp == peer) + continue; + + if (!tmp->GetChannel()) + continue; + + if (tmp->GetChannel() == GUILD_WARP_WAR_CHANNEL || tmp->GetChannel() == peer->GetChannel()) + { + TMapLocation kMapLocation2; + + strlcpy(kMapLocation2.szHost, tmp->GetPublicIP(), sizeof(kMapLocation2.szHost)); + kMapLocation2.wPort = tmp->GetListenPort(); + thecore_memcpy(kMapLocation2.alMaps, tmp->GetMaps(), sizeof(kMapLocation2.alMaps)); + + vec_kMapLocations.push_back(kMapLocation2); + } + + if (tmp->GetChannel() == peer->GetChannel()) + { + tmp->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation)); + bMapCount = 1; + tmp->EncodeBYTE(bMapCount); + tmp->Encode(&kMapLocations, sizeof(TMapLocation)); + } + } + } + + vec_kMapLocations.push_back(kMapLocations); + + peer->EncodeHeader(HEADER_DG_MAP_LOCATIONS, 0, sizeof(BYTE) + sizeof(TMapLocation) * vec_kMapLocations.size()); + bMapCount = vec_kMapLocations.size(); + peer->EncodeBYTE(bMapCount); + peer->Encode(&vec_kMapLocations[0], sizeof(TMapLocation) * vec_kMapLocations.size()); + + // + // ¾ : Ǿ ٸ Ǿ ϰ . (P2P ؼ ) + // + sys_log(0, "SETUP: channel %u listen %u p2p %u count %u", peer->GetChannel(), p->wListenPort, p->wP2PPort, bMapCount); + + TPacketDGP2P p2pSetupPacket; + p2pSetupPacket.wPort = peer->GetP2PPort(); + p2pSetupPacket.bChannel = peer->GetChannel(); + strlcpy(p2pSetupPacket.szHost, peer->GetPublicIP(), sizeof(p2pSetupPacket.szHost)); + + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end();++i) + { + CPeer * tmp = *i; + + if (tmp == peer) + continue; + + // ä 0̶ SETUP Ŷ Ǿ Ǵ auth + if (0 == tmp->GetChannel()) + continue; + + tmp->EncodeHeader(HEADER_DG_P2P, 0, sizeof(TPacketDGP2P)); + tmp->Encode(&p2pSetupPacket, sizeof(TPacketDGP2P)); + } + + // + // α + // + TPacketLoginOnSetup * pck = (TPacketLoginOnSetup *) c_pData;; + std::vector vec_repair; + + for (DWORD c = 0; c < p->dwLoginCount; ++c, ++pck) + { + CLoginData * pkLD = new CLoginData; + + pkLD->SetKey(pck->dwLoginKey); + pkLD->SetClientKey(pck->adwClientKey); + pkLD->SetIP(pck->szHost); + + TAccountTable & r = pkLD->GetAccountRef(); + + r.id = pck->dwID; + trim_and_lower(pck->szLogin, r.login, sizeof(r.login)); + strlcpy(r.social_id, pck->szSocialID, sizeof(r.social_id)); + strlcpy(r.passwd, "TEMP", sizeof(r.passwd)); + + InsertLoginData(pkLD); + + if (InsertLogonAccount(pck->szLogin, peer->GetHandle(), pck->szHost)) + { + sys_log(0, "SETUP: login %u %s login_key %u host %s", pck->dwID, pck->szLogin, pck->dwLoginKey, pck->szHost); + pkLD->SetPlay(true); + + if (m_pkAuthPeer) + { + TPacketBillingRepair pck_repair; + pck_repair.dwLoginKey = pkLD->GetKey(); + strlcpy(pck_repair.szLogin, pck->szLogin, sizeof(pck_repair.szLogin)); + strlcpy(pck_repair.szHost, pck->szHost, sizeof(pck_repair.szHost)); + vec_repair.push_back(pck_repair); + } + } + else + sys_log(0, "SETUP: login_fail %u %s login_key %u", pck->dwID, pck->szLogin, pck->dwLoginKey); + } + + if (m_pkAuthPeer && !vec_repair.empty()) + { + sys_log(0, "REPAIR size %d", vec_repair.size()); + + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_REPAIR, 0, sizeof(DWORD) + sizeof(TPacketBillingRepair) * vec_repair.size()); + m_pkAuthPeer->EncodeDWORD(vec_repair.size()); + m_pkAuthPeer->Encode(&vec_repair[0], sizeof(TPacketBillingRepair) * vec_repair.size()); + } + + SendPartyOnSetup(peer); + CGuildManager::instance().OnSetup(peer); + CPrivManager::instance().SendPrivOnSetup(peer); + SendEventFlagsOnSetup(peer); + marriage::CManager::instance().OnSetup(peer); +} + +void CClientManager::QUERY_ITEM_FLUSH(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwID = *(DWORD *) c_pData; + + if (g_log) + sys_log(0, "HEADER_GD_ITEM_FLUSH: %u", dwID); + + CItemCache * c = GetItemCache(dwID); + + if (c) + c->Flush(); +} + +void CClientManager::QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData) +{ + TPlayerItem * p = (TPlayerItem *) c_pData; + + // â ij ʰ, ij ִ ͵ Ѵ. + // auction Ʈ Ÿ ʾƾ Ѵ. EnrollInAuction ŸѴ. + + if (p->window == SAFEBOX || p->window == MALL) + { + CItemCache * c = GetItemCache(p->id); + + if (c) + { + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_test_server) + sys_log(0, "ITEM_CACHE: safebox owner %u id %u", c->Get()->owner, c->Get()->id); + + it->second->erase(c); + } + + m_map_itemCache.erase(p->id); + + delete c; + } + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO item%s (id, owner_id, window, pos, count, vnum, socket0, socket1, socket2, " + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6) " + "VALUES(%u, %u, %d, %d, %u, %u, %ld, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", + GetTablePostfix(), + p->id, + p->owner, + p->window, + p->pos, + p->count, + p->vnum, + p->alSockets[0], + p->alSockets[1], + p->alSockets[2], + p->aAttr[0].bType, p->aAttr[0].sValue, + p->aAttr[1].bType, p->aAttr[1].sValue, + p->aAttr[2].bType, p->aAttr[2].sValue, + p->aAttr[3].bType, p->aAttr[3].sValue, + p->aAttr[4].bType, p->aAttr[4].sValue, + p->aAttr[5].bType, p->aAttr[5].sValue, + p->aAttr[6].bType, p->aAttr[6].sValue); + + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_SAVE, pkPeer->GetHandle(), NULL); + } +#ifdef __AUCTION__ + else if (p->window == AUCTION) + { + sys_err("invalid window. how can you enter this route?"); + return ; + } +#endif + else + { + if (g_test_server) + sys_log(0, "QUERY_ITEM_SAVE => PutItemCache() owner %d id %d vnum %d ", p->owner, p->id, p->vnum); + + PutItemCache(p); + } +} + +CClientManager::TItemCacheSet * CClientManager::GetItemCacheSet(DWORD pid) +{ + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + return NULL; + + return it->second; +} + +void CClientManager::CreateItemCacheSet(DWORD pid) +{ + if (m_map_pkItemCacheSetPtr.find(pid) != m_map_pkItemCacheSetPtr.end()) + return; + + TItemCacheSet * pSet = new TItemCacheSet; + m_map_pkItemCacheSetPtr.insert(TItemCacheSetPtrMap::value_type(pid, pSet)); + + if (g_log) + sys_log(0, "ITEM_CACHE: new cache %u", pid); +} + +void CClientManager::FlushItemCacheSet(DWORD pid) +{ + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + { + sys_log(0, "FLUSH_ITEMCACHESET : No ItemCacheSet pid(%d)", pid); + return; + } + + TItemCacheSet * pSet = it->second; + TItemCacheSet::iterator it_set = pSet->begin(); + + while (it_set != pSet->end()) + { + CItemCache * c = *it_set++; + c->Flush(); + + m_map_itemCache.erase(c->Get()->id); + delete c; + } + + pSet->clear(); + delete pSet; + + m_map_pkItemCacheSetPtr.erase(it); + + if (g_log) + sys_log(0, "FLUSH_ITEMCACHESET : Deleted pid(%d)", pid); +} + +CItemCache * CClientManager::GetItemCache(DWORD id) +{ + TItemCacheMap::iterator it = m_map_itemCache.find(id); + + if (it == m_map_itemCache.end()) + return NULL; + + return it->second; +} + +void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery) +{ + CItemCache * c; + + c = GetItemCache(pNew->id); + + // + if (!c) + { + if (g_log) + sys_log(0, "ITEM_CACHE: PutItemCache ==> New CItemCache id%d vnum%d new owner%d", pNew->id, pNew->vnum, pNew->owner); + + c = new CItemCache; + m_map_itemCache.insert(TItemCacheMap::value_type(pNew->id, c)); + } + // + else + { + if (g_log) + sys_log(0, "ITEM_CACHE: PutItemCache ==> Have Cache"); + // ڰ Ʋ + if (pNew->owner != c->Get()->owner) + { + // ̹ ־ Ѵ. + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_log) + sys_log(0, "ITEM_CACHE: delete owner %u id %u new owner %u", c->Get()->owner, c->Get()->id, pNew->owner); + it->second->erase(c); + } + } + } + + // ο Ʈ + c->Put(pNew, bSkipQuery); + + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + if (g_log) + sys_log(0, "ITEM_CACHE: save %u id %u", c->Get()->owner, c->Get()->id); + else + sys_log(1, "ITEM_CACHE: save %u id %u", c->Get()->owner, c->Get()->id); + it->second->insert(c); + } + else + { + // ڰ Ƿ ٷ ؾ SQL Ͽ + // Ƿ ٷ Ѵ. + if (g_log) + sys_log(0, "ITEM_CACHE: direct save %u id %u", c->Get()->owner, c->Get()->id); + else + sys_log(1, "ITEM_CACHE: direct save %u id %u", c->Get()->owner, c->Get()->id); + + c->OnFlush(); + } +} + +bool CClientManager::DeleteItemCache(DWORD dwID) +{ + CItemCache * c = GetItemCache(dwID); + + if (!c) + return false; + + c->Delete(); + return true; +} + +// MYSHOP_PRICE_LIST +CItemPriceListTableCache* CClientManager::GetItemPriceListCache(DWORD dwID) +{ + TItemPriceListCacheMap::iterator it = m_mapItemPriceListCache.find(dwID); + + if (it == m_mapItemPriceListCache.end()) + return NULL; + + return it->second; +} + +void CClientManager::PutItemPriceListCache(const TItemPriceListTable* pItemPriceList) +{ + CItemPriceListTableCache* pCache = GetItemPriceListCache(pItemPriceList->dwOwnerID); + + if (!pCache) + { + pCache = new CItemPriceListTableCache; + m_mapItemPriceListCache.insert(TItemPriceListCacheMap::value_type(pItemPriceList->dwOwnerID, pCache)); + } + + pCache->Put(const_cast(pItemPriceList), true); +} + +void CClientManager::UpdatePlayerCache() +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.begin(); + + while (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = (it++)->second; + + if (c->CheckTimeout()) + { + if (g_log) + sys_log(0, "UPDATE : UpdatePlayerCache() ==> FlushPlayerCache %d %s ", c->Get(false)->id, c->Get(false)->name); + + c->Flush(); + + // Item Cache Ʈ + UpdateItemCacheSet(c->Get()->id); + } + else if (c->CheckFlushTimeout()) + c->Flush(); + } +} +// END_OF_MYSHOP_PRICE_LIST + +void CClientManager::SetCacheFlushCountLimit(int iLimit) +{ + m_iCacheFlushCountLimit = MAX(10, iLimit); + sys_log(0, "CACHE_FLUSH_LIMIT_PER_SECOND: %d", m_iCacheFlushCountLimit); +} + +void CClientManager::UpdateItemCache() +{ + if (m_iCacheFlushCount >= m_iCacheFlushCountLimit) + return; + + TItemCacheMap::iterator it = m_map_itemCache.begin(); + + while (it != m_map_itemCache.end()) + { + CItemCache * c = (it++)->second; + + // Flush Ѵ. + if (c->CheckFlushTimeout()) + { + if (g_test_server) + sys_log(0, "UpdateItemCache ==> Flush() vnum %d id owner %d", c->Get()->vnum, c->Get()->id, c->Get()->owner); + + c->Flush(); + + if (++m_iCacheFlushCount >= m_iCacheFlushCountLimit) + break; + } + } +} + +void CClientManager::UpdateItemPriceListCache() +{ + TItemPriceListCacheMap::iterator it = m_mapItemPriceListCache.begin(); + + while (it != m_mapItemPriceListCache.end()) + { + CItemPriceListTableCache* pCache = it->second; + + if (pCache->CheckFlushTimeout()) + { + pCache->Flush(); + m_mapItemPriceListCache.erase(it++); + } + else + ++it; + } +} + +void CClientManager::QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwID = *(DWORD *) c_pData; + c_pData += sizeof(DWORD); + + DWORD dwPID = *(DWORD *) c_pData; + + if (!DeleteItemCache(dwID)) + { + char szQuery[64]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM item%s WHERE id=%u", GetTablePostfix(), dwID); + + if (g_log) + sys_log(0, "HEADER_GD_ITEM_DESTROY: PID %u ID %u", dwPID, dwID); + + if (dwPID == 0) // ƹ ٸ, 񵿱 + CDBManager::instance().AsyncQuery(szQuery); + else + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, pkPeer->GetHandle(), NULL); + } +} + +void CClientManager::QUERY_FLUSH_CACHE(CPeer * pkPeer, const char * c_pData) +{ + DWORD dwPID = *(DWORD *) c_pData; + + CPlayerTableCache * pkCache = GetPlayerCache(dwPID); + + if (!pkCache) + return; + + sys_log(0, "FLUSH_CACHE: %u", dwPID); + + pkCache->Flush(); + FlushItemCacheSet(dwPID); + + m_map_playerCache.erase(dwPID); + delete pkCache; +} + +void CClientManager::QUERY_SMS(CPeer * pkPeer, TPacketGDSMS * pack) +{ + char szQuery[QUERY_MAX_LEN]; + + char szMsg[256+1]; + //unsigned long len = CDBManager::instance().EscapeString(szMsg, pack->szMsg, strlen(pack->szMsg), SQL_ACCOUNT); + unsigned long len = CDBManager::instance().EscapeString(szMsg, pack->szMsg, strlen(pack->szMsg)); + szMsg[len] = '\0'; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO sms_pool (server, sender, receiver, mobile, msg) VALUES(%d, '%s', '%s', '%s', '%s')", + (m_iPlayerIDStart + 2) / 3, pack->szFrom, pack->szTo, pack->szMobile, szMsg); + + CDBManager::instance().AsyncQuery(szQuery); +} + +void CClientManager::QUERY_RELOAD_PROTO() +{ + if (!InitializeTables()) + { + sys_err("QUERY_RELOAD_PROTO: cannot load tables"); + return; + } + + for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i) + { + CPeer * tmp = *i; + + if (!tmp->GetChannel()) + continue; + + tmp->EncodeHeader(HEADER_DG_RELOAD_PROTO, 0, + sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() + + sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() + + sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() + + sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size()); + + tmp->EncodeWORD(m_vec_skillTable.size()); + tmp->Encode(&m_vec_skillTable[0], sizeof(TSkillTable) * m_vec_skillTable.size()); + + tmp->EncodeWORD(m_vec_banwordTable.size()); + tmp->Encode(&m_vec_banwordTable[0], sizeof(TBanwordTable) * m_vec_banwordTable.size()); + + tmp->EncodeWORD(m_vec_itemTable.size()); + tmp->Encode(&m_vec_itemTable[0], sizeof(TItemTable) * m_vec_itemTable.size()); + + tmp->EncodeWORD(m_vec_mobTable.size()); + tmp->Encode(&m_vec_mobTable[0], sizeof(TMobTable) * m_vec_mobTable.size()); + } +} + +// ADD_GUILD_PRIV_TIME +/** + * @version 05/06/08 Bang2ni - ӽð ߰ + */ +void CClientManager::AddGuildPriv(TPacketGiveGuildPriv* p) +{ + CPrivManager::instance().AddGuildPriv(p->guild_id, p->type, p->value, p->duration_sec); +} + +void CClientManager::AddEmpirePriv(TPacketGiveEmpirePriv* p) +{ + CPrivManager::instance().AddEmpirePriv(p->empire, p->type, p->value, p->duration_sec); +} +// END_OF_ADD_GUILD_PRIV_TIME + +void CClientManager::AddCharacterPriv(TPacketGiveCharacterPriv* p) +{ + CPrivManager::instance().AddCharPriv(p->pid, p->type, p->value); +} + +void CClientManager::MoneyLog(TPacketMoneyLog* p) +{ + CMoneyLog::instance().AddLog(p->type, p->vnum, p->gold); +} + +CLoginData * CClientManager::GetLoginData(DWORD dwKey) +{ + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(dwKey); + + if (it == m_map_pkLoginData.end()) + return NULL; + + return it->second; +} + +CLoginData * CClientManager::GetLoginDataByLogin(const char * c_pszLogin) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + TLoginDataByLogin::iterator it = m_map_pkLoginDataByLogin.find(szLogin); + + if (it == m_map_pkLoginDataByLogin.end()) + return NULL; + + return it->second; +} + +CLoginData * CClientManager::GetLoginDataByAID(DWORD dwAID) +{ + TLoginDataByAID::iterator it = m_map_pkLoginDataByAID.find(dwAID); + + if (it == m_map_pkLoginDataByAID.end()) + return NULL; + + return it->second; +} + +void CClientManager::InsertLoginData(CLoginData * pkLD) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(pkLD->GetAccountRef().login, szLogin, sizeof(szLogin)); + + m_map_pkLoginData.insert(std::make_pair(pkLD->GetKey(), pkLD)); + m_map_pkLoginDataByLogin.insert(std::make_pair(szLogin, pkLD)); + m_map_pkLoginDataByAID.insert(std::make_pair(pkLD->GetAccountRef().id, pkLD)); +} + +void CClientManager::DeleteLoginData(CLoginData * pkLD) +{ + m_map_pkLoginData.erase(pkLD->GetKey()); + m_map_pkLoginDataByLogin.erase(pkLD->GetAccountRef().login); + m_map_pkLoginDataByAID.erase(pkLD->GetAccountRef().id); + + if (m_map_kLogonAccount.find(pkLD->GetAccountRef().login) == m_map_kLogonAccount.end()) + delete pkLD; + else + pkLD->SetDeleted(true); +} + +void CClientManager::QUERY_AUTH_LOGIN(CPeer * pkPeer, DWORD dwHandle, TPacketGDAuthLogin * p) +{ + if (g_test_server) + sys_log(0, "QUERY_AUTH_LOGIN %d %d %s", p->dwID, p->dwLoginKey, p->szLogin); + CLoginData * pkLD = GetLoginDataByLogin(p->szLogin); + + if (pkLD) + { + DeleteLoginData(pkLD); + } + + BYTE bResult; + + if (GetLoginData(p->dwLoginKey)) + { + sys_err("LoginData already exist key %u login %s", p->dwLoginKey, p->szLogin); + bResult = 0; + + pkPeer->EncodeHeader(HEADER_DG_AUTH_LOGIN, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bResult); + } + else + { + CLoginData * pkLD = new CLoginData; + + pkLD->SetKey(p->dwLoginKey); + pkLD->SetClientKey(p->adwClientKey); + pkLD->SetBillType(p->bBillType); + pkLD->SetBillID(p->dwBillID); + pkLD->SetPremium(p->iPremiumTimes); + + TAccountTable & r = pkLD->GetAccountRef(); + + r.id = p->dwID; + trim_and_lower(p->szLogin, r.login, sizeof(r.login)); + strlcpy(r.social_id, p->szSocialID, sizeof(r.social_id)); + strlcpy(r.passwd, "TEMP", sizeof(r.passwd)); + + sys_log(0, "AUTH_LOGIN id(%u) login(%s) social_id(%s) login_key(%u), client_key(%u %u %u %u)", + p->dwID, p->szLogin, p->szSocialID, p->dwLoginKey, + p->adwClientKey[0], p->adwClientKey[1], p->adwClientKey[2], p->adwClientKey[3]); + + bResult = 1; + + InsertLoginData(pkLD); + + pkPeer->EncodeHeader(HEADER_DG_AUTH_LOGIN, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(bResult); + } +} + +void CClientManager::BillingExpire(TPacketBillingExpire * p) +{ + char key[LOGIN_MAX_LEN + 1]; + trim_and_lower(p->szLogin, key, sizeof(key)); + + switch (p->bBillType) + { + case BILLING_IP_TIME: + case BILLING_IP_DAY: + { + DWORD dwIPID = 0; + str_to_number(dwIPID, p->szLogin); + + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = (it++)->second; + + if (pkLD->GetBillID() == dwIPID) + { + CPeer * pkPeer = GetPeer(pkLD->GetConnectedPeerHandle()); + + if (pkPeer) + { + strlcpy(p->szLogin, pkLD->GetAccountRef().login, sizeof(p->szLogin)); + pkPeer->EncodeHeader(HEADER_DG_BILLING_EXPIRE, 0, sizeof(TPacketBillingExpire)); + pkPeer->Encode(p, sizeof(TPacketBillingExpire)); + } + } + } + } + break; + + case BILLING_TIME: + case BILLING_DAY: + { + TLogonAccountMap::iterator it = m_map_kLogonAccount.find(key); + + if (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = it->second; + + CPeer * pkPeer = GetPeer(pkLD->GetConnectedPeerHandle()); + + if (pkPeer) + { + pkPeer->EncodeHeader(HEADER_DG_BILLING_EXPIRE, 0, sizeof(TPacketBillingExpire)); + pkPeer->Encode(p, sizeof(TPacketBillingExpire)); + } + } + } + break; + } +} + +void CClientManager::BillingCheck(const char * data) +{ + if (!m_pkAuthPeer) + return; + + time_t curTime = GetCurrentTime(); + + DWORD dwCount = *(DWORD *) data; + data += sizeof(DWORD); + + std::vector vec; + + sys_log(0, "BillingCheck: size %u", dwCount); + + for (DWORD i = 0; i < dwCount; ++i) + { + DWORD dwKey = *(DWORD *) data; + data += sizeof(DWORD); + + sys_log(0, "BillingCheck: %u", dwKey); + + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(dwKey); + + if (it == m_map_pkLoginData.end()) + { + sys_log(0, "BillingCheck: key not exist: %u", dwKey); + vec.push_back(dwKey); + } + else + { + CLoginData * pkLD = it->second; + + if (!pkLD->IsPlay() && curTime - pkLD->GetLastPlayTime() > 180) + { + sys_log(0, "BillingCheck: not login: %u", dwKey); + vec.push_back(dwKey); + } + } + } + + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_CHECK, 0, sizeof(DWORD) + sizeof(DWORD) * vec.size()); + m_pkAuthPeer->EncodeDWORD(vec.size()); + + if (!vec.empty()) + m_pkAuthPeer->Encode(&vec[0], sizeof(DWORD) * vec.size()); +} + +void CClientManager::GuildDepositMoney(TPacketGDGuildMoney* p) +{ + CGuildManager::instance().DepositMoney(p->dwGuild, p->iGold); +} + +void CClientManager::GuildWithdrawMoney(CPeer* peer, TPacketGDGuildMoney* p) +{ + CGuildManager::instance().WithdrawMoney(peer, p->dwGuild, p->iGold); +} + +void CClientManager::GuildWithdrawMoneyGiveReply(TPacketGDGuildMoneyWithdrawGiveReply* p) +{ + CGuildManager::instance().WithdrawMoneyReply(p->dwGuild, p->bGiveSuccess, p->iChangeGold); +} + +void CClientManager::GuildWarBet(TPacketGDGuildWarBet * p) +{ + CGuildManager::instance().Bet(p->dwWarID, p->szLogin, p->dwGold, p->dwGuild); +} + +void CClientManager::SendAllLoginToBilling() +{ + if (!m_pkAuthPeer) + return; + + std::vector vec; + TPacketBillingRepair p; + + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = (it++)->second; + + p.dwLoginKey = pkLD->GetKey(); + strlcpy(p.szLogin, pkLD->GetAccountRef().login, sizeof(p.szLogin)); + strlcpy(p.szHost, pkLD->GetIP(), sizeof(p.szHost)); + sys_log(0, "SendAllLoginToBilling %s %s", pkLD->GetAccountRef().login, pkLD->GetIP()); + vec.push_back(p); + } + + if (!vec.empty()) + { + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_REPAIR, 0, sizeof(DWORD) + sizeof(TPacketBillingRepair) * vec.size()); + m_pkAuthPeer->EncodeDWORD(vec.size()); + m_pkAuthPeer->Encode(&vec[0], sizeof(TPacketBillingRepair) * vec.size()); + } +} + +void CClientManager::SendLoginToBilling(CLoginData * pkLD, bool bLogin) +{ + if (!m_pkAuthPeer) + return; + + TPacketBillingLogin p; + + p.dwLoginKey = pkLD->GetKey(); + p.bLogin = bLogin ? 1 : 0; + + DWORD dwCount = 1; + m_pkAuthPeer->EncodeHeader(HEADER_DG_BILLING_LOGIN, 0, sizeof(DWORD) + sizeof(TPacketBillingLogin)); + m_pkAuthPeer->EncodeDWORD(dwCount); + m_pkAuthPeer->Encode(&p, sizeof(TPacketBillingLogin)); +} + +void CClientManager::CreateObject(TPacketGDCreateObject * p) +{ + using namespace building; + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO object%s (land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot) VALUES(%u, %u, %d, %d, %d, %f, %f, %f)", + GetTablePostfix(), p->dwLandID, p->dwVnum, p->lMapIndex, p->x, p->y, p->xRot, p->yRot, p->zRot); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiInsertID == 0) + { + sys_err("cannot insert object"); + return; + } + + TObject * pkObj = new TObject; + + memset(pkObj, 0, sizeof(TObject)); + + pkObj->dwID = pmsg->Get()->uiInsertID; + pkObj->dwVnum = p->dwVnum; + pkObj->dwLandID = p->dwLandID; + pkObj->lMapIndex = p->lMapIndex; + pkObj->x = p->x; + pkObj->y = p->y; + pkObj->xRot = p->xRot; + pkObj->yRot = p->yRot; + pkObj->zRot = p->zRot; + pkObj->lLife = 0; + + ForwardPacket(HEADER_DG_CREATE_OBJECT, pkObj, sizeof(TObject)); + + m_map_pkObjectTable.insert(std::make_pair(pkObj->dwID, pkObj)); +} + +void CClientManager::DeleteObject(DWORD dwID) +{ + char szQuery[128]; + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM object%s WHERE id=%u", GetTablePostfix(), dwID); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_err("no object by id %u", dwID); + return; + } + + itertype(m_map_pkObjectTable) it = m_map_pkObjectTable.find(dwID); + + if (it != m_map_pkObjectTable.end()) + { + delete it->second; + m_map_pkObjectTable.erase(it); + } + + ForwardPacket(HEADER_DG_DELETE_OBJECT, &dwID, sizeof(DWORD)); +} + +void CClientManager::UpdateLand(DWORD * pdw) +{ + DWORD dwID = pdw[0]; + DWORD dwGuild = pdw[1]; + + building::TLand * p = &m_vec_kLandTable[0]; + + DWORD i; + + for (i = 0; i < m_vec_kLandTable.size(); ++i, ++p) + { + if (p->dwID == dwID) + { + char buf[256]; + snprintf(buf, sizeof(buf), "UPDATE land%s SET guild_id=%u WHERE id=%u", GetTablePostfix(), dwGuild, dwID); + CDBManager::instance().AsyncQuery(buf); + + p->dwGuildID = dwGuild; + break; + } + } + + if (i < m_vec_kLandTable.size()) + ForwardPacket(HEADER_DG_UPDATE_LAND, p, sizeof(building::TLand)); +} + +void CClientManager::VCard(TPacketGDVCard * p) +{ + sys_log(0, "VCARD: %u %s %s %s %s", + p->dwID, p->szSellCharacter, p->szSellAccount, p->szBuyCharacter, p->szBuyAccount); + + m_queue_vcard.push(*p); +} + +void CClientManager::VCardProcess() +{ + if (!m_pkAuthPeer) + return; + + while (!m_queue_vcard.empty()) + { + m_pkAuthPeer->EncodeHeader(HEADER_DG_VCARD, 0, sizeof(TPacketGDVCard)); + m_pkAuthPeer->Encode(&m_queue_vcard.front(), sizeof(TPacketGDVCard)); + + m_queue_vcard.pop(); + } +} + +// BLOCK_CHAT +void CClientManager::BlockChat(TPacketBlockChat* p) +{ + char szQuery[256]; + + if (g_stLocale == "sjis") + snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s' collate sjis_japanese_ci", GetTablePostfix(), p->szName); + else + snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s'", GetTablePostfix(), p->szName); + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + SQLResult * pRes = pmsg->Get(); + + if (pRes->uiNumRows) + { + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + DWORD pid = strtoul(row[0], NULL, 10); + + TPacketGDAddAffect pa; + pa.dwPID = pid; + pa.elem.dwType = 223; + pa.elem.bApplyOn = 0; + pa.elem.lApplyValue = 0; + pa.elem.dwFlag = 0; + pa.elem.lDuration = p->lDuration; + pa.elem.lSPCost = 0; + QUERY_ADD_AFFECT(NULL, &pa); + } + else + { + // cannot find user with that name + } +} +// END_OF_BLOCK_CHAT + +void CClientManager::MarriageAdd(TPacketMarriageAdd * p) +{ + sys_log(0, "MarriageAdd %u %u %s %s", p->dwPID1, p->dwPID2, p->szName1, p->szName2); + marriage::CManager::instance().Add(p->dwPID1, p->dwPID2, p->szName1, p->szName2); +} + +void CClientManager::MarriageUpdate(TPacketMarriageUpdate * p) +{ + sys_log(0, "MarriageUpdate PID:%u %u LP:%d ST:%d", p->dwPID1, p->dwPID2, p->iLovePoint, p->byMarried); + marriage::CManager::instance().Update(p->dwPID1, p->dwPID2, p->iLovePoint, p->byMarried); +} + +void CClientManager::MarriageRemove(TPacketMarriageRemove * p) +{ + sys_log(0, "MarriageRemove %u %u", p->dwPID1, p->dwPID2); + marriage::CManager::instance().Remove(p->dwPID1, p->dwPID2); +} + +void CClientManager::WeddingRequest(TPacketWeddingRequest * p) +{ + sys_log(0, "WeddingRequest %u %u", p->dwPID1, p->dwPID2); + ForwardPacket(HEADER_DG_WEDDING_REQUEST, p, sizeof(TPacketWeddingRequest)); + //marriage::CManager::instance().RegisterWedding(p->dwPID1, p->szName1, p->dwPID2, p->szName2); +} + +void CClientManager::WeddingReady(TPacketWeddingReady * p) +{ + sys_log(0, "WeddingReady %u %u", p->dwPID1, p->dwPID2); + ForwardPacket(HEADER_DG_WEDDING_READY, p, sizeof(TPacketWeddingReady)); + marriage::CManager::instance().ReadyWedding(p->dwMapIndex, p->dwPID1, p->dwPID2); +} + +void CClientManager::WeddingEnd(TPacketWeddingEnd * p) +{ + sys_log(0, "WeddingEnd %u %u", p->dwPID1, p->dwPID2); + marriage::CManager::instance().EndWedding(p->dwPID1, p->dwPID2); +} + +// +// ijÿ ijø Ʈ ϰ ijÿ ٸ +// 켱 ͸ ε ڿ ijø Ʈ Ѵ. +// +void CClientManager::MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* pPacket) +{ + if (pPacket->byCount > SHOP_PRICELIST_MAX_NUM) + { + sys_err("count overflow!"); + return; + } + + CItemPriceListTableCache* pCache = GetItemPriceListCache(pPacket->dwOwnerID); + + if (pCache) + { + TItemPriceListTable table; + + table.dwOwnerID = pPacket->dwOwnerID; + table.byCount = pPacket->byCount; + + const TItemPriceInfo * pInfo = reinterpret_cast(pPacket + sizeof(TPacketMyshopPricelistHeader)); + thecore_memcpy(table.aPriceInfo, pInfo, sizeof(TItemPriceInfo) * pPacket->byCount); + + pCache->UpdateList(&table); + } + else + { + TItemPriceListTable* pUpdateTable = new TItemPriceListTable; + + pUpdateTable->dwOwnerID = pPacket->dwOwnerID; + pUpdateTable->byCount = pPacket->byCount; + + const TItemPriceInfo * pInfo = reinterpret_cast(pPacket + sizeof(TPacketMyshopPricelistHeader)); + thecore_memcpy(pUpdateTable->aPriceInfo, pInfo, sizeof(TItemPriceInfo) * pPacket->byCount); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT item_vnum, price FROM myshop_pricelist%s WHERE owner_id=%u", GetTablePostfix(), pPacket->dwOwnerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_LOAD_FOR_UPDATE, 0, pUpdateTable); + } +} + +// MYSHOP_PRICE_LIST +// ijõ ijø о ٷ ϰ ijÿ DB Ѵ. +// +void CClientManager::MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID) +{ + if (CItemPriceListTableCache* pCache = GetItemPriceListCache(dwPlayerID)) + { + sys_log(0, "Cache MyShopPricelist handle[%d] pid[%d]", dwHandle, dwPlayerID); + + TItemPriceListTable* pTable = pCache->Get(false); + + TPacketMyshopPricelistHeader header = + { + pTable->dwOwnerID, + pTable->byCount + }; + + size_t sizePriceListSize = sizeof(TItemPriceInfo) * pTable->byCount; + + peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, dwHandle, sizeof(header) + sizePriceListSize); + peer->Encode(&header, sizeof(header)); + peer->Encode(pTable->aPriceInfo, sizePriceListSize); + + } + else + { + sys_log(0, "Query MyShopPricelist handle[%d] pid[%d]", dwHandle, dwPlayerID); + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT item_vnum, price FROM myshop_pricelist%s WHERE owner_id=%u", GetTablePostfix(), dwPlayerID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_LOAD, peer->GetHandle(), new TItemPricelistReqInfo(dwHandle, dwPlayerID)); + } +} +// END_OF_MYSHOP_PRICE_LIST + +void CPacketInfo::Add(int header) +{ + itertype(m_map_info) it = m_map_info.find(header); + + if (it == m_map_info.end()) + m_map_info.insert(std::map::value_type(header, 1)); + else + ++it->second; +} + +void CPacketInfo::Reset() +{ + m_map_info.clear(); +} + +void CClientManager::ProcessPackets(CPeer * peer) +{ + BYTE header; + DWORD dwHandle; + DWORD dwLength; + const char * data = NULL; + int i = 0; + int iCount = 0; + + while (peer->PeekPacket(i, header, dwHandle, dwLength, &data)) + { + // DISABLE_DB_HEADER_LOG + // sys_log(0, "header %d %p size %d", header, this, dwLength); + // END_OF_DISABLE_DB_HEADER_LOG + m_bLastHeader = header; + ++iCount; + +#ifdef _TEST + if (header != 10) + sys_log(0, " ProcessPacket Header [%d] Handle[%d] Length[%d] iCount[%d]", header, dwHandle, dwLength, iCount); +#endif + if (g_test_server) + { + if (header != 10) + sys_log(0, " ProcessPacket Header [%d] Handle[%d] Length[%d] iCount[%d]", header, dwHandle, dwLength, iCount); + } + + + // test log by mhh + { + if (HEADER_GD_BLOCK_COUNTRY_IP == header) + sys_log(0, "recved : HEADER_GD_BLOCK_COUNTRY_IP"); + } + + switch (header) + { + case HEADER_GD_BOOT: + QUERY_BOOT(peer, (TPacketGDBoot *) data); + break; + + case HEADER_GD_HAMMER_OF_TOR: + break; + + case HEADER_GD_LOGIN_BY_KEY: + QUERY_LOGIN_BY_KEY(peer, dwHandle, (TPacketGDLoginByKey *) data); + break; + + case HEADER_GD_LOGOUT: + //sys_log(0, "HEADER_GD_LOGOUT (handle: %d length: %d)", dwHandle, dwLength); + QUERY_LOGOUT(peer, dwHandle, data); + break; + + case HEADER_GD_PLAYER_LOAD: + sys_log(1, "HEADER_GD_PLAYER_LOAD (handle: %d length: %d)", dwHandle, dwLength); + QUERY_PLAYER_LOAD(peer, dwHandle, (TPlayerLoadPacket *) data); + break; + + case HEADER_GD_PLAYER_SAVE: + sys_log(1, "HEADER_GD_PLAYER_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_PLAYER_SAVE(peer, dwHandle, (TPlayerTable *) data); + break; + + case HEADER_GD_PLAYER_CREATE: + sys_log(0, "HEADER_GD_PLAYER_CREATE (handle: %d length: %d)", dwHandle, dwLength); + __QUERY_PLAYER_CREATE(peer, dwHandle, (TPlayerCreatePacket *) data); + sys_log(0, "END"); + break; + + case HEADER_GD_PLAYER_DELETE: + sys_log(1, "HEADER_GD_PLAYER_DELETE (handle: %d length: %d)", dwHandle, dwLength); + __QUERY_PLAYER_DELETE(peer, dwHandle, (TPlayerDeletePacket *) data); + break; + + case HEADER_GD_PLAYER_COUNT: + QUERY_PLAYER_COUNT(peer, (TPlayerCountPacket *) data); + break; + + case HEADER_GD_QUEST_SAVE: + sys_log(1, "HEADER_GD_QUEST_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_QUEST_SAVE(peer, (TQuestTable *) data, dwLength); + break; + + case HEADER_GD_SAFEBOX_LOAD: + QUERY_SAFEBOX_LOAD(peer, dwHandle, (TSafeboxLoadPacket *) data, 0); + break; + + case HEADER_GD_SAFEBOX_SAVE: + sys_log(1, "HEADER_GD_SAFEBOX_SAVE (handle: %d length: %d)", dwHandle, dwLength); + QUERY_SAFEBOX_SAVE(peer, (TSafeboxTable *) data); + break; + + case HEADER_GD_SAFEBOX_CHANGE_SIZE: + QUERY_SAFEBOX_CHANGE_SIZE(peer, dwHandle, (TSafeboxChangeSizePacket *) data); + break; + + case HEADER_GD_SAFEBOX_CHANGE_PASSWORD: + QUERY_SAFEBOX_CHANGE_PASSWORD(peer, dwHandle, (TSafeboxChangePasswordPacket *) data); + break; + + case HEADER_GD_MALL_LOAD: + QUERY_SAFEBOX_LOAD(peer, dwHandle, (TSafeboxLoadPacket *) data, 1); + break; + + case HEADER_GD_EMPIRE_SELECT: + QUERY_EMPIRE_SELECT(peer, dwHandle, (TEmpireSelectPacket *) data); + break; + + case HEADER_GD_SETUP: + QUERY_SETUP(peer, dwHandle, data); + break; + + case HEADER_GD_GUILD_CREATE: + GuildCreate(peer, *(DWORD *) data); + break; + + case HEADER_GD_GUILD_SKILL_UPDATE: + GuildSkillUpdate(peer, (TPacketGuildSkillUpdate *) data); + break; + + case HEADER_GD_GUILD_EXP_UPDATE: + GuildExpUpdate(peer, (TPacketGuildExpUpdate *) data); + break; + + case HEADER_GD_GUILD_ADD_MEMBER: + GuildAddMember(peer, (TPacketGDGuildAddMember*) data); + break; + + case HEADER_GD_GUILD_REMOVE_MEMBER: + GuildRemoveMember(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_CHANGE_GRADE: + GuildChangeGrade(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_CHANGE_MEMBER_DATA: + GuildChangeMemberData(peer, (TPacketGuildChangeMemberData*) data); + break; + + case HEADER_GD_GUILD_DISBAND: + GuildDisband(peer, (TPacketGuild*) data); + break; + + case HEADER_GD_GUILD_WAR: + GuildWar(peer, (TPacketGuildWar*) data); + break; + + case HEADER_GD_GUILD_WAR_SCORE: + GuildWarScore(peer, (TPacketGuildWarScore*) data); + break; + + case HEADER_GD_GUILD_CHANGE_LADDER_POINT: + GuildChangeLadderPoint((TPacketGuildLadderPoint*) data); + break; + + case HEADER_GD_GUILD_USE_SKILL: + GuildUseSkill((TPacketGuildUseSkill*) data); + break; + + case HEADER_GD_FLUSH_CACHE: + QUERY_FLUSH_CACHE(peer, data); + break; + + case HEADER_GD_ITEM_SAVE: + QUERY_ITEM_SAVE(peer, data); + break; + + case HEADER_GD_ITEM_DESTROY: + QUERY_ITEM_DESTROY(peer, data); + break; + + case HEADER_GD_ITEM_FLUSH: + QUERY_ITEM_FLUSH(peer, data); + break; + + case HEADER_GD_ADD_AFFECT: + sys_log(1, "HEADER_GD_ADD_AFFECT"); + QUERY_ADD_AFFECT(peer, (TPacketGDAddAffect *) data); + break; + + case HEADER_GD_REMOVE_AFFECT: + sys_log(1, "HEADER_GD_REMOVE_AFFECT"); + QUERY_REMOVE_AFFECT(peer, (TPacketGDRemoveAffect *) data); + break; + + case HEADER_GD_HIGHSCORE_REGISTER: + QUERY_HIGHSCORE_REGISTER(peer, (TPacketGDHighscore *) data); + break; + + case HEADER_GD_PARTY_CREATE: + QUERY_PARTY_CREATE(peer, (TPacketPartyCreate*) data); + break; + + case HEADER_GD_PARTY_DELETE: + QUERY_PARTY_DELETE(peer, (TPacketPartyDelete*) data); + break; + + case HEADER_GD_PARTY_ADD: + QUERY_PARTY_ADD(peer, (TPacketPartyAdd*) data); + break; + + case HEADER_GD_PARTY_REMOVE: + QUERY_PARTY_REMOVE(peer, (TPacketPartyRemove*) data); + break; + + case HEADER_GD_PARTY_STATE_CHANGE: + QUERY_PARTY_STATE_CHANGE(peer, (TPacketPartyStateChange*) data); + break; + + case HEADER_GD_PARTY_SET_MEMBER_LEVEL: + QUERY_PARTY_SET_MEMBER_LEVEL(peer, (TPacketPartySetMemberLevel*) data); + break; + + case HEADER_GD_RELOAD_PROTO: + QUERY_RELOAD_PROTO(); + break; + + case HEADER_GD_CHANGE_NAME: + QUERY_CHANGE_NAME(peer, dwHandle, (TPacketGDChangeName *) data); + break; + + case HEADER_GD_SMS: + QUERY_SMS(peer, (TPacketGDSMS *) data); + break; + + case HEADER_GD_AUTH_LOGIN: + QUERY_AUTH_LOGIN(peer, dwHandle, (TPacketGDAuthLogin *) data); + break; + + case HEADER_GD_REQUEST_GUILD_PRIV: + AddGuildPriv((TPacketGiveGuildPriv*)data); + break; + + case HEADER_GD_REQUEST_EMPIRE_PRIV: + AddEmpirePriv((TPacketGiveEmpirePriv*)data); + break; + + case HEADER_GD_REQUEST_CHARACTER_PRIV: + AddCharacterPriv((TPacketGiveCharacterPriv*) data); + break; + + case HEADER_GD_MONEY_LOG: + MoneyLog((TPacketMoneyLog*)data); + break; + + case HEADER_GD_GUILD_DEPOSIT_MONEY: + GuildDepositMoney((TPacketGDGuildMoney*)data); + break; + + case HEADER_GD_GUILD_WITHDRAW_MONEY: + GuildWithdrawMoney(peer, (TPacketGDGuildMoney*)data); + break; + + case HEADER_GD_GUILD_WITHDRAW_MONEY_GIVE_REPLY: + GuildWithdrawMoneyGiveReply((TPacketGDGuildMoneyWithdrawGiveReply*)data); + break; + + case HEADER_GD_GUILD_WAR_BET: + GuildWarBet((TPacketGDGuildWarBet *) data); + break; + + case HEADER_GD_SET_EVENT_FLAG: + SetEventFlag((TPacketSetEventFlag*) data); + break; + + case HEADER_GD_BILLING_EXPIRE: + BillingExpire((TPacketBillingExpire *) data); + break; + + case HEADER_GD_BILLING_CHECK: + BillingCheck(data); + break; + + case HEADER_GD_CREATE_OBJECT: + CreateObject((TPacketGDCreateObject *) data); + break; + + case HEADER_GD_DELETE_OBJECT: + DeleteObject(*(DWORD *) data); + break; + + case HEADER_GD_UPDATE_LAND: + UpdateLand((DWORD *) data); + break; + + case HEADER_GD_VCARD: + VCard((TPacketGDVCard *) data); + break; + + case HEADER_GD_MARRIAGE_ADD: + MarriageAdd((TPacketMarriageAdd *) data); + break; + + case HEADER_GD_MARRIAGE_UPDATE: + MarriageUpdate((TPacketMarriageUpdate *) data); + break; + + case HEADER_GD_MARRIAGE_REMOVE: + MarriageRemove((TPacketMarriageRemove *) data); + break; + + case HEADER_GD_WEDDING_REQUEST: + WeddingRequest((TPacketWeddingRequest *) data); + break; + + case HEADER_GD_WEDDING_READY: + WeddingReady((TPacketWeddingReady *) data); + break; + + case HEADER_GD_WEDDING_END: + WeddingEnd((TPacketWeddingEnd *) data); + break; + + // BLOCK_CHAT + case HEADER_GD_BLOCK_CHAT: + BlockChat((TPacketBlockChat *) data); + break; + // END_OF_BLOCK_CHAT + + // MYSHOP_PRICE_LIST + case HEADER_GD_MYSHOP_PRICELIST_UPDATE: + MyshopPricelistUpdate((TPacketMyshopPricelistHeader*)data); + break; + + case HEADER_GD_MYSHOP_PRICELIST_REQ: + MyshopPricelistRequest(peer, dwHandle, *(DWORD*)data); + break; + // END_OF_MYSHOP_PRICE_LIST + + //RELOAD_ADMIN + case HEADER_GD_RELOAD_ADMIN: + ReloadAdmin(peer, (TPacketReloadAdmin*)data); + break; + //END_RELOAD_ADMIN + + case HEADER_GD_BREAK_MARRIAGE: + BreakMarriage(peer, data); + break; + + //MOANRCH + case HEADER_GD_ELECT_MONARCH: + Election(peer, dwHandle, data); + break; + + case HEADER_GD_CANDIDACY: + Candidacy(peer, dwHandle, data); + break; + + case HEADER_GD_ADD_MONARCH_MONEY: + AddMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_DEC_MONARCH_MONEY: + DecMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_TAKE_MONARCH_MONEY: + TakeMonarchMoney(peer, dwHandle, data); + break; + + case HEADER_GD_COME_TO_VOTE: + ComeToVote(peer, dwHandle, data); + break; + + case HEADER_GD_RMCANDIDACY: //< ĺ () + RMCandidacy(peer, dwHandle, data); + break; + + case HEADER_GD_SETMONARCH: ///<ּ () + SetMonarch(peer, dwHandle, data); + break; + + case HEADER_GD_RMMONARCH: ///<ֻ + RMMonarch(peer, dwHandle, data); + break; + //END_MONARCH + + case HEADER_GD_CHANGE_MONARCH_LORD : + ChangeMonarchLord(peer, dwHandle, (TPacketChangeMonarchLord*)data); + break; + + case HEADER_GD_BLOCK_COUNTRY_IP: + sys_log(0, "HEADER_GD_BLOCK_COUNTRY_IP received"); + CBlockCountry::instance().SendBlockedCountryIp(peer); + CBlockCountry::instance().SendBlockException(peer); + break; + + case HEADER_GD_BLOCK_EXCEPTION: + sys_log(0, "HEADER_GD_BLOCK_EXCEPTION received"); + BlockException((TPacketBlockException*) data); + break; + + case HEADER_GD_REQ_SPARE_ITEM_ID_RANGE : + SendSpareItemIDRange(peer); + break; + + case HEADER_GD_REQ_CHANGE_GUILD_MASTER : + GuildChangeMaster((TPacketChangeGuildMaster*) data); + break; + + case HEADER_GD_UPDATE_HORSE_NAME : + UpdateHorseName((TPacketUpdateHorseName*) data, peer); + break; + + case HEADER_GD_REQ_HORSE_NAME : + AckHorseName(*(DWORD*)data, peer); + break; + + case HEADER_GD_DC: + DeleteLoginKey((TPacketDC*) data); + break; + + case HEADER_GD_VALID_LOGOUT: + ResetLastPlayerID((TPacketNeedLoginLogInfo*)data); + break; + + case HEADER_GD_REQUEST_CHARGE_CASH: + ChargeCash((TRequestChargeCash*)data); + break; + + //delete gift notify icon + + case HEADER_GD_DELETE_AWARDID: + DeleteAwardId((TPacketDeleteAwardID*) data); + break; + + case HEADER_GD_UPDATE_CHANNELSTATUS: + UpdateChannelStatus((SChannelStatus*) data); + break; + case HEADER_GD_REQUEST_CHANNELSTATUS: + RequestChannelStatus(peer, dwHandle); + break; +#ifdef __AUCTION__ + case HEADER_GD_COMMAND_AUCTION: + { + TPacketGDCommnadAuction* auction_data = (TPacketGDCommnadAuction*)data; + + switch (auction_data->get_cmd()) + { + case AUCTION_ENR_AUC: + EnrollInAuction (peer, dwHandle, (AuctionEnrollProductInfo*)data); + break; + case AUCTION_ENR_SALE: + EnrollInSale (peer, dwHandle, (AuctionEnrollSaleInfo*)data); + break; + case AUCTION_ENR_WISH: + EnrollInWish (peer, dwHandle, (AuctionEnrollWishInfo*)data); + break; + case AUCTION_BID: + AuctionBid (peer, dwHandle, (AuctionBidInfo*)data); + break; + case AUCTION_IMME_PUR: + AuctionImpur (peer, dwHandle, (AuctionImpurInfo*)data); + break; + case AUCTION_GET_AUC: + AuctionGetAuctionedItem (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_BUY_SOLD: + AuctionBuySoldItem (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_CANCEL_AUC: + AuctionCancelAuction (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_CANCEL_WISH: + AuctionCancelWish (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_CANCEL_SALE: + AuctionCancelSale (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_DELETE_AUCTION_ITEM: + AuctionDeleteAuctionItem (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_DELETE_SALE_ITEM: + AuctionDeleteSaleItem (peer, dwHandle, auction_data->get_item()); + break; + case AUCTION_REBID: + AuctionReBid (peer, dwHandle, (AuctionBidInfo*)data); + break; +// case AUCTION_BID_CANCEL: +// AuctionBidCancel (peer, dwHandle, data->get_item()); + default : + break; + } + } + break; +#endif + default: + sys_err("Unknown header (header: %d handle: %d length: %d)", header, dwHandle, dwLength); + break; + } + } + + peer->RecvEnd(i); +} + +void CClientManager::AddPeer(socket_t fd) +{ + CPeer * pPeer = new CPeer; + + if (pPeer->Accept(fd)) + m_peerList.push_front(pPeer); + else + delete pPeer; +} + +void CClientManager::RemovePeer(CPeer * pPeer) +{ + if (m_pkAuthPeer == pPeer) + { + m_pkAuthPeer = NULL; + } + else + { + TLogonAccountMap::iterator it = m_map_kLogonAccount.begin(); + + while (it != m_map_kLogonAccount.end()) + { + CLoginData * pkLD = it->second; + + if (pkLD->GetConnectedPeerHandle() == pPeer->GetHandle()) + { + if (pkLD->IsPlay()) + { + pkLD->SetPlay(false); + SendLoginToBilling(pkLD, false); + } + + if (pkLD->IsDeleted()) + { + sys_log(0, "DELETING LoginData"); + delete pkLD; + } + + m_map_kLogonAccount.erase(it++); + } + else + ++it; + } + } + + m_peerList.remove(pPeer); + delete pPeer; +} + +CPeer * CClientManager::GetPeer(IDENT ident) +{ + for (itertype(m_peerList) i = m_peerList.begin(); i != m_peerList.end();++i) + { + CPeer * tmp = *i; + + if (tmp->GetHandle() == ident) + return tmp; + } + + return NULL; +} + +CPeer * CClientManager::GetAnyPeer() +{ + if (m_peerList.empty()) + return NULL; + + return m_peerList.front(); +} + +// DB Ŵ óѴ. +// +// @version 05/06/10 Bang2ni - (QID_ITEMPRICE_XXX) ߰ +int CClientManager::AnalyzeQueryResult(SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + CPeer * peer = GetPeer(qi->dwIdent); + +#ifdef _TEST + if (qi->iType != QID_ITEM_AWARD_LOAD) + sys_log(0, "AnalyzeQueryResult %d", qi->iType); +#endif + switch (qi->iType) + { + case QID_ITEM_AWARD_LOAD: + ItemAwardManager::instance().Load(msg); + delete qi; + return true; + + case QID_GUILD_RANKING: + CGuildManager::instance().ResultRanking(msg->Get()->pSQLResult); + break; + + // MYSHOP_PRICE_LIST + case QID_ITEMPRICE_LOAD_FOR_UPDATE: + RESULT_PRICELIST_LOAD_FOR_UPDATE(msg); + break; + // END_OF_MYSHOP_PRICE_LIST + } + + if (!peer) + { + //sys_err("CClientManager::AnalyzeQueryResult: peer not exist anymore. (ident: %d)", qi->dwIdent); + delete qi; + return true; + } + + switch (qi->iType) + { + case QID_PLAYER: + case QID_ITEM: + case QID_QUEST: + case QID_AFFECT: + RESULT_COMPOSITE_PLAYER(peer, msg, qi->iType); + break; + + case QID_LOGIN: + RESULT_LOGIN(peer, msg); + break; + + case QID_SAFEBOX_LOAD: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_LOAD"); + RESULT_SAFEBOX_LOAD(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_SIZE: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_SIZE"); + RESULT_SAFEBOX_CHANGE_SIZE(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_PASSWORD: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_PASSWORD %p", msg); + RESULT_SAFEBOX_CHANGE_PASSWORD(peer, msg); + break; + + case QID_SAFEBOX_CHANGE_PASSWORD_SECOND: + sys_log(0, "QUERY_RESULT: HEADER_GD_SAFEBOX_CHANGE_PASSWORD %p", msg); + RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(peer, msg); + break; + + case QID_HIGHSCORE_REGISTER: + sys_log(0, "QUERY_RESULT: HEADER_GD_HIGHSCORE_REGISTER %p", msg); + RESULT_HIGHSCORE_REGISTER(peer, msg); + break; + + case QID_SAFEBOX_SAVE: + case QID_ITEM_SAVE: + case QID_ITEM_DESTROY: + case QID_QUEST_SAVE: + case QID_PLAYER_SAVE: + case QID_ITEM_AWARD_TAKEN: + break; + + // PLAYER_INDEX_CREATE_BUG_FIX + case QID_PLAYER_INDEX_CREATE: + RESULT_PLAYER_INDEX_CREATE(peer, msg); + break; + // END_PLAYER_INDEX_CREATE_BUG_FIX + + case QID_PLAYER_DELETE: + __RESULT_PLAYER_DELETE(peer, msg); + break; + + case QID_LOGIN_BY_KEY: + RESULT_LOGIN_BY_KEY(peer, msg); + break; + + // MYSHOP_PRICE_LIST + case QID_ITEMPRICE_LOAD: + RESULT_PRICELIST_LOAD(peer, msg); + break; + // END_OF_MYSHOP_PRICE_LIST + + default: + sys_log(0, "CClientManager::AnalyzeQueryResult unknown query result type: %d, str: %s", qi->iType, msg->stQuery.c_str()); + break; + } + + delete qi; + return true; +} + +void UsageLog() +{ + FILE* fp = NULL; + + time_t ct; + char *time_s; + struct tm lt; + + int avg = g_dwUsageAvg / 3600; // 60 * 60 + + fp = fopen("usage.txt", "a+"); + + if (!fp) + return; + + ct = time(0); + lt = *localtime(&ct); + time_s = asctime(<); + + time_s[strlen(time_s) - 1] = '\0'; + + fprintf(fp, "| %4d %-15.15s | %5d | %5u |", lt.tm_year + 1900, time_s + 4, avg, g_dwUsageMax); + + fprintf(fp, "\n"); + fclose(fp); + + g_dwUsageMax = g_dwUsageAvg = 0; +} + +int CClientManager::Process() +{ + int pulses; + + if (!(pulses = thecore_idle())) + return 0; + + while (pulses--) + { + ++thecore_heart->pulse; + + /* + //30и + if (((thecore_pulse() % (60 * 30 * 10)) == 0)) + { + g_iPlayerCacheFlushSeconds = MAX(60, rand() % 180); + g_iItemCacheFlushSeconds = MAX(60, rand() % 180); + sys_log(0, "[SAVE_TIME]Change saving time item %d player %d", g_iPlayerCacheFlushSeconds, g_iItemCacheFlushSeconds); + } + */ + + if (!(thecore_heart->pulse % thecore_heart->passes_per_sec)) + { + if (g_test_server) + { + + if (!(thecore_heart->pulse % thecore_heart->passes_per_sec * 10)) + + { + pt_log("[%9d] return %d/%d/%d/%d async %d/%d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountReturnCopiedQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncCopiedQuery(SQL_PLAYER)); + + if ((thecore_heart->pulse % 50) == 0) + sys_log(0, "[%9d] return %d/%d/%d async %d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER)); + } + } + else + { + pt_log("[%9d] return %d/%d/%d/%d async %d/%d/%d%/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountReturnCopiedQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncCopiedQuery(SQL_PLAYER)); + + if ((thecore_heart->pulse % 50) == 0) + sys_log(0, "[%9d] return %d/%d/%d async %d/%d/%d", + thecore_heart->pulse, + CDBManager::instance().CountReturnQuery(SQL_PLAYER), + CDBManager::instance().CountReturnResult(SQL_PLAYER), + CDBManager::instance().CountReturnQueryFinished(SQL_PLAYER), + CDBManager::instance().CountAsyncQuery(SQL_PLAYER), + CDBManager::instance().CountAsyncResult(SQL_PLAYER), + CDBManager::instance().CountAsyncQueryFinished(SQL_PLAYER)); + } + + CDBManager::instance().ResetCounter(); + + DWORD dwCount = CClientManager::instance().GetUserCount(); + + g_dwUsageAvg += dwCount; + g_dwUsageMax = MAX(g_dwUsageMax, dwCount); + + memset(&thecore_profiler[0], 0, sizeof(thecore_profiler)); + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) + UsageLog(); + + m_iCacheFlushCount = 0; + + + //÷̾ ÷ + UpdatePlayerCache(); + // ÷ + UpdateItemCache(); + //α׾ƿ ó- ij ÷ + UpdateLogoutPlayer(); + + // MYSHOP_PRICE_LIST + UpdateItemPriceListCache(); + // END_OF_MYSHOP_PRICE_LIST + + CGuildManager::instance().Update(); + CPrivManager::instance().Update(); + marriage::CManager::instance().Update(); + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 5))) + { + ItemAwardManager::instance().RequestLoad(); + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 10))) + { + /* + char buf[4096 + 1]; + int len + itertype(g_query_info.m_map_info) it; + + ///////////////////////////////////////////////////////////////// + buf[0] = '\0'; + len = 0; + + it = g_query_info.m_map_info.begin(); + + int count = 0; + + while (it != g_query_info.m_map_info.end()) + { + len += snprintf(buf + len, sizeof(buf) - len, "%2d %3d\n", it->first, it->second); + count += it->second; + it++; + } + + pt_log("QUERY:\n%s-------------------- MAX : %d\n", buf, count); + g_query_info.Reset(); + */ + pt_log("QUERY: MAIN[%d] ASYNC[%d]", g_query_count[0], g_query_count[1]); + g_query_count[0] = 0; + g_query_count[1] = 0; + ///////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////// + /* + buf[0] = '\0'; + len = 0; + + it = g_item_info.m_map_info.begin(); + + count = 0; + while (it != g_item_info.m_map_info.end()) + { + len += snprintf(buf + len, sizeof(buf) - len, "%5d %3d\n", it->first, it->second); + count += it->second; + it++; + } + + pt_log("ITEM:\n%s-------------------- MAX : %d\n", buf, count); + g_item_info.Reset(); + */ + pt_log("ITEM:%d\n", g_item_count); + g_item_count = 0; + ///////////////////////////////////////////////////////////////// + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 60))) // 60ʿ ѹ + { + // ũ ð . + CClientManager::instance().SendTime(); + } + + if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) // ѽð ѹ + { + CMoneyLog::instance().Save(); + } + } + + int num_events = fdwatch(m_fdWatcher, 0); + int idx; + CPeer * peer; + + for (idx = 0; idx < num_events; ++idx) // Dz + { + peer = (CPeer *) fdwatch_get_client_data(m_fdWatcher, idx); + + if (!peer) + { + if (fdwatch_check_event(m_fdWatcher, m_fdAccept, idx) == FDW_READ) + { + AddPeer(m_fdAccept); + fdwatch_clear_event(m_fdWatcher, m_fdAccept, idx); + } + else + { + sys_err("FDWATCH: peer null in event: ident %d", fdwatch_get_ident(m_fdWatcher, idx)); + } + + continue; + } + + switch (fdwatch_check_event(m_fdWatcher, peer->GetFd(), idx)) + { + case FDW_READ: + if (peer->Recv() < 0) + { + sys_err("Recv failed"); + RemovePeer(peer); + } + else + { + if (peer == m_pkAuthPeer) + if (g_log) + sys_log(0, "AUTH_PEER_READ: size %d", peer->GetRecvLength()); + + ProcessPackets(peer); + } + break; + + case FDW_WRITE: + if (peer == m_pkAuthPeer) + if (g_log) + sys_log(0, "AUTH_PEER_WRITE: size %d", peer->GetSendLength()); + + if (peer->Send() < 0) + { + sys_err("Send failed"); + RemovePeer(peer); + } + + break; + + case FDW_EOF: + RemovePeer(peer); + break; + + default: + sys_err("fdwatch_check_fd returned unknown result"); + RemovePeer(peer); + break; + } + } + +#ifdef __WIN32__ + if (_kbhit()) { + int c = _getch(); + switch (c) { + case 0x1b: // Esc + return 0; // shutdown + break; + default: + break; + } + } +#endif + + VCardProcess(); + return 1; +} + +DWORD CClientManager::GetUserCount() +{ + // ܼ α īƮ .. --; + return m_map_kLogonAccount.size(); +} + +void CClientManager::SendAllGuildSkillRechargePacket() +{ + ForwardPacket(HEADER_DG_GUILD_SKILL_RECHARGE, NULL, 0); +} + +void CClientManager::SendTime() +{ + time_t now = GetCurrentTime(); + ForwardPacket(HEADER_DG_TIME, &now, sizeof(time_t)); +} + +void CClientManager::ForwardPacket(BYTE header, const void* data, int size, BYTE bChannel, CPeer* except) +{ + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * peer = *it; + + if (peer == except) + continue; + + if (!peer->GetChannel()) + continue; + + if (bChannel && peer->GetChannel() != bChannel) + continue; + + peer->EncodeHeader(header, 0, size); + + if (size > 0 && data) + peer->Encode(data, size); + } +} + +void CClientManager::SendNotice(const char * c_pszFormat, ...) +{ + char szBuf[255+1]; + va_list args; + + va_start(args, c_pszFormat); + int len = vsnprintf(szBuf, sizeof(szBuf), c_pszFormat, args); + va_end(args); + szBuf[len] = '\0'; + + ForwardPacket(HEADER_DG_NOTICE, szBuf, len + 1); +} + +time_t CClientManager::GetCurrentTime() +{ + return time(0); +} + +// ITEM_UNIQUE_ID +bool CClientManager::InitializeNowItemID() +{ + DWORD dwMin, dwMax; + + // ID ʱȭ Ѵ. + if (!CConfig::instance().GetTwoValue("ITEM_ID_RANGE", &dwMin, &dwMax)) + { + sys_err("conf.txt: Cannot find ITEM_ID_RANGE [start_item_id] [end_item_id]"); + return false; + } + + sys_log(0, "ItemRange From File %u ~ %u ", dwMin, dwMax); + + if (CItemIDRangeManager::instance().BuildRange(dwMin, dwMax, m_itemRange) == false) + { + sys_err("Can not build ITEM_ID_RANGE"); + return false; + } + + sys_log(0, " Init Success Start %u End %u Now %u\n", m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin); + + return true; +} + +DWORD CClientManager::GainItemID() +{ + return m_itemRange.dwUsableItemIDMin++; +} + +DWORD CClientManager::GetItemID() +{ + return m_itemRange.dwUsableItemIDMin; +} +// ITEM_UNIQUE_ID_END +//BOOT_LOCALIZATION + +bool CClientManager::InitializeLocalization() +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "SELECT mValue, mKey FROM locale"); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err("InitializeLocalization() ==> DirectQuery failed(%s)", szQuery); + delete pMsg; + return false; + } + + sys_log(0, "InitializeLocalization() - LoadLocaleTable(count:%d)", pMsg->Get()->uiNumRows); + + m_vec_Locale.clear(); + + MYSQL_ROW row = NULL; + + for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + { + int col = 0; + tLocale locale; + + strlcpy(locale.szValue, row[col++], sizeof(locale.szValue)); + strlcpy(locale.szKey, row[col++], sizeof(locale.szKey)); + + //DB_NAME_COLUMN Setting + if (strcmp(locale.szKey, "LOCALE") == 0) + { + if (strcmp(locale.szValue, "cibn") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "gb2312"); + + g_stLocale = "gb2312"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "ymir") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "japan") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "sjis"); + + g_stLocale = "sjis"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "english") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = ""; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "germany") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "france") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "italy") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "spain") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "uk") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "turkey") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin5"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "poland") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "portugal") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "hongkong") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "big5"); + + g_stLocale = "big5"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "newcibn") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "gb2312"); + + g_stLocale = "gb2312"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "korea") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "canada") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "gb2312name"; + } + else if (strcmp(locale.szValue, "brazil") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "greek") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "greek"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "russia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "denmark") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "bulgaria") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "croatia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "cp1251"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "mexico") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "arabia") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "cp1256"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "czech") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "hungary") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "romania") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin2"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "netherlands") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "singapore") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "vietnam") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "thailand") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "usa") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "latin1"); + + g_stLocale = "latin1"; + g_stLocaleNameColumn = "locale_name"; + } + else if (strcmp(locale.szValue, "we_korea") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "euckr"); + + g_stLocale = "euckr"; + g_stLocaleNameColumn = "name"; + } + else if (strcmp(locale.szValue, "taiwan") == 0) + { + sys_log(0, "locale[LOCALE] = %s", locale.szValue); + + if (g_stLocale != locale.szValue) + sys_log(0, "Changed g_stLocale %s to %s", g_stLocale.c_str(), "big5"); + g_stLocale = "big5"; + g_stLocaleNameColumn = "locale_name"; + } + else + { + sys_err("locale[LOCALE] = UNKNOWN(%s)", locale.szValue); + exit(0); + } + + CDBManager::instance().SetLocale(g_stLocale.c_str()); + } + else if (strcmp(locale.szKey, "DB_NAME_COLUMN") == 0) + { + sys_log(0, "locale[DB_NAME_COLUMN] = %s", locale.szValue); + g_stLocaleNameColumn = locale.szValue; + } + else + { + sys_log(0, "locale[UNKNOWN_KEY(%s)] = %s", locale.szKey, locale.szValue); + } + m_vec_Locale.push_back(locale); + } + + delete pMsg; + + return true; +} +//END_BOOT_LOCALIZATION +//ADMIN_MANAGER + +bool CClientManager::__GetAdminInfo(const char *szIP, std::vector & rAdminVec) +{ + //szIP == NULL ϰ 缭  ´. + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), + "SELECT mID,mAccount,mName,mContactIP,mServerIP,mAuthority FROM gmlist WHERE mServerIP='ALL' or mServerIP='%s'", + szIP ? szIP : "ALL"); + + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err("__GetAdminInfo() ==> DirectQuery failed(%s)", szQuery); + delete pMsg; + return false; + } + + MYSQL_ROW row; + rAdminVec.reserve(pMsg->Get()->uiNumRows); + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + int idx = 0; + tAdminInfo Info; + + str_to_number(Info.m_ID, row[idx++]); + trim_and_lower(row[idx++], Info.m_szAccount, sizeof(Info.m_szAccount)); + strlcpy(Info.m_szName, row[idx++], sizeof(Info.m_szName)); + strlcpy(Info.m_szContactIP, row[idx++], sizeof(Info.m_szContactIP)); + strlcpy(Info.m_szServerIP, row[idx++], sizeof(Info.m_szServerIP)); + std::string stAuth = row[idx++]; + + if (!stAuth.compare("IMPLEMENTOR")) + Info.m_Authority = GM_IMPLEMENTOR; + else if (!stAuth.compare("GOD")) + Info.m_Authority = GM_GOD; + else if (!stAuth.compare("HIGH_WIZARD")) + Info.m_Authority = GM_HIGH_WIZARD; + else if (!stAuth.compare("LOW_WIZARD")) + Info.m_Authority = GM_LOW_WIZARD; + else if (!stAuth.compare("WIZARD")) + Info.m_Authority = GM_WIZARD; + else + continue; + + rAdminVec.push_back(Info); + + sys_log(0, "GM: PID %u Login %s Character %s ContactIP %s ServerIP %s Authority %d[%s]", + Info.m_ID, Info.m_szAccount, Info.m_szName, Info.m_szContactIP, Info.m_szServerIP, Info.m_Authority, stAuth.c_str()); + } + + delete pMsg; + + return true; +} + +bool CClientManager::__GetHostInfo(std::vector & rIPVec) +{ + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "SELECT mIP FROM gmhost"); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err("__GetHostInfo() ==> DirectQuery failed(%s)", szQuery); + delete pMsg; + return false; + } + + rIPVec.reserve(pMsg->Get()->uiNumRows); + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + { + if (row[0] && *row[0]) + { + rIPVec.push_back(row[0]); + sys_log(0, "GMHOST: %s", row[0]); + } + } + + delete pMsg; + return true; +} +//END_ADMIN_MANAGER + +void CClientManager::ReloadAdmin(CPeer*, TPacketReloadAdmin* p) +{ + std::vector vAdmin; + std::vector vHost; + + __GetHostInfo(vHost); + __GetAdminInfo(p->szIP, vAdmin); + + DWORD dwPacketSize = sizeof(WORD) + sizeof (WORD) + sizeof(tAdminInfo) * vAdmin.size() + + sizeof(WORD) + sizeof(WORD) + 16 * vHost.size(); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * peer = *it; + + if (!peer->GetChannel()) + continue; + + peer->EncodeHeader(HEADER_DG_RELOAD_ADMIN, 0, dwPacketSize); + + peer->EncodeWORD(16); + peer->EncodeWORD(vHost.size()); + + for (size_t n = 0; n < vHost.size(); ++n) + peer->Encode(vHost[n].c_str(), 16); + + peer->EncodeWORD(sizeof(tAdminInfo)); + peer->EncodeWORD(vAdmin.size()); + + for (size_t n = 0; n < vAdmin.size(); ++n) + peer->Encode(&vAdmin[n], sizeof(tAdminInfo)); + } + + sys_log(0, "ReloadAdmin End %s", p->szIP); +} + +//BREAK_MARRIAGE +void CClientManager::BreakMarriage(CPeer * peer, const char * data) +{ + DWORD pid1, pid2; + + pid1 = *(int *) data; + data += sizeof(int); + + pid2 = *(int *) data; + data += sizeof(int); + + sys_log(0, "Breaking off a marriage engagement! pid %d and pid %d", pid1, pid2); + marriage::CManager::instance().Remove(pid1, pid2); +} +//END_BREAK_MARIIAGE + +void CClientManager::UpdateItemCacheSet(DWORD pid) +{ + itertype(m_map_pkItemCacheSetPtr) it = m_map_pkItemCacheSetPtr.find(pid); + + if (it == m_map_pkItemCacheSetPtr.end()) + { + if (g_test_server) + sys_log(0, "UPDATE_ITEMCACHESET : UpdateItemCacheSet ==> No ItemCacheSet pid(%d)", pid); + return; + } + + TItemCacheSet * pSet = it->second; + TItemCacheSet::iterator it_set = pSet->begin(); + + while (it_set != pSet->end()) + { + CItemCache * c = *it_set++; + c->Flush(); + } + + if (g_log) + sys_log(0, "UPDATE_ITEMCACHESET : UpdateItemCachsSet pid(%d)", pid); +} + +void CClientManager::Election(CPeer * peer, DWORD dwHandle, const char* data) +{ + DWORD idx; + DWORD selectingpid; + + idx = *(DWORD *) data; + data += sizeof(DWORD); + + selectingpid = *(DWORD *) data; + data += sizeof(DWORD); + + int Success = 0; + + if (!(Success = CMonarch::instance().VoteMonarch(selectingpid, idx))) + { + if (g_test_server) + sys_log(0, "[MONARCH_VOTE] Failed %d %d", idx, selectingpid); + peer->EncodeHeader(HEADER_DG_ELECT_MONARCH, dwHandle, sizeof(int)); + peer->Encode(&Success, sizeof(int)); + return; + } + else + { + if (g_test_server) + sys_log(0, "[MONARCH_VOTE] Success %d %d", idx, selectingpid); + peer->EncodeHeader(HEADER_DG_ELECT_MONARCH, dwHandle, sizeof(int)); + peer->Encode(&Success, sizeof(int)); + return; + } + +} +void CClientManager::Candidacy(CPeer * peer, DWORD dwHandle, const char* data) +{ + DWORD pid; + + pid = *(DWORD *) data; + data += sizeof(DWORD); + + if (!CMonarch::instance().AddCandidacy(pid, data)) + { + if (g_test_server) + sys_log(0, "[MONARCH_CANDIDACY] Failed %d %s", pid, data); + + peer->EncodeHeader(HEADER_DG_CANDIDACY, dwHandle, sizeof(int) + 32); + peer->Encode(0, sizeof(int)); + peer->Encode(data, 32); + return; + } + else + { + if (g_test_server) + sys_log(0, "[MONARCH_CANDIDACY] Success %d %s", pid, data); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (0 && p->GetChannel() != 0) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_CANDIDACY, dwHandle, sizeof(int) + 32); + p->Encode(&pid, sizeof(int)); + p->Encode(data, 32); + } + else + { + p->EncodeHeader(HEADER_DG_CANDIDACY, 0, sizeof(int) + 32); + p->Encode(&pid, sizeof(int)); + p->Encode(data, 32); + } + } + } +} + +void CClientManager::AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Add money Empire(%d) Money(%d)", Empire, Money); + + CMonarch::instance().AddMoney(Empire, Money); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_ADD_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + p->Encode(&Empire, sizeof(int)); + p->Encode(&Money, sizeof(int)); + } + else + { + p->EncodeHeader(HEADER_DG_ADD_MONARCH_MONEY, 0, sizeof(int) + sizeof(int)); + p->Encode(&Empire, sizeof(int)); + p->Encode(&Money, sizeof(int)); + } + + } +} +void CClientManager::DecMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Dec money Empire(%d) Money(%d)", Empire, Money); + + CMonarch::instance().DecMoney(Empire, Money); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_DEC_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + p->Encode(&Empire, sizeof(int)); + p->Encode(&Money, sizeof(int)); + } + else + { + p->EncodeHeader(HEADER_DG_DEC_MONARCH_MONEY, 0, sizeof(int) + sizeof(int)); + p->Encode(&Empire, sizeof(int)); + p->Encode(&Money, sizeof(int)); + } + } +} + +void CClientManager::TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char * data) +{ + int Empire = *(int *) data; + data += sizeof(int); + + DWORD pid = *(DWORD *) data; + data += sizeof(int); + + int Money = *(int *) data; + data += sizeof(int); + + if (g_test_server) + sys_log(0, "[MONARCH] Take money Empire(%d) Money(%d)", Empire, Money); + + if (CMonarch::instance().TakeMoney(Empire, pid, Money) == true) + { + peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + peer->Encode(&Empire, sizeof(int)); + peer->Encode(&Money, sizeof(int)); + } + else + { + Money = 0; + peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); + peer->Encode(&Empire, sizeof(int)); + peer->Encode(&Money, sizeof(int)); + } +} + +void CClientManager::ComeToVote(CPeer * peer, DWORD dwHandle, const char * data) +{ + CMonarch::instance().ElectMonarch(); +} + +void CClientManager::RMCandidacy(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + sys_log(0, "[MONARCH_GM] Remove candidacy name(%s)", szName); + + int iRet = CMonarch::instance().DelCandidacy(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + else + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } +} + +void CClientManager::SetMonarch(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + + if (g_test_server) + sys_log(0, "[MONARCH_GM] Set Monarch name(%s)", szName); + + int iRet = CMonarch::instance().SetMonarch(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + else + { + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } +} + +void CClientManager::RMMonarch(CPeer * peer, DWORD dwHandle, const char * data) +{ + char szName[32]; + + strlcpy(szName, data, sizeof(szName)); + + if (g_test_server) + sys_log(0, "[MONARCH_GM] Remove Monarch name(%s)", szName); + + CMonarch::instance().DelMonarch(szName); + + int iRet = CMonarch::instance().DelMonarch(szName) ? 1 : 0; + + if (1 == iRet) + { + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer * p = *it; + + if (!p->GetChannel()) + continue; + + if (p == peer) + { + p->EncodeHeader(HEADER_DG_RMMONARCH, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + else + { + p->EncodeHeader(HEADER_DG_RMMONARCH, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } + } + } + else + { + CPeer * p = peer; + p->EncodeHeader(HEADER_DG_RMCANDIDACY, dwHandle, sizeof(int) + sizeof(szName)); + p->Encode(&iRet, sizeof(int)); + p->Encode(szName, sizeof(szName)); + } +} + +void CClientManager::ChangeMonarchLord(CPeer * peer, DWORD dwHandle, TPacketChangeMonarchLord* info) +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "SELECT a.name, NOW() FROM player%s AS a, player_index%s AS b WHERE (a.account_id=b.id AND a.id=%u AND b.empire=%u) AND " + "(b.pid1=%u OR b.pid2=%u OR b.pid3=%u OR b.pid4=%u)", + GetTablePostfix(), GetTablePostfix(), info->dwPID, info->bEmpire, + info->dwPID, info->dwPID, info->dwPID, info->dwPID); + + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows != 0) + { + TPacketChangeMonarchLordACK ack; + ack.bEmpire = info->bEmpire; + ack.dwPID = info->dwPID; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + strlcpy(ack.szName, row[0], sizeof(ack.szName)); + strlcpy(ack.szDate, row[1], sizeof(ack.szDate)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch SET pid=%u, windate=NOW() WHERE empire=%d", ack.dwPID, ack.bEmpire); + SQLMsg* pMsg2 = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg2->Get()->uiAffectedRows > 0) + { + CMonarch::instance().LoadMonarch(); + + TMonarchInfo* newInfo = CMonarch::instance().GetMonarch(); + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); it++) + { + CPeer* client = *it; + + client->EncodeHeader(HEADER_DG_CHANGE_MONARCH_LORD_ACK, 0, sizeof(TPacketChangeMonarchLordACK)); + client->Encode(&ack, sizeof(TPacketChangeMonarchLordACK)); + + client->EncodeHeader(HEADER_DG_UPDATE_MONARCH_INFO, 0, sizeof(TMonarchInfo)); + client->Encode(newInfo, sizeof(TMonarchInfo)); + } + } + + delete pMsg2; + } + + delete pMsg; +} + +void CClientManager::BlockException(TPacketBlockException *data) +{ + sys_log(0, "[BLOCK_EXCEPTION] CMD(%d) login(%s)", data->cmd, data->login); + + // save sql + { + char buf[1024]; + + switch (data->cmd) + { + case BLOCK_EXCEPTION_CMD_ADD: + snprintf(buf, sizeof(buf), "INSERT INTO block_exception VALUES('%s')", data->login); + CDBManager::instance().AsyncQuery(buf, SQL_ACCOUNT); + CBlockCountry::instance().AddBlockException(data->login); + break; + case BLOCK_EXCEPTION_CMD_DEL: + snprintf(buf, sizeof(buf), "DELETE FROM block_exception VALUES('%s')", data->login); + CDBManager::instance().AsyncQuery(buf, SQL_ACCOUNT); + CBlockCountry::instance().DelBlockException(data->login); + break; + default: + return; + } + + } + + for (itertype(m_peerList) it = m_peerList.begin(); it != m_peerList.end(); ++it) + { + CPeer *peer = *it; + + if (!peer->GetChannel()) + continue; + + CBlockCountry::instance().SendBlockExceptionOne(peer, data->login, data->cmd); + } +} + +void CClientManager::SendSpareItemIDRange(CPeer* peer) +{ + peer->SendSpareItemIDRange(); +} + +// +// Login Key ʿ . +// +void CClientManager::DeleteLoginKey(TPacketDC *data) +{ + char login[LOGIN_MAX_LEN+1] = {0}; + trim_and_lower(data->login, login, sizeof(login)); + + CLoginData *pkLD = GetLoginDataByLogin(login); + + if (pkLD) + { + TLoginDataByLoginKey::iterator it = m_map_pkLoginData.find(pkLD->GetKey()); + + if (it != m_map_pkLoginData.end()) + m_map_pkLoginData.erase(it); + } +} + +// delete gift notify icon +void CClientManager::DeleteAwardId(TPacketDeleteAwardID *data) +{ + //sys_log(0,"data from game server arrived %d",data->dwID); + std::map::iterator it; + it = ItemAwardManager::Instance().GetMapAward().find(data->dwID); + if ( it != ItemAwardManager::Instance().GetMapAward().end() ) + { + std::set & kSet = ItemAwardManager::Instance().GetMapkSetAwardByLogin()[it->second->szLogin]; + if(kSet.erase(it->second)) + sys_log(0,"erase ItemAward id: %d from cache", data->dwID); + ItemAwardManager::Instance().GetMapAward().erase(data->dwID); + } + else + { + sys_log(0,"DELETE_AWARDID : could not find the id: %d", data->dwID); + } + +} + +void CClientManager::UpdateChannelStatus(TChannelStatus* pData) +{ + TChannelStatusMap::iterator it = m_mChannelStatus.find(pData->nPort); + if (it != m_mChannelStatus.end()) { + it->second = pData->bStatus; + } + else { + m_mChannelStatus.insert(TChannelStatusMap::value_type(pData->nPort, pData->bStatus)); + } +} + +void CClientManager::RequestChannelStatus(CPeer* peer, DWORD dwHandle) +{ + const int nSize = m_mChannelStatus.size(); + peer->EncodeHeader(HEADER_DG_RESPOND_CHANNELSTATUS, dwHandle, sizeof(TChannelStatus)*nSize+sizeof(int)); + peer->Encode(&nSize, sizeof(int)); + for (TChannelStatusMap::iterator it = m_mChannelStatus.begin(); it != m_mChannelStatus.end(); it++) { + peer->Encode(&it->first, sizeof(short)); + peer->Encode(&it->second, sizeof(BYTE)); + } +} + +void CClientManager::ResetLastPlayerID(const TPacketNeedLoginLogInfo* data) +{ + CLoginData* pkLD = GetLoginDataByAID( data->dwPlayerID ); + + if (NULL != pkLD) + { + pkLD->SetLastPlayerID( 0 ); + } +} + +void CClientManager::ChargeCash(const TRequestChargeCash* packet) +{ + char szQuery[512]; + + if (ERequestCharge_Cash == packet->eChargeType) + sprintf(szQuery, "update account set `cash` = `cash` + %d where id = %d limit 1", packet->dwAmount, packet->dwAID); + else if(ERequestCharge_Mileage == packet->eChargeType) + sprintf(szQuery, "update account set `mileage` = `mileage` + %d where id = %d limit 1", packet->dwAmount, packet->dwAID); + else + { + sys_err ("Invalid request charge type (type : %d, amount : %d, aid : %d)", packet->eChargeType, packet->dwAmount, packet->dwAID); + return; + } + + sys_err ("Request Charge (type : %d, amount : %d, aid : %d)", packet->eChargeType, packet->dwAmount, packet->dwAID); + + CDBManager::Instance().AsyncQuery(szQuery, SQL_ACCOUNT); +} + +#ifdef __AUCTION__ +void CClientManager::EnrollInAuction (CPeer * peer, DWORD owner_id, AuctionEnrollProductInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (owner_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", owner_id); + return; + } + CItemCache* c = GetItemCache (data->get_item_id()); + + if (c == NULL) + { + sys_err ("Item %d doesn't exist in db cache.", data->get_item_id()); + return; + } + TPlayerItem* item = c->Get(false); + + if (item->owner != owner_id) + { + sys_err ("Player id %d doesn't have item %d.", owner_id, data->get_item_id()); + return; + } + // ð + 24ð . + time_t expired_time = time(0) + 24 * 60 * 60; + TAuctionItemInfo auctioned_item_info (item->vnum, data->get_bid_price(), + data->get_impur_price(), owner_id, "", expired_time, data->get_item_id(), 0, data->get_empire()); + + AuctionResult result = AuctionManager::instance().EnrollInAuction( c, auctioned_item_info ); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_AUC; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, owner_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + peer->Encode(c->Get(false), sizeof(TPlayerItem)); + } + else + { + // ɽø Auction ClientManager . + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(item->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + it->second->erase(c); + } + m_map_itemCache.erase(item->id); + sys_log(0, "Enroll In Auction Success. owner_id item_id %d %d", owner_id, item->id); + + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_AUC; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, owner_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem) + sizeof(TAuctionItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(c->Get(false), sizeof(TPlayerItem)); + (*it)->Encode(&auctioned_item_info, sizeof(TAuctionItemInfo)); + } + } + + return; +} + +void CClientManager::EnrollInSale (CPeer * peer, DWORD owner_id, AuctionEnrollSaleInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (owner_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", owner_id); + return; + } + + CPlayerTableCache* player_cache = it->second; + TPlayerTable* player = player_cache->Get(false); + + CItemCache* c = GetItemCache (data->get_item_id()); + + if (c == NULL) + { + sys_err ("Item %d doesn't exist in db cache.", data->get_item_id()); + return; + } + TPlayerItem* item = c->Get(false); + + if (item->owner != owner_id) + { + sys_err ("Player id %d doesn't have item %d.", owner_id, data->get_item_id()); + return; + } + // ð + 24ð . + time_t expired_time = time(0) + 24 * 60 * 60; + TSaleItemInfo sold_item_info (item->vnum, data->get_sale_price(), + owner_id, player->name, data->get_item_id(), data->get_wisher_id()); + + AuctionResult result = AuctionManager::instance().EnrollInSale( c, sold_item_info ); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_SALE; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, owner_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + peer->Encode(c->Get(false), sizeof(TPlayerItem)); + } + else + { + // ɽø Auction ClientManager . + TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(item->owner); + + if (it != m_map_pkItemCacheSetPtr.end()) + { + it->second->erase(c); + } + m_map_itemCache.erase(item->id); + sys_log(0, "Enroll In Sale Success. owner_id item_id %d %d", owner_id, item->id); + + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_SALE; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, owner_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem) + sizeof(TSaleItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(c->Get(false), sizeof(TPlayerItem)); + (*it)->Encode(&sold_item_info, sizeof(TSaleItemInfo)); + } + } + + return; +} + +void CClientManager::EnrollInWish (CPeer * peer, DWORD wisher_id, AuctionEnrollWishInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (wisher_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", wisher_id); + return; + } + + CPlayerTableCache* player_cache = it->second; + TPlayerTable* player = player_cache->Get(false); + + // ð + 24ð . + time_t expired_time = time(0) + 24 * 60 * 60; + TWishItemInfo wished_item_info (data->get_item_num(), data->get_wish_price(), wisher_id, player->name, expired_time, data->get_empire()); + + AuctionResult result = AuctionManager::instance().EnrollInWish ( wished_item_info ); + + if (result <= AUCTION_FAIL) + { + sys_log(0, "Enroll In Wish Success. wisher_id item_num %d %d", wisher_id, data->get_item_num()); + + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_WISH; + enroll_result.target = data->get_item_num(); + enroll_result.result = result; + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, wisher_id, sizeof(TPacketDGResultAuction)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + sys_log(0, "Enroll In Wish Fail. wisher_id item_num %d %d", wisher_id, data->get_item_num()); + + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_ENR_WISH; + enroll_result.target = data->get_item_num(); + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, wisher_id, sizeof(TPacketDGResultAuction) + sizeof(TWishItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(&wished_item_info, sizeof(TWishItemInfo)); + } + } + + return; +} + +void CClientManager::AuctionBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (bidder_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", bidder_id); + return; + } + + CPlayerTableCache* player_cache = it->second; + TPlayerTable* player = player_cache->Get(false); + + AuctionResult result = AuctionManager::instance().Bid(bidder_id, player->name, data->get_item_id(), data->get_bid_price()); + + if (result == AUCTION_FAIL) + { + sys_log(0, "Bid Fail. bidder_id item_id %d %d", bidder_id, data->get_item_id()); + } + else + { + sys_log(0, "Bid Success. bidder_id item_id %d %d", bidder_id, data->get_item_id()); + } + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BID; + enroll_result.target = data->get_bid_price(); + enroll_result.result = result; + + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction) + sizeof(AuctionBidInfo)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BID; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + TAuctionItemInfo* auctioned_item_info = AuctionManager::instance().GetAuctionItemInfoCache(data->get_item_id())->Get(false); + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction) + sizeof(TAuctionItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(auctioned_item_info, sizeof(TAuctionItemInfo)); + } + + } + return; +} + +void CClientManager::AuctionImpur (CPeer * peer, DWORD purchaser_id, AuctionImpurInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (purchaser_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", purchaser_id); + return; + } + + CPlayerTableCache* player_cache = it->second; + TPlayerTable* player = player_cache->Get(false); + + AuctionResult result = AuctionManager::instance().Impur(purchaser_id, player->name, data->get_item_id()); + + if (result == AUCTION_FAIL) + { + sys_log(0, "Impur Fail. purchaser_id item_id %d %d", purchaser_id, data->get_item_id()); + } + else + { + sys_log(0, "Impur Success. purchaser_id item_id %d %d", purchaser_id, data->get_item_id()); + } + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_IMME_PUR; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, purchaser_id, sizeof(TPacketDGResultAuction)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_IMME_PUR; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + TAuctionItemInfo* auctioned_item_info = AuctionManager::instance().GetAuctionItemInfoCache(data->get_item_id())->Get(false); + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, purchaser_id, sizeof(TPacketDGResultAuction) + sizeof(TAuctionItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(auctioned_item_info, sizeof(TAuctionItemInfo)); + } + } + return; +} + +void CClientManager::AuctionGetAuctionedItem (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + TPlayerItem item; + result = AuctionManager::instance().GetAuctionedItem(actor_id, item_id, item); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_GET_AUC; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + peer->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_GET_AUC; + enroll_result.target = item_id; + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + (*it)->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode (&item, sizeof(TPlayerItem)); + } + } + return; +} + +void CClientManager::AuctionBuySoldItem (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + TPlayerItem item; + result = AuctionManager::instance().BuySoldItem(actor_id, item_id, item); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BUY_SOLD; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + peer->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BUY_SOLD; + enroll_result.target = item_id; + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + (*it)->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode (&item, sizeof(TPlayerItem)); + } + } + return; +} + +void CClientManager::AuctionCancelAuction (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + TPlayerItem item; + result = AuctionManager::instance().CancelAuction(actor_id, item_id, item); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_AUC; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + peer->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_AUC; + enroll_result.target = item_id; + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + (*it)->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode (&item, sizeof(TPlayerItem)); + } + } + return; +} + +void CClientManager::AuctionCancelWish (CPeer * peer, DWORD actor_id, DWORD item_num) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + TPlayerItem item; + result = AuctionManager::instance().CancelWish(actor_id, item_num); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_WISH; + enroll_result.target = item_num; + enroll_result.result = result; + + peer->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + peer->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_WISH; + enroll_result.target = item_num; + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + (*it)->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + } + return; +} + +void CClientManager::AuctionCancelSale (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + TPlayerItem item; + result = AuctionManager::instance().CancelSale(actor_id, item_id, item); + + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_SALE; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction)); + peer->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_CANCEL_SALE; + enroll_result.target = item_id; + enroll_result.result = result; + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader (HEADER_DG_AUCTION_RESULT, actor_id, sizeof(TPacketDGResultAuction) + sizeof(TPlayerItem)); + (*it)->Encode (&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode (&item, sizeof(TPlayerItem)); + } + } + return; +} + +void CClientManager::AuctionDeleteAuctionItem (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + AuctionManager::instance().DeleteAuctionItem (actor_id, item_id); +} +void CClientManager::AuctionDeleteSaleItem (CPeer * peer, DWORD actor_id, DWORD item_id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (actor_id); + AuctionResult result = AUCTION_FAIL; + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", actor_id); + return; + } + + AuctionManager::instance().DeleteSaleItem (actor_id, item_id); +} + +// ReBid ݾ׿ ؼ Ѵ. +// ReBid data->bid_price +// ݾ rebidϴ . +// ̷ rebid , +// ȣָӴϿ ֱ ϰ ϱ ̴. + +void CClientManager::AuctionReBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find (bidder_id); + + if (it == m_map_playerCache.end()) + { + sys_err ("Invalid Player id %d. how can you get it?", bidder_id); + return; + } + + CPlayerTableCache* player_cache = it->second; + TPlayerTable* player = player_cache->Get(false); + + AuctionResult result = AuctionManager::instance().ReBid(bidder_id, player->name, data->get_item_id(), data->get_bid_price()); + + if (result == AUCTION_FAIL) + { + sys_log(0, "ReBid Fail. bidder_id item_id %d %d", bidder_id, data->get_item_id()); + } + else + { + sys_log(0, "ReBid Success. bidder_id item_id %d %d", bidder_id, data->get_item_id()); + } + // ̰ FAIL ȵ. + // FAIL °, MyBid ִ bidder_id bidder_id ְŵ? + // ׷Ƿ ٸ ۵Ѵٰ Ѵٸ + // bidder_id MyBid Ѵ , װ ȭ . + // ٸ Ȱ bidder_id ϱ. + // ׷Ƿ BidCancel db ȴٴ , + // ̹ κп ؼ ˻簡 Ϻϴٴ ̾. + // ׷ Ȥó ;, fail ڵ带 ܵд. + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_REBID; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction) + sizeof(int)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + peer->EncodeDWORD(data->get_bid_price()); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_REBID; + enroll_result.target = data->get_item_id(); + enroll_result.result = result; + + TAuctionItemInfo* auctioned_item_info = AuctionManager::instance().GetAuctionItemInfoCache(data->get_item_id())->Get(false); + + for (TPeerList::iterator it = m_peerList.begin(); it != m_peerList.end(); it++) + { + (*it)->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction) + sizeof(TAuctionItemInfo)); + (*it)->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + (*it)->Encode(auctioned_item_info, sizeof(TAuctionItemInfo)); + } + } + return; +} + +void CClientManager::AuctionBidCancel (CPeer * peer, DWORD bidder_id, DWORD item_id) +{ + AuctionResult result = AuctionManager::instance().BidCancel (bidder_id, item_id); + + // ̰ FAIL ȵ. + // FAIL °, MyBid ִ bidder_id bidder_id ְŵ? + // ׷Ƿ ٸ ۵Ѵٰ Ѵٸ + // bidder_id MyBid Ѵ , װ ȭ . + // ٸ Ȱ bidder_id ϱ. + // ׷Ƿ BidCancel db ȴٴ , + // ̹ κп ؼ ˻簡 Ϻϴٴ ̾. + // ׷ Ȥó ;, fail ڵ带 ܵд. + if (result <= AUCTION_FAIL) + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BID_CANCEL; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + } + else + { + TPacketDGResultAuction enroll_result; + enroll_result.cmd = AUCTION_BID_CANCEL; + enroll_result.target = item_id; + enroll_result.result = result; + + peer->EncodeHeader(HEADER_DG_AUCTION_RESULT, bidder_id, sizeof(TPacketDGResultAuction)); + peer->Encode(&enroll_result, sizeof(TPacketDGResultAuction)); + } +} +#endif diff --git a/db/src/ClientManager.h b/db/src/ClientManager.h new file mode 100644 index 0000000..be64265 --- /dev/null +++ b/db/src/ClientManager.h @@ -0,0 +1,578 @@ +// vim:ts=8 sw=4 +#ifndef __INC_CLIENTMANAGER_H__ +#define __INC_CLIENTMANAGER_H__ + +#include +#include + +#include "../../common/stl.h" +#include "../../common/building.h" +#include "../../common/auction_table.h" + +#include "Peer.h" +#include "DBManager.h" +#include "LoginData.h" + +class CPlayerTableCache; +class CItemCache; +class CItemPriceListTableCache; + +class CPacketInfo +{ + public: + void Add(int header); + void Reset(); + + std::map m_map_info; +}; + +size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab); + +class CClientManager : public CNetBase, public singleton +{ + public: + typedef std::list TPeerList; + typedef boost::unordered_map TPlayerTableCacheMap; + typedef boost::unordered_map TItemCacheMap; + typedef boost::unordered_set > TItemCacheSet; + typedef boost::unordered_map TItemCacheSetPtrMap; + typedef boost::unordered_map TItemPriceListCacheMap; + typedef boost::unordered_map TChannelStatusMap; + + // MYSHOP_PRICE_LIST + /// Ʈ û + /** + * first: Peer handle + * second: û ÷̾ ID + */ + typedef std::pair< DWORD, DWORD > TItemPricelistReqInfo; + // END_OF_MYSHOP_PRICE_LIST + + class ClientHandleInfo + { + public: + DWORD dwHandle; + DWORD account_id; + DWORD player_id; + BYTE account_index; + char login[LOGIN_MAX_LEN + 1]; + char safebox_password[SAFEBOX_PASSWORD_MAX_LEN + 1]; + char ip[MAX_HOST_LENGTH + 1]; + + TAccountTable * pAccountTable; + TSafeboxTable * pSafebox; + + ClientHandleInfo(DWORD argHandle, DWORD dwPID = 0) + { + dwHandle = argHandle; + pSafebox = NULL; + pAccountTable = NULL; + player_id = dwPID; + }; + //ϼɿ + ClientHandleInfo(DWORD argHandle, DWORD dwPID, DWORD accountId) + { + dwHandle = argHandle; + pSafebox = NULL; + pAccountTable = NULL; + player_id = dwPID; + account_id = accountId; + }; + + ~ClientHandleInfo() + { + if (pSafebox) + { + delete pSafebox; + pSafebox = NULL; + } + } + }; + + public: + CClientManager(); + ~CClientManager(); + + bool Initialize(); + time_t GetCurrentTime(); + + void MainLoop(); + void Quit(); + + void SetTablePostfix(const char* c_pszTablePostfix); + void SetPlayerIDStart(int iIDStart); + int GetPlayerIDStart() { return m_iPlayerIDStart; } + + int GetPlayerDeleteLevelLimit() { return m_iPlayerDeleteLevelLimit; } + + void SetChinaEventServer(bool flag) { m_bChinaEventServer = flag; } + bool IsChinaEventServer() { return m_bChinaEventServer; } + + DWORD GetUserCount(); // ӵ Ѵ. + + void SendAllGuildSkillRechargePacket(); + void SendTime(); + + CPlayerTableCache * GetPlayerCache(DWORD id); + void PutPlayerCache(TPlayerTable * pNew); + + void CreateItemCacheSet(DWORD dwID); + TItemCacheSet * GetItemCacheSet(DWORD dwID); + void FlushItemCacheSet(DWORD dwID); + + CItemCache * GetItemCache(DWORD id); + void PutItemCache(TPlayerItem * pNew, bool bSkipQuery = false); + bool DeleteItemCache(DWORD id); + + void UpdatePlayerCache(); + void UpdateItemCache(); + + // MYSHOP_PRICE_LIST + /// Ʈ ijø ´. + /** + * @param [in] dwID Ʈ .(÷̾ ID) + * @return Ʈ ij + */ + CItemPriceListTableCache* GetItemPriceListCache(DWORD dwID); + + /// Ʈ ijø ִ´. + /** + * @param [in] pItemPriceList ijÿ Ʈ + * + * ijð ̹ Update ƴ replace Ѵ. + */ + void PutItemPriceListCache(const TItemPriceListTable* pItemPriceList); + + + /// Flush ð Ʈ ijø Flush ְ ijÿ Ѵ. + void UpdateItemPriceListCache(void); + // END_OF_MYSHOP_PRICE_LIST + + + void SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable); + + void SetCacheFlushCountLimit(int iLimit); + + template + Func for_each_peer(Func f); + + CPeer * GetAnyPeer(); + + void ForwardPacket(BYTE header, const void* data, int size, BYTE bChannel = 0, CPeer * except = NULL); + + void SendNotice(const char * c_pszFormat, ...); + + char* GetCommand(char* str); //ϼɿ ɾ Լ + void ItemAward(CPeer * peer, char* login); // + + protected: + void Destroy(); + + private: + bool InitializeTables(); + bool InitializeShopTable(); + bool InitializeMobTable(); + bool InitializeItemTable(); + bool InitializeQuestItemTable(); + bool InitializeSkillTable(); + bool InitializeRefineTable(); + bool InitializeBanwordTable(); + bool InitializeItemAttrTable(); + bool InitializeItemRareTable(); + bool InitializeLandTable(); + bool InitializeObjectProto(); + bool InitializeObjectTable(); + bool InitializeMonarch(); + + // mob_proto.txt, item_proto.txt mob_proto, item_proto real db ݿ. + // item_proto, mob_proto db ݿ ʾƵ, ưµ , + //   db item_proto, mob_proto о ߻Ѵ. + bool MirrorMobTableIntoDB(); + bool MirrorItemTableIntoDB(); + + void AddPeer(socket_t fd); + void RemovePeer(CPeer * pPeer); + CPeer * GetPeer(IDENT ident); + + int AnalyzeQueryResult(SQLMsg * msg); + int AnalyzeErrorMsg(CPeer * peer, SQLMsg * msg); + + int Process(); + + void ProcessPackets(CPeer * peer); + + CLoginData * GetLoginData(DWORD dwKey); + CLoginData * GetLoginDataByLogin(const char * c_pszLogin); + CLoginData * GetLoginDataByAID(DWORD dwAID); + + void InsertLoginData(CLoginData * pkLD); + void DeleteLoginData(CLoginData * pkLD); + + bool InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP); + bool DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle); + bool FindLogonAccount(const char * c_pszLogin); + + void GuildCreate(CPeer * peer, DWORD dwGuildID); + void GuildSkillUpdate(CPeer * peer, TPacketGuildSkillUpdate* p); + void GuildExpUpdate(CPeer * peer, TPacketGuildExpUpdate* p); + void GuildAddMember(CPeer * peer, TPacketGDGuildAddMember* p); + void GuildChangeGrade(CPeer * peer, TPacketGuild* p); + void GuildRemoveMember(CPeer * peer, TPacketGuild* p); + void GuildChangeMemberData(CPeer * peer, TPacketGuildChangeMemberData* p); + void GuildDisband(CPeer * peer, TPacketGuild * p); + void GuildWar(CPeer * peer, TPacketGuildWar * p); + void GuildWarScore(CPeer * peer, TPacketGuildWarScore * p); + void GuildChangeLadderPoint(TPacketGuildLadderPoint* p); + void GuildUseSkill(TPacketGuildUseSkill* p); + void GuildDepositMoney(TPacketGDGuildMoney* p); + void GuildWithdrawMoney(CPeer* peer, TPacketGDGuildMoney* p); + void GuildWithdrawMoneyGiveReply(TPacketGDGuildMoneyWithdrawGiveReply* p); + void GuildWarBet(TPacketGDGuildWarBet * p); + void GuildChangeMaster(TPacketChangeGuildMaster* p); + + void SetGuildWarEndTime(DWORD guild_id1, DWORD guild_id2, time_t tEndTime); + + void QUERY_BOOT(CPeer * peer, TPacketGDBoot * p); + + void QUERY_LOGIN(CPeer * peer, DWORD dwHandle, SLoginPacket* data); + void QUERY_LOGOUT(CPeer * peer, DWORD dwHandle, const char *); + + void RESULT_LOGIN(CPeer * peer, SQLMsg *msg); + + void QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket*); + void RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID); + void RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo); + void RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID); + void RESULT_QUEST_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID); + void RESULT_AFFECT_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle); + + // PLAYER_INDEX_CREATE_BUG_FIX + void RESULT_PLAYER_INDEX_CREATE(CPeer *pkPeer, SQLMsg *msg); + // END_PLAYER_INDEX_CREATE_BUG_FIX + + // MYSHOP_PRICE_LIST + /// ε Result ó + /** + * @param peer û Game server peer ü + * @param pMsg Result ü + * + * ε Ʈ ijÿ ϰ peer Ʈ ش. + */ + void RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg); + + /// Ʈ ε Result ó + /** + * @param pMsg Result ü + * + * ε Ʈ ijø Ʈ Ʈ Ѵ. + */ + void RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg); + // END_OF_MYSHOP_PRICE_LIST + + void QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable*); + + void __QUERY_PLAYER_CREATE(CPeer * peer, DWORD dwHandle, TPlayerCreatePacket *); + void __QUERY_PLAYER_DELETE(CPeer * peer, DWORD dwHandle, TPlayerDeletePacket *); + void __RESULT_PLAYER_DELETE(CPeer * peer, SQLMsg* msg); + + void QUERY_PLAYER_COUNT(CPeer * pkPeer, TPlayerCountPacket *); + + void QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData); + void QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData); + void QUERY_ITEM_FLUSH(CPeer * pkPeer, const char * c_pData); + + + void QUERY_QUEST_SAVE(CPeer * pkPeer, TQuestTable *, DWORD dwLen); + void QUERY_ADD_AFFECT(CPeer * pkPeer, TPacketGDAddAffect * p); + void QUERY_REMOVE_AFFECT(CPeer * pkPeer, TPacketGDRemoveAffect * p); + + void QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafeboxLoadPacket *, bool bMall); + void QUERY_SAFEBOX_SAVE(CPeer * pkPeer, TSafeboxTable * pTable); + void QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p); + void QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p); + + void RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg); + void RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg); + + void QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p); + void QUERY_SETUP(CPeer * pkPeer, DWORD dwHandle, const char * c_pData); + + void SendPartyOnSetup(CPeer * peer); + + void QUERY_HIGHSCORE_REGISTER(CPeer * peer, TPacketGDHighscore* data); + void RESULT_HIGHSCORE_REGISTER(CPeer * pkPeer, SQLMsg * msg); + + void QUERY_FLUSH_CACHE(CPeer * pkPeer, const char * c_pData); + + void QUERY_PARTY_CREATE(CPeer * peer, TPacketPartyCreate* p); + void QUERY_PARTY_DELETE(CPeer * peer, TPacketPartyDelete* p); + void QUERY_PARTY_ADD(CPeer * peer, TPacketPartyAdd* p); + void QUERY_PARTY_REMOVE(CPeer * peer, TPacketPartyRemove* p); + void QUERY_PARTY_STATE_CHANGE(CPeer * peer, TPacketPartyStateChange* p); + void QUERY_PARTY_SET_MEMBER_LEVEL(CPeer * peer, TPacketPartySetMemberLevel* p); + + void QUERY_RELOAD_PROTO(); + + void QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p); + void GetPlayerFromRes(TPlayerTable * player_table, MYSQL_RES* res); + + void QUERY_SMS(CPeer * pkPeer, TPacketGDSMS * p); + void QUERY_LOGIN_KEY(CPeer * pkPeer, TPacketGDLoginKey * p); + + void AddGuildPriv(TPacketGiveGuildPriv* p); + void AddEmpirePriv(TPacketGiveEmpirePriv* p); + void AddCharacterPriv(TPacketGiveCharacterPriv* p); + + void MoneyLog(TPacketMoneyLog* p); + + void QUERY_AUTH_LOGIN(CPeer * pkPeer, DWORD dwHandle, TPacketGDAuthLogin * p); + + void QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p); + void RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg); + + void ChargeCash(const TRequestChargeCash * p); + + void LoadEventFlag(); + void SetEventFlag(TPacketSetEventFlag* p); + void SendEventFlagsOnSetup(CPeer* peer); + + void BillingExpire(TPacketBillingExpire * p); + void BillingCheck(const char * data); + + void SendAllLoginToBilling(); + void SendLoginToBilling(CLoginData * pkLD, bool bLogin); + + // ȥ + void MarriageAdd(TPacketMarriageAdd * p); + void MarriageUpdate(TPacketMarriageUpdate * p); + void MarriageRemove(TPacketMarriageRemove * p); + + void WeddingRequest(TPacketWeddingRequest * p); + void WeddingReady(TPacketWeddingReady * p); + void WeddingEnd(TPacketWeddingEnd * p); + + // MYSHOP_PRICE_LIST + // λ + + /// Ʈ Ʈ Ŷ(HEADER_GD_MYSHOP_PRICELIST_UPDATE) óԼ + /** + * @param [in] pPacket Ŷ + */ + void MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* pPacket); + + /// Ʈ û Ŷ(HEADER_GD_MYSHOP_PRICELIST_REQ) óԼ + /** + * @param peer Ŷ Game server peer ü + * @param [in] dwHandle û peer ڵ + * @param [in] dwPlayerID Ʈ û ÷̾ ID + */ + void MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID); + // END_OF_MYSHOP_PRICE_LIST + + // Building + void CreateObject(TPacketGDCreateObject * p); + void DeleteObject(DWORD dwID); + void UpdateLand(DWORD * pdw); + + // VCard + void VCard(TPacketGDVCard * p); + void VCardProcess(); + + // BLOCK_CHAT + void BlockChat(TPacketBlockChat * p); + // END_OF_BLOCK_CHAT + + private: + int m_looping; + socket_t m_fdAccept; // ޴ + TPeerList m_peerList; + + CPeer * m_pkAuthPeer; + + // LoginKey, LoginData pair + typedef boost::unordered_map TLoginDataByLoginKey; + TLoginDataByLoginKey m_map_pkLoginData; + + // Login LoginData pair + typedef boost::unordered_map TLoginDataByLogin; + TLoginDataByLogin m_map_pkLoginDataByLogin; + + // AccountID LoginData pair + typedef boost::unordered_map TLoginDataByAID; + TLoginDataByAID m_map_pkLoginDataByAID; + + // Login LoginData pair ( α Ǿִ ) + typedef boost::unordered_map TLogonAccountMap; + TLogonAccountMap m_map_kLogonAccount; + + int m_iPlayerIDStart; + int m_iPlayerDeleteLevelLimit; + int m_iPlayerDeleteLevelLimitLower; + bool m_bChinaEventServer; + + std::vector m_vec_mobTable; + std::vector m_vec_itemTable; + std::map m_map_itemTableByVnum; + + int m_iShopTableSize; + TShopTable * m_pShopTable; + + int m_iRefineTableSize; + TRefineTable* m_pRefineTable; + + std::vector m_vec_skillTable; + std::vector m_vec_banwordTable; + std::vector m_vec_itemAttrTable; + std::vector m_vec_itemRareTable; + + std::vector m_vec_kLandTable; + std::vector m_vec_kObjectProto; + std::map m_map_pkObjectTable; + + std::queue m_queue_vcard; + + bool m_bShutdowned; + + TPlayerTableCacheMap m_map_playerCache; // ÷̾ id key + + TItemCacheMap m_map_itemCache; // id key + TItemCacheSetPtrMap m_map_pkItemCacheSetPtr; // ÷̾ id key, ÷̾  ij ֳ? + + // MYSHOP_PRICE_LIST + /// ÷̾ Ʈ map. key: ÷̾ ID, value: Ʈ ij + TItemPriceListCacheMap m_mapItemPriceListCache; ///< ÷̾ Ʈ + // END_OF_MYSHOP_PRICE_LIST + + TChannelStatusMap m_mChannelStatus; + + struct TPartyInfo + { + BYTE bRole; + BYTE bLevel; + + TPartyInfo() :bRole(0), bLevel(0) + { + } + }; + + typedef std::map TPartyMember; + typedef std::map TPartyMap; + typedef std::map TPartyChannelMap; + TPartyChannelMap m_map_pkChannelParty; + + typedef std::map TEventFlagMap; + TEventFlagMap m_map_lEventFlag; + + BYTE m_bLastHeader; + int m_iCacheFlushCount; + int m_iCacheFlushCountLimit; + + private : + TItemIDRangeTable m_itemRange; + + public : + bool InitializeNowItemID(); + DWORD GetItemID(); + DWORD GainItemID(); + TItemIDRangeTable GetItemRange() { return m_itemRange; } + + //BOOT_LOCALIZATION + public: + /* ʱȭ + **/ + bool InitializeLocalization(); + + private: + std::vector m_vec_Locale; + //END_BOOT_LOCALIZATION + //ADMIN_MANAGER + + bool __GetAdminInfo(const char *szIP, std::vector & rAdminVec); + bool __GetHostInfo(std::vector & rIPVec); + //END_ADMIN_MANAGER + + + //RELOAD_ADMIN + void ReloadAdmin(CPeer * peer, TPacketReloadAdmin * p); + //END_RELOAD_ADMIN + void BreakMarriage(CPeer * peer, const char * data); + + struct TLogoutPlayer + { + DWORD pid; + time_t time; + + bool operator < (const TLogoutPlayer & r) + { + return (pid < r.pid); + } + }; + + typedef boost::unordered_map TLogoutPlayerMap; + TLogoutPlayerMap m_map_logout; + + void InsertLogoutPlayer(DWORD pid); + void DeleteLogoutPlayer(DWORD pid); + void UpdateLogoutPlayer(); + void UpdateItemCacheSet(DWORD pid); + + void FlushPlayerCacheSet(DWORD pid); + + //MONARCH + void Election(CPeer * peer, DWORD dwHandle, const char * p); + void Candidacy(CPeer * peer, DWORD dwHandle, const char * p); + void AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + void TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + void ComeToVote(CPeer * peer, DWORD dwHandle, const char * p); + void RMCandidacy(CPeer * peer, DWORD dwHandle, const char * p); + void SetMonarch(CPeer * peer, DWORD dwHandle, const char * p); + void RMMonarch(CPeer * peer, DWORD dwHandle, const char * p); + + + void DecMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p); + //END_MONARCH + + void ChangeMonarchLord(CPeer* peer, DWORD dwHandle, TPacketChangeMonarchLord* info); + void BlockException(TPacketBlockException *data); + + void SendSpareItemIDRange(CPeer* peer); + + void UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer); + void AckHorseName(DWORD dwPID, CPeer* peer); + void DeleteLoginKey(TPacketDC *data); + void ResetLastPlayerID(const TPacketNeedLoginLogInfo* data); + //delete gift notify icon + void DeleteAwardId(TPacketDeleteAwardID* data); + void UpdateChannelStatus(TChannelStatus* pData); + void RequestChannelStatus(CPeer* peer, DWORD dwHandle); +#ifdef __AUCTION__ + void EnrollInAuction (CPeer * peer, DWORD owner_id, AuctionEnrollProductInfo* data); + void EnrollInSale (CPeer * peer, DWORD owner_id, AuctionEnrollSaleInfo* data); + void EnrollInWish (CPeer * peer, DWORD wisher_id, AuctionEnrollWishInfo* data); + void AuctionBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data); + void AuctionImpur (CPeer * peer, DWORD purchaser_id, AuctionImpurInfo* data); + void AuctionGetAuctionedItem (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionBuySoldItem (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionCancelAuction (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionCancelWish (CPeer * peer, DWORD actor_id, DWORD item_num); + void AuctionCancelSale (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionDeleteAuctionItem (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionDeleteSaleItem (CPeer * peer, DWORD actor_id, DWORD item_id); + void AuctionReBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data); + void AuctionBidCancel (CPeer * peer, DWORD bidder_id, DWORD item_id); +#endif +}; + +template +Func CClientManager::for_each_peer(Func f) +{ + TPeerList::iterator it; + for (it = m_peerList.begin(); it!=m_peerList.end();++it) + { + f(*it); + } + return f; +} +#endif diff --git a/db/src/ClientManagerBoot.cpp b/db/src/ClientManagerBoot.cpp new file mode 100644 index 0000000..1707df6 --- /dev/null +++ b/db/src/ClientManagerBoot.cpp @@ -0,0 +1,1524 @@ +// vim:ts=4 sw=4 +#include +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Monarch.h" +#include "CsvReader.h" +#include "ProtoReader.h" + +using namespace std; + +extern int g_test_server; +extern std::string g_stLocaleNameColumn; + +bool CClientManager::InitializeTables() +{ + if (!InitializeMobTable()) + { + sys_err("InitializeMobTable FAILED"); + return false; + } + if (!MirrorMobTableIntoDB()) + { + sys_err("MirrorMobTableIntoDB FAILED"); + return false; + } + + if (!InitializeItemTable()) + { + sys_err("InitializeItemTable FAILED"); + return false; + } + + if (!MirrorItemTableIntoDB()) + { + sys_err("MirrorItemTableIntoDB FAILED"); + return false; + } + + if (!InitializeShopTable()) + { + sys_err("InitializeShopTable FAILED"); + return false; + } + + if (!InitializeSkillTable()) + { + sys_err("InitializeSkillTable FAILED"); + return false; + } + + if (!InitializeRefineTable()) + { + sys_err("InitializeRefineTable FAILED"); + return false; + } + + if (!InitializeItemAttrTable()) + { + sys_err("InitializeItemAttrTable FAILED"); + return false; + } + + if (!InitializeItemRareTable()) + { + sys_err("InitializeItemRareTable FAILED"); + return false; + } + + if (!InitializeBanwordTable()) + { + sys_err("InitializeBanwordTable FAILED"); + return false; + } + + if (!InitializeLandTable()) + { + sys_err("InitializeLandTable FAILED"); + return false; + } + + if (!InitializeObjectProto()) + { + sys_err("InitializeObjectProto FAILED"); + return false; + } + + if (!InitializeObjectTable()) + { + sys_err("InitializeObjectTable FAILED"); + return false; + } + + if (!InitializeMonarch()) + { + sys_err("InitializeMonarch FAILED"); + return false; + } + + + return true; +} + +bool CClientManager::InitializeRefineTable() +{ + char query[2048]; + + snprintf(query, sizeof(query), + "SELECT id, cost, prob, vnum0, count0, vnum1, count1, vnum2, count2, vnum3, count3, vnum4, count4 FROM refine_proto%s", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + return true; + + if (m_pRefineTable) + { + sys_log(0, "RELOAD: refine_proto"); + delete [] m_pRefineTable; + m_pRefineTable = NULL; + } + + m_iRefineTableSize = pRes->uiNumRows; + + m_pRefineTable = new TRefineTable[m_iRefineTableSize]; + memset(m_pRefineTable, 0, sizeof(TRefineTable) * m_iRefineTableSize); + + TRefineTable* prt = m_pRefineTable; + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + //const char* s_szQuery = "SELECT src_vnum, result_vnum, cost, prob, " + //"vnum0, count0, vnum1, count1, vnum2, count2, vnum3, count3, vnum4, count4 " + + int col = 0; + //prt->src_vnum = atoi(data[col++]); + //prt->result_vnum = atoi(data[col++]); + str_to_number(prt->id, data[col++]); + str_to_number(prt->cost, data[col++]); + str_to_number(prt->prob, data[col++]); + + for (int i = 0; i < REFINE_MATERIAL_MAX_NUM; i++) + { + str_to_number(prt->materials[i].vnum, data[col++]); + str_to_number(prt->materials[i].count, data[col++]); + if (prt->materials[i].vnum == 0) + { + prt->material_count = i; + break; + } + } + + sys_log(0, "REFINE: id %ld cost %d prob %d mat1 %lu cnt1 %d", prt->id, prt->cost, prt->prob, prt->materials[0].vnum, prt->materials[0].count); + + prt++; + } + return true; +} + +class FCompareVnum +{ + public: + bool operator () (const TEntityTable & a, const TEntityTable & b) const + { + return (a.dwVnum < b.dwVnum); + } +}; + +bool CClientManager::InitializeMobTable() +{ + //================== Լ ==================// + //1. : 'mob_proto.txt', 'mob_proto_test.txt', 'mob_names.txt' а, + // (!)[mob_table] ̺ Ʈ Ѵ. (Ÿ : TMobTable) + //2. + // 1) 'mob_names.txt' о (a)[localMap](vnum:name) . + // 2) 'mob_proto_test.txt'ϰ (a)[localMap] + // (b)[test_map_mobTableByVnum](vnum:TMobTable) Ѵ. + // 3) 'mob_proto.txt' ϰ (a)[localMap] + // (!)[mob_table] ̺ . + // <> + // row , + // (b)[test_map_mobTableByVnum],(!)[mob_table] ο ִ row + // (b)[test_map_mobTableByVnum] Ѵ. + // 4) (b)[test_map_mobTableByVnum] row, (!)[mob_table] ߰Ѵ. + //3. ׽Ʈ + // 1)'mob_proto.txt' mob_table . -> Ϸ + // 2)'mob_names.txt' mob_table . + // 3)'mob_proto_test.txt' [ġ] mob_table . + // 4)'mob_proto_test.txt' [ο] mob_table . + // 5) () Ŭ̾Ʈ ۵ ϴ. + //_______________________________________________// + + + //===============================================// + // 1) 'mob_names.txt' о (a)[localMap] . + //<(a)localMap > + map localMap; + bool isNameFile = true; + //< б> + cCsvTable nameData; + if(!nameData.Load("mob_names.txt",'\t')) + { + fprintf(stderr, "mob_names.txt о ߽ϴ\n"); + isNameFile = false; + } else { + nameData.Next(); //row . + while(nameData.Next()) { + localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); + } + } + //________________________________________________// + + + //===============================================// + // 2) 'mob_proto_test.txt'ϰ (a)localMap + // (b)[test_map_mobTableByVnum](vnum:TMobTable) Ѵ. + //0. + set vnumSet; //׽Ʈ , űԿ Ȯο . + //1. о + bool isTestFile = true; + cCsvTable test_data; + if(!test_data.Load("mob_proto_test.txt",'\t')) + { + fprintf(stderr, "׽Ʈ ϴ. ״ մϴ.\n"); + isTestFile = false; + } + //2. (c)[test_map_mobTableByVnum](vnum:TMobTable) . + map test_map_mobTableByVnum; + if (isTestFile) { + test_data.Next(); // ο Ѿ. + + //. ׽Ʈ ̺ . + TMobTable * test_mob_table = NULL; + int test_MobTableSize = test_data.m_File.GetRowCount()-1; + test_mob_table = new TMobTable[test_MobTableSize]; + memset(test_mob_table, 0, sizeof(TMobTable) * test_MobTableSize); + + //. ׽Ʈ ̺ ְ, ʿ ֱ. + while(test_data.Next()) { + + if (!Set_Proto_Mob_Table(test_mob_table, test_data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + test_map_mobTableByVnum.insert(std::map::value_type(test_mob_table->dwVnum, test_mob_table)); + + + ++test_mob_table; + } + + } + + // 3) 'mob_proto.txt' ϰ (a)[localMap] + // (!)[mob_table] ̺ . + // <> + // row , + // (b)[test_map_mobTableByVnum],(!)[mob_table] ο ִ row + // (b)[test_map_mobTableByVnum] Ѵ. + + //1. б. + cCsvTable data; + if(!data.Load("mob_proto.txt",'\t')) { + fprintf(stderr, "mob_proto.txt о ߽ϴ\n"); + return false; + } + data.Next(); // row Ѿ + //2. (!)[mob_table] ϱ + //2.1 ߰Ǵ ľ + int addNumber = 0; + while(data.Next()) { + int vnum = atoi(data.AsStringByIndex(0)); + std::map::iterator it_map_mobTable; + it_map_mobTable = test_map_mobTableByVnum.find(vnum); + if(it_map_mobTable != test_map_mobTableByVnum.end()) { + addNumber++; + } + } + //data ٽ ùٷ ű.(ٽ о´;;) + data.Destroy(); + if(!data.Load("mob_proto.txt",'\t')) + { + fprintf(stderr, "mob_proto.txt о ߽ϴ\n"); + return false; + } + data.Next(); // ( Į ϴ κ) + //2.2 ũ⿡ ° mob_table + if (!m_vec_mobTable.empty()) + { + sys_log(0, "RELOAD: mob_proto"); + m_vec_mobTable.clear(); + } + m_vec_mobTable.resize(data.m_File.GetRowCount()-1 + addNumber); + memset(&m_vec_mobTable[0], 0, sizeof(TMobTable) * m_vec_mobTable.size()); + TMobTable * mob_table = &m_vec_mobTable[0]; + //2.3 ä + while (data.Next()) + { + int col = 0; + //(b)[test_map_mobTableByVnum] row ִ . + bool isSameRow = true; + std::map::iterator it_map_mobTable; + it_map_mobTable = test_map_mobTableByVnum.find(atoi(data.AsStringByIndex(col))); + if(it_map_mobTable == test_map_mobTableByVnum.end()) { + isSameRow = false; + } + // row (b) о´. + if(isSameRow) { + TMobTable *tempTable = it_map_mobTable->second; + + mob_table->dwVnum = tempTable->dwVnum; + strlcpy(mob_table->szName, tempTable->szName, sizeof(tempTable->szName)); + strlcpy(mob_table->szLocaleName, tempTable->szLocaleName, sizeof(tempTable->szName)); + mob_table->bRank = tempTable->bRank; + mob_table->bType = tempTable->bType; + mob_table->bBattleType = tempTable->bBattleType; + mob_table->bLevel = tempTable->bLevel; + mob_table->bSize = tempTable->bSize; + mob_table->dwAIFlag = tempTable->dwAIFlag; + mob_table->dwRaceFlag = tempTable->dwRaceFlag; + mob_table->dwImmuneFlag = tempTable->dwImmuneFlag; + mob_table->bEmpire = tempTable->bEmpire; + strlcpy(mob_table->szFolder, tempTable->szFolder, sizeof(tempTable->szName)); + mob_table->bOnClickType = tempTable->bOnClickType; + mob_table->bStr = tempTable->bStr; + mob_table->bDex = tempTable->bDex; + mob_table->bCon = tempTable->bCon; + mob_table->bInt = tempTable->bInt; + mob_table->dwDamageRange[0] = tempTable->dwDamageRange[0]; + mob_table->dwDamageRange[1] = tempTable->dwDamageRange[1]; + mob_table->dwMaxHP = tempTable->dwMaxHP; + mob_table->bRegenCycle = tempTable->bRegenCycle; + mob_table->bRegenPercent = tempTable->bRegenPercent; + mob_table->dwGoldMin = tempTable->dwGoldMin; + mob_table->dwGoldMax = tempTable->dwGoldMax; + mob_table->dwExp = tempTable->dwExp; + mob_table->wDef = tempTable->wDef; + mob_table->sAttackSpeed = tempTable->sAttackSpeed; + mob_table->sMovingSpeed = tempTable->sMovingSpeed; + mob_table->bAggresiveHPPct = tempTable->bAggresiveHPPct; + mob_table->wAggressiveSight = tempTable->wAggressiveSight; + mob_table->wAttackRange = tempTable->wAttackRange; + + mob_table->dwDropItemVnum = tempTable->dwDropItemVnum; + mob_table->dwResurrectionVnum = tempTable->dwResurrectionVnum; + for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i) + mob_table->cEnchants[i] = tempTable->cEnchants[i]; + + for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i) + mob_table->cResists[i] = tempTable->cResists[i]; + + mob_table->fDamMultiply = tempTable->fDamMultiply; + mob_table->dwSummonVnum = tempTable->dwSummonVnum; + mob_table->dwDrainSP = tempTable->dwDrainSP; + mob_table->dwPolymorphItemVnum = tempTable->dwPolymorphItemVnum; + + + mob_table->Skills[0].bLevel = tempTable->Skills[0].bLevel; + mob_table->Skills[0].dwVnum = tempTable->Skills[0].dwVnum; + mob_table->Skills[1].bLevel = tempTable->Skills[1].bLevel; + mob_table->Skills[1].dwVnum = tempTable->Skills[1].dwVnum; + mob_table->Skills[2].bLevel = tempTable->Skills[2].bLevel; + mob_table->Skills[2].dwVnum = tempTable->Skills[2].dwVnum; + mob_table->Skills[3].bLevel = tempTable->Skills[3].bLevel; + mob_table->Skills[3].dwVnum = tempTable->Skills[3].dwVnum; + mob_table->Skills[4].bLevel = tempTable->Skills[4].bLevel; + mob_table->Skills[4].dwVnum = tempTable->Skills[4].dwVnum; + + mob_table->bBerserkPoint = tempTable->bBerserkPoint; + mob_table->bStoneSkinPoint = tempTable->bStoneSkinPoint; + mob_table->bGodSpeedPoint = tempTable->bGodSpeedPoint; + mob_table->bDeathBlowPoint = tempTable->bDeathBlowPoint; + mob_table->bRevivePoint = tempTable->bRevivePoint; + } else { + + if (!Set_Proto_Mob_Table(mob_table, data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + + } + + //¿ vnum ߰ + vnumSet.insert(mob_table->dwVnum); + + + sys_log(1, "MOB #%-5d %-24s %-24s level: %-3u rank: %u empire: %d", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire); + ++mob_table; + + } + //_____________________________________________________// + + + // 4) (b)[test_map_mobTableByVnum] row, (!)[mob_table] ߰Ѵ. + // ٽ о. + test_data.Destroy(); + isTestFile = true; + test_data; + if(!test_data.Load("mob_proto_test.txt",'\t')) + { + fprintf(stderr, "׽Ʈ ϴ. ״ մϴ.\n"); + isTestFile = false; + } + if(isTestFile) { + test_data.Next(); // ο Ѿ. + + while (test_data.Next()) //׽Ʈ Ⱦ,ο ߰Ѵ. + { + //ߺǴ κ̸ Ѿ. + set::iterator itVnum; + itVnum=vnumSet.find(atoi(test_data.AsStringByIndex(0))); + if (itVnum != vnumSet.end()) { + continue; + } + + if (!Set_Proto_Mob_Table(mob_table, test_data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + sys_log(0, "MOB #%-5d %-24s %-24s level: %-3u rank: %u empire: %d", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire); + ++mob_table; + + } + } + sort(m_vec_mobTable.begin(), m_vec_mobTable.end(), FCompareVnum()); + return true; +} + +bool CClientManager::InitializeShopTable() +{ + MYSQL_ROW data; + int col; + + static const char * s_szQuery = + "SELECT " + "shop.vnum, " + "shop.npc_vnum, " + "shop_item.item_vnum, " + "shop_item.count " + "FROM shop LEFT JOIN shop_item " + "ON shop.vnum = shop_item.shop_vnum ORDER BY shop.vnum, shop_item.item_vnum"; + + std::auto_ptr pkMsg2(CDBManager::instance().DirectQuery(s_szQuery)); + + // shop vnum ִµ shop_item ... з óǴ . + // óҺκ + SQLResult * pRes2 = pkMsg2->Get(); + + if (!pRes2->uiNumRows) + { + sys_err("InitializeShopTable : Table count is zero."); + return false; + } + + std::map map_shop; + + if (m_pShopTable) + { + delete [] (m_pShopTable); + m_pShopTable = NULL; + } + + TShopTable * shop_table = m_pShopTable; + + while ((data = mysql_fetch_row(pRes2->pSQLResult))) + { + col = 0; + + int iShopVnum = 0; + str_to_number(iShopVnum, data[col++]); + + if (map_shop.end() == map_shop.find(iShopVnum)) + { + shop_table = new TShopTable; + memset(shop_table, 0, sizeof(TShopTable)); + shop_table->dwVnum = iShopVnum; + + map_shop[iShopVnum] = shop_table; + } + else + shop_table = map_shop[iShopVnum]; + + str_to_number(shop_table->dwNPCVnum, data[col++]); + + if (!data[col]) // ϳ NULL ǹǷ.. + continue; + + TShopItemTable * pItem = &shop_table->items[shop_table->byItemCount]; + + str_to_number(pItem->vnum, data[col++]); + str_to_number(pItem->count, data[col++]); + + ++shop_table->byItemCount; + } + + m_pShopTable = new TShopTable[map_shop.size()]; + m_iShopTableSize = map_shop.size(); + + typeof(map_shop.begin()) it = map_shop.begin(); + + int i = 0; + + while (it != map_shop.end()) + { + thecore_memcpy((m_pShopTable + i), (it++)->second, sizeof(TShopTable)); + sys_log(0, "SHOP: #%d items: %d", (m_pShopTable + i)->dwVnum, (m_pShopTable + i)->byItemCount); + ++i; + } + + return true; +} + +bool CClientManager::InitializeQuestItemTable() +{ + using namespace std; + + static const char * s_szQuery = "SELECT vnum, name, %s FROM quest_item_proto ORDER BY vnum"; + + char query[1024]; + snprintf(query, sizeof(query), s_szQuery, g_stLocaleNameColumn.c_str()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("query error or no rows: %s", query); + return false; + } + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + int col = 0; + + TItemTable tbl; + memset(&tbl, 0, sizeof(tbl)); + + str_to_number(tbl.dwVnum, row[col++]); + + if (row[col]) + strlcpy(tbl.szName, row[col], sizeof(tbl.szName)); + + col++; + + if (row[col]) + strlcpy(tbl.szLocaleName, row[col], sizeof(tbl.szLocaleName)); + + col++; + + if (m_map_itemTableByVnum.find(tbl.dwVnum) != m_map_itemTableByVnum.end()) + { + sys_err("QUEST_ITEM_ERROR! %lu vnum already exist! (name %s)", tbl.dwVnum, tbl.szLocaleName); + continue; + } + + tbl.bType = ITEM_QUEST; // quest_item_proto ̺ ִ ͵ ITEM_QUEST + tbl.bSize = 1; + + m_vec_itemTable.push_back(tbl); + } + + return true; +} + +bool CClientManager::InitializeItemTable() +{ + //================== Լ ==================// + //1. : 'item_proto.txt', 'item_proto_test.txt', 'item_names.txt' а, + // (TItemTable), Ʈ Ѵ. + //2. + // 1) 'item_names.txt' о (a)[localMap](vnum:name) . + // 2) 'item_proto_text.txt'ϰ (a)[localMap] + // (b)[test_map_itemTableByVnum](vnum:TItemTable) Ѵ. + // 3) 'item_proto.txt' ϰ (a)[localMap] + // (!)[item_table], . + // <> + // row , + // (b)[test_map_itemTableByVnum],(!)[mob_table] ο ִ row + // (b)[test_map_itemTableByVnum] Ѵ. + // 4) (b)[test_map_itemTableByVnum] row, (!)[item_table] ߰Ѵ. + //3. ׽Ʈ + // 1)'item_proto.txt' item_table . -> Ϸ + // 2)'item_names.txt' item_table . + // 3)'item_proto_test.txt' [ġ] item_table . + // 4)'item_proto_test.txt' [ο] item_table . + // 5) () Ŭ̾Ʈ ۵ ϴ. + //_______________________________________________// + + + + //=================================================================================// + // 1) 'item_names.txt' о (a)[localMap](vnum:name) . + //=================================================================================// + bool isNameFile = true; + map localMap; + cCsvTable nameData; + if(!nameData.Load("item_names.txt",'\t')) + { + fprintf(stderr, "item_names.txt о ߽ϴ\n"); + isNameFile = false; + } else { + nameData.Next(); + while(nameData.Next()) { + localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); + } + } + //_________________________________________________________________// + + //=================================================================// + // 2) 'item_proto_text.txt'ϰ (a)[localMap] + // (b)[test_map_itemTableByVnum](vnum:TItemTable) Ѵ. + //=================================================================// + map test_map_itemTableByVnum; + //1. о. + cCsvTable test_data; + if(!test_data.Load("item_proto_test.txt",'\t')) + { + fprintf(stderr, "item_proto_test.txt о ߽ϴ\n"); + //return false; + } else { + test_data.Next(); // ο Ѿ. + + //2. ׽Ʈ ̺ . + TItemTable * test_item_table = NULL; + int test_itemTableSize = test_data.m_File.GetRowCount()-1; + test_item_table = new TItemTable[test_itemTableSize]; + memset(test_item_table, 0, sizeof(TItemTable) * test_itemTableSize); + + //3. ׽Ʈ ̺ ְ, ʿ ֱ. + while(test_data.Next()) { + + + if (!Set_Proto_Item_Table(test_item_table, test_data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + test_map_itemTableByVnum.insert(std::map::value_type(test_item_table->dwVnum, test_item_table)); + test_item_table++; + + } + } + //______________________________________________________________________// + + + //========================================================================// + // 3) 'item_proto.txt' ϰ (a)[localMap] + // (!)[item_table], . + // <> + // row , + // (b)[test_map_itemTableByVnum],(!)[mob_table] ο ִ row + // (b)[test_map_itemTableByVnum] Ѵ. + //========================================================================// + + //vnum . ο ׽Ʈ ǺҶ ȴ. + set vnumSet; + + // о. + cCsvTable data; + if(!data.Load("item_proto.txt",'\t')) + { + fprintf(stderr, "item_proto.txt о ߽ϴ\n"); + return false; + } + data.Next(); // ( Į ϴ κ) + + if (!m_vec_itemTable.empty()) + { + sys_log(0, "RELOAD: item_proto"); + m_vec_itemTable.clear(); + m_map_itemTableByVnum.clear(); + } + + //===== ̺ =====// + // ߰Ǵ ľѴ. + int addNumber = 0; + while(data.Next()) { + int vnum = atoi(data.AsStringByIndex(0)); + std::map::iterator it_map_itemTable; + it_map_itemTable = test_map_itemTableByVnum.find(vnum); + if(it_map_itemTable != test_map_itemTableByVnum.end()) { + addNumber++; + } + } + //data ٽ ùٷ ű.(ٽ о´;;) + data.Destroy(); + if(!data.Load("item_proto.txt",'\t')) + { + fprintf(stderr, "item_proto.txt о ߽ϴ\n"); + return false; + } + data.Next(); // ( Į ϴ κ) + + m_vec_itemTable.resize(data.m_File.GetRowCount() - 1 + addNumber); + memset(&m_vec_itemTable[0], 0, sizeof(TItemTable) * m_vec_itemTable.size()); + int testValue = m_vec_itemTable.size(); + + TItemTable * item_table = &m_vec_itemTable[0]; + + while (data.Next()) + { + int col = 0; + + std::map::iterator it_map_itemTable; + it_map_itemTable = test_map_itemTableByVnum.find(atoi(data.AsStringByIndex(col))); + if(it_map_itemTable == test_map_itemTableByVnum.end()) { + // Į + + if (!Set_Proto_Item_Table(item_table, data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + + + } else { //$$$$$$$$$$$$$$$$$$$$$$$ ׽Ʈ ִ! + TItemTable *tempTable = it_map_itemTable->second; + + item_table->dwVnum = tempTable->dwVnum; + strlcpy(item_table->szName, tempTable->szName, sizeof(item_table->szName)); + strlcpy(item_table->szLocaleName, tempTable->szLocaleName, sizeof(item_table->szLocaleName)); + item_table->bType = tempTable->bType; + item_table->bSubType = tempTable->bSubType; + item_table->bSize = tempTable->bSize; + item_table->dwAntiFlags = tempTable->dwAntiFlags; + item_table->dwFlags = tempTable->dwFlags; + item_table->dwWearFlags = tempTable->dwWearFlags; + item_table->dwImmuneFlag = tempTable->dwImmuneFlag; + item_table->dwGold = tempTable->dwGold; + item_table->dwShopBuyPrice = tempTable->dwShopBuyPrice; + item_table->dwRefinedVnum =tempTable->dwRefinedVnum; + item_table->wRefineSet =tempTable->wRefineSet; + item_table->bAlterToMagicItemPct = tempTable->bAlterToMagicItemPct; + item_table->cLimitRealTimeFirstUseIndex = -1; + item_table->cLimitTimerBasedOnWearIndex = -1; + + int i; + + for (i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + item_table->aLimits[i].bType = tempTable->aLimits[i].bType; + item_table->aLimits[i].lValue = tempTable->aLimits[i].lValue; + + if (LIMIT_REAL_TIME_START_FIRST_USE == item_table->aLimits[i].bType) + item_table->cLimitRealTimeFirstUseIndex = (char)i; + + if (LIMIT_TIMER_BASED_ON_WEAR == item_table->aLimits[i].bType) + item_table->cLimitTimerBasedOnWearIndex = (char)i; + } + + for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i) + { + item_table->aApplies[i].bType = tempTable->aApplies[i].bType; + item_table->aApplies[i].lValue = tempTable->aApplies[i].lValue; + } + + for (i = 0; i < ITEM_VALUES_MAX_NUM; ++i) + item_table->alValues[i] = tempTable->alValues[i]; + + item_table->bGainSocketPct = tempTable->bGainSocketPct; + item_table->sAddonType = tempTable->sAddonType; + + item_table->bWeight = tempTable->bWeight; + + } + vnumSet.insert(item_table->dwVnum); + m_map_itemTableByVnum.insert(std::map::value_type(item_table->dwVnum, item_table)); + ++item_table; + } + //_______________________________________________________________________// + + //========================================================================// + // 4) (b)[test_map_itemTableByVnum] row, (!)[item_table] ߰Ѵ. + //========================================================================// + test_data.Destroy(); + if(!test_data.Load("item_proto_test.txt",'\t')) + { + fprintf(stderr, "item_proto_test.txt о ߽ϴ\n"); + //return false; + } else { + test_data.Next(); // ο Ѿ. + + while (test_data.Next()) //׽Ʈ Ⱦ,ο ߰Ѵ. + { + //ߺǴ κ̸ Ѿ. + set::iterator itVnum; + itVnum=vnumSet.find(atoi(test_data.AsStringByIndex(0))); + if (itVnum != vnumSet.end()) { + continue; + } + + if (!Set_Proto_Item_Table(item_table, test_data, localMap)) + { + fprintf(stderr, " ̺ .\n"); + } + + + m_map_itemTableByVnum.insert(std::map::value_type(item_table->dwVnum, item_table)); + + item_table++; + + } + } + + + + // QUEST_ITEM_PROTO_DISABLE + // InitializeQuestItemTable(); + // END_OF_QUEST_ITEM_PROTO_DISABLE + + m_map_itemTableByVnum.clear(); + + itertype(m_vec_itemTable) it = m_vec_itemTable.begin(); + + while (it != m_vec_itemTable.end()) + { + TItemTable * item_table = &(*(it++)); + + sys_log(1, "ITEM: #%-5lu %-24s %-24s VAL: %ld %ld %ld %ld %ld %ld WEAR %lu ANTI %lu IMMUNE %lu REFINE %lu REFINE_SET %u MAGIC_PCT %u", + item_table->dwVnum, + item_table->szName, + item_table->szLocaleName, + item_table->alValues[0], + item_table->alValues[1], + item_table->alValues[2], + item_table->alValues[3], + item_table->alValues[4], + item_table->alValues[5], + item_table->dwWearFlags, + item_table->dwAntiFlags, + item_table->dwImmuneFlag, + item_table->dwRefinedVnum, + item_table->wRefineSet, + item_table->bAlterToMagicItemPct); + + m_map_itemTableByVnum.insert(std::map::value_type(item_table->dwVnum, item_table)); + } + sort(m_vec_itemTable.begin(), m_vec_itemTable.end(), FCompareVnum()); + return true; +} + + +bool CClientManager::InitializeSkillTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT dwVnum, szName, bType, bMaxLevel, dwSplashRange, " + "szPointOn, szPointPoly, szSPCostPoly, szDurationPoly, szDurationSPCostPoly, " + "szCooldownPoly, szMasterBonusPoly, setFlag+0, setAffectFlag+0, " + "szPointOn2, szPointPoly2, szDurationPoly2, setAffectFlag2+0, " + "szPointOn3, szPointPoly3, szDurationPoly3, szGrandMasterAddSPCostPoly, " + "bLevelStep, bLevelLimit, prerequisiteSkillVnum, prerequisiteSkillLevel, iMaxHit, szSplashAroundDamageAdjustPoly, eSkillType+0, dwTargetRange " + "FROM skill_proto%s ORDER BY dwVnum", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from skill_proto"); + return false; + } + + if (!m_vec_skillTable.empty()) + { + sys_log(0, "RELOAD: skill_proto"); + m_vec_skillTable.clear(); + } + + m_vec_skillTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + int col; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TSkillTable t; + memset(&t, 0, sizeof(t)); + + col = 0; + + str_to_number(t.dwVnum, data[col++]); + strlcpy(t.szName, data[col++], sizeof(t.szName)); + str_to_number(t.bType, data[col++]); + str_to_number(t.bMaxLevel, data[col++]); + str_to_number(t.dwSplashRange, data[col++]); + + strlcpy(t.szPointOn, data[col++], sizeof(t.szPointOn)); + strlcpy(t.szPointPoly, data[col++], sizeof(t.szPointPoly)); + strlcpy(t.szSPCostPoly, data[col++], sizeof(t.szSPCostPoly)); + strlcpy(t.szDurationPoly, data[col++], sizeof(t.szDurationPoly)); + strlcpy(t.szDurationSPCostPoly, data[col++], sizeof(t.szDurationSPCostPoly)); + strlcpy(t.szCooldownPoly, data[col++], sizeof(t.szCooldownPoly)); + strlcpy(t.szMasterBonusPoly, data[col++], sizeof(t.szMasterBonusPoly)); + + str_to_number(t.dwFlag, data[col++]); + str_to_number(t.dwAffectFlag, data[col++]); + + strlcpy(t.szPointOn2, data[col++], sizeof(t.szPointOn2)); + strlcpy(t.szPointPoly2, data[col++], sizeof(t.szPointPoly2)); + strlcpy(t.szDurationPoly2, data[col++], sizeof(t.szDurationPoly2)); + str_to_number(t.dwAffectFlag2, data[col++]); + + // ADD_GRANDMASTER_SKILL + strlcpy(t.szPointOn3, data[col++], sizeof(t.szPointOn3)); + strlcpy(t.szPointPoly3, data[col++], sizeof(t.szPointPoly3)); + strlcpy(t.szDurationPoly3, data[col++], sizeof(t.szDurationPoly3)); + + strlcpy(t.szGrandMasterAddSPCostPoly, data[col++], sizeof(t.szGrandMasterAddSPCostPoly)); + // END_OF_ADD_GRANDMASTER_SKILL + + str_to_number(t.bLevelStep, data[col++]); + str_to_number(t.bLevelLimit, data[col++]); + str_to_number(t.preSkillVnum, data[col++]); + str_to_number(t.preSkillLevel, data[col++]); + + str_to_number(t.lMaxHit, data[col++]); + + strlcpy(t.szSplashAroundDamageAdjustPoly, data[col++], sizeof(t.szSplashAroundDamageAdjustPoly)); + + str_to_number(t.bSkillAttrType, data[col++]); + str_to_number(t.dwTargetRange, data[col++]); + + sys_log(0, "SKILL: #%d %s flag %u point %s affect %u cooldown %s", t.dwVnum, t.szName, t.dwFlag, t.szPointOn, t.dwAffectFlag, t.szCooldownPoly); + + m_vec_skillTable.push_back(t); + } + + return true; +} + +bool CClientManager::InitializeBanwordTable() +{ + m_vec_banwordTable.clear(); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery("SELECT word FROM banword")); + + SQLResult * pRes = pkMsg->Get(); + + if (pRes->uiNumRows == 0) + return true; + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TBanwordTable t; + + if (data[0]) + { + strlcpy(t.szWord, data[0], sizeof(t.szWord)); + m_vec_banwordTable.push_back(t); + } + } + + sys_log(0, "BANWORD: total %d", m_vec_banwordTable.size()); + return true; +} + +bool CClientManager::InitializeItemAttrTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT apply, apply+0, prob, lv1, lv2, lv3, lv4, lv5, weapon, body, wrist, foots, neck, head, shield, ear FROM item_attr%s ORDER BY apply", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from item_attr"); + return false; + } + + if (!m_vec_itemAttrTable.empty()) + { + sys_log(0, "RELOAD: item_attr"); + m_vec_itemAttrTable.clear(); + } + + m_vec_itemAttrTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TItemAttrTable t; + + memset(&t, 0, sizeof(TItemAttrTable)); + + int col = 0; + + strlcpy(t.szApply, data[col++], sizeof(t.szApply)); + str_to_number(t.dwApplyIndex, data[col++]); + str_to_number(t.dwProb, data[col++]); + str_to_number(t.lValues[0], data[col++]); + str_to_number(t.lValues[1], data[col++]); + str_to_number(t.lValues[2], data[col++]); + str_to_number(t.lValues[3], data[col++]); + str_to_number(t.lValues[4], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_EAR], data[col++]); + + sys_log(0, "ITEM_ATTR: %-20s %4lu { %3d %3d %3d %3d %3d } { %d %d %d %d %d %d %d }", + t.szApply, + t.dwProb, + t.lValues[0], + t.lValues[1], + t.lValues[2], + t.lValues[3], + t.lValues[4], + t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], + t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], + t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], + t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], + t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], + t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], + t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], + t.bMaxLevelBySet[ATTRIBUTE_SET_EAR]); + + m_vec_itemAttrTable.push_back(t); + } + + return true; +} + +bool CClientManager::InitializeItemRareTable() +{ + char query[4096]; + snprintf(query, sizeof(query), + "SELECT apply, apply+0, prob, lv1, lv2, lv3, lv4, lv5, weapon, body, wrist, foots, neck, head, shield, ear FROM item_attr_rare%s ORDER BY apply", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!pRes->uiNumRows) + { + sys_err("no result from item_attr_rare"); + return false; + } + + if (!m_vec_itemRareTable.empty()) + { + sys_log(0, "RELOAD: item_attr_rare"); + m_vec_itemRareTable.clear(); + } + + m_vec_itemRareTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TItemAttrTable t; + + memset(&t, 0, sizeof(TItemAttrTable)); + + int col = 0; + + strlcpy(t.szApply, data[col++], sizeof(t.szApply)); + str_to_number(t.dwApplyIndex, data[col++]); + str_to_number(t.dwProb, data[col++]); + str_to_number(t.lValues[0], data[col++]); + str_to_number(t.lValues[1], data[col++]); + str_to_number(t.lValues[2], data[col++]); + str_to_number(t.lValues[3], data[col++]); + str_to_number(t.lValues[4], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], data[col++]); + str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_EAR], data[col++]); + + sys_log(0, "ITEM_RARE: %-20s %4lu { %3d %3d %3d %3d %3d } { %d %d %d %d %d %d %d }", + t.szApply, + t.dwProb, + t.lValues[0], + t.lValues[1], + t.lValues[2], + t.lValues[3], + t.lValues[4], + t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], + t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], + t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], + t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], + t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], + t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], + t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], + t.bMaxLevelBySet[ATTRIBUTE_SET_EAR]); + + m_vec_itemRareTable.push_back(t); + } + + return true; +} + +bool CClientManager::InitializeLandTable() +{ + using namespace building; + + char query[4096]; + + snprintf(query, sizeof(query), + "SELECT id, map_index, x, y, width, height, guild_id, guild_level_limit, price " + "FROM land%s WHERE enable='YES' ORDER BY id", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_vec_kLandTable.empty()) + { + sys_log(0, "RELOAD: land"); + m_vec_kLandTable.clear(); + } + + m_vec_kLandTable.reserve(pRes->uiNumRows); + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TLand t; + + memset(&t, 0, sizeof(t)); + + int col = 0; + + str_to_number(t.dwID, data[col++]); + str_to_number(t.lMapIndex, data[col++]); + str_to_number(t.x, data[col++]); + str_to_number(t.y, data[col++]); + str_to_number(t.width, data[col++]); + str_to_number(t.height, data[col++]); + str_to_number(t.dwGuildID, data[col++]); + str_to_number(t.bGuildLevelLimit, data[col++]); + str_to_number(t.dwPrice, data[col++]); + + sys_log(0, "LAND: %lu map %-4ld %7ldx%-7ld w %-4ld h %-4ld", t.dwID, t.lMapIndex, t.x, t.y, t.width, t.height); + + m_vec_kLandTable.push_back(t); + } + + return true; +} + +void parse_pair_number_string(const char * c_pszString, std::vector > & vec) +{ + // format: 10,1/20,3/300,50 + const char * t = c_pszString; + const char * p = strchr(t, '/'); + std::pair k; + + char szNum[32 + 1]; + char * comma; + + while (p) + { + if (isnhdigit(*t)) + { + strlcpy(szNum, t, MIN(sizeof(szNum), (p-t)+1)); + + comma = strchr(szNum, ','); + + if (comma) + { + *comma = '\0'; + str_to_number(k.second, comma+1); + } + else + k.second = 0; + + str_to_number(k.first, szNum); + vec.push_back(k); + } + + t = p + 1; + p = strchr(t, '/'); + } + + if (isnhdigit(*t)) + { + strlcpy(szNum, t, sizeof(szNum)); + + comma = strchr(const_cast(t), ','); + + if (comma) + { + *comma = '\0'; + str_to_number(k.second, comma+1); + } + else + k.second = 0; + + str_to_number(k.first, szNum); + vec.push_back(k); + } +} + +bool CClientManager::InitializeObjectProto() +{ + using namespace building; + + char query[4096]; + snprintf(query, sizeof(query), + "SELECT vnum, price, materials, upgrade_vnum, upgrade_limit_time, life, reg_1, reg_2, reg_3, reg_4, npc, group_vnum, dependent_group " + "FROM object_proto%s ORDER BY vnum", + GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_vec_kObjectProto.empty()) + { + sys_log(0, "RELOAD: object_proto"); + m_vec_kObjectProto.clear(); + } + + m_vec_kObjectProto.reserve(MAX(0, pRes->uiNumRows)); + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TObjectProto t; + + memset(&t, 0, sizeof(t)); + + int col = 0; + + str_to_number(t.dwVnum, data[col++]); + str_to_number(t.dwPrice, data[col++]); + + std::vector > vec; + parse_pair_number_string(data[col++], vec); + + for (unsigned int i = 0; i < OBJECT_MATERIAL_MAX_NUM && i < vec.size(); ++i) + { + std::pair & r = vec[i]; + + t.kMaterials[i].dwItemVnum = r.first; + t.kMaterials[i].dwCount = r.second; + } + + str_to_number(t.dwUpgradeVnum, data[col++]); + str_to_number(t.dwUpgradeLimitTime, data[col++]); + str_to_number(t.lLife, data[col++]); + str_to_number(t.lRegion[0], data[col++]); + str_to_number(t.lRegion[1], data[col++]); + str_to_number(t.lRegion[2], data[col++]); + str_to_number(t.lRegion[3], data[col++]); + + // ADD_BUILDING_NPC + str_to_number(t.dwNPCVnum, data[col++]); + str_to_number(t.dwGroupVnum, data[col++]); + str_to_number(t.dwDependOnGroupVnum, data[col++]); + + t.lNPCX = 0; + t.lNPCY = MAX(t.lRegion[1], t.lRegion[3])+300; + // END_OF_ADD_BUILDING_NPC + + sys_log(0, "OBJ_PROTO: vnum %lu price %lu mat %lu %lu", + t.dwVnum, t.dwPrice, t.kMaterials[0].dwItemVnum, t.kMaterials[0].dwCount); + + m_vec_kObjectProto.push_back(t); + } + + return true; +} + +bool CClientManager::InitializeObjectTable() +{ + using namespace building; + + char query[4096]; + snprintf(query, sizeof(query), "SELECT id, land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot, life FROM object%s ORDER BY id", GetTablePostfix()); + + std::auto_ptr pkMsg(CDBManager::instance().DirectQuery(query)); + SQLResult * pRes = pkMsg->Get(); + + if (!m_map_pkObjectTable.empty()) + { + sys_log(0, "RELOAD: object"); + m_map_pkObjectTable.clear(); + } + + MYSQL_ROW data; + + if (pRes->uiNumRows > 0) + while ((data = mysql_fetch_row(pRes->pSQLResult))) + { + TObject * k = new TObject; + + memset(k, 0, sizeof(TObject)); + + int col = 0; + + str_to_number(k->dwID, data[col++]); + str_to_number(k->dwLandID, data[col++]); + str_to_number(k->dwVnum, data[col++]); + str_to_number(k->lMapIndex, data[col++]); + str_to_number(k->x, data[col++]); + str_to_number(k->y, data[col++]); + str_to_number(k->xRot, data[col++]); + str_to_number(k->yRot, data[col++]); + str_to_number(k->zRot, data[col++]); + str_to_number(k->lLife, data[col++]); + + sys_log(0, "OBJ: %lu vnum %lu map %-4ld %7ldx%-7ld life %ld", + k->dwID, k->dwVnum, k->lMapIndex, k->x, k->y, k->lLife); + + m_map_pkObjectTable.insert(std::make_pair(k->dwID, k)); + } + + return true; +} + +bool CClientManager::InitializeMonarch() +{ + CMonarch::instance().LoadMonarch(); + + return true; +} + +bool CClientManager::MirrorMobTableIntoDB() +{ + for (itertype(m_vec_mobTable) it = m_vec_mobTable.begin(); it != m_vec_mobTable.end(); it++) + { + const TMobTable& t = *it; + char query[4096]; + if (g_stLocaleNameColumn == "name") + { + snprintf(query, sizeof(query), + "replace into mob_proto%s " + "(" + "vnum, name, type, rank, battle_type, level, size, ai_flag, setRaceFlag, setImmuneFlag, " + "on_click, empire, drop_item, resurrection_vnum, folder, " + "st, dx, ht, iq, damage_min, damage_max, max_hp, regen_cycle, regen_percent, exp, " + "gold_min, gold_max, def, attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item, " + + "enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate, " + "resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow, " + "resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, " + "dam_multiply, summon, drain_sp, " + + "skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, " + "skill_vnum3, skill_level3, skill_vnum4, skill_level4, " + "sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive" + ") " + "values (" + + "%d, \"%s\", %d, %d, %d, %d, %d, %u, %u, %u, " + "%d, %d, %d, %d, '%s', " + "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, %d, %d, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%f, %d, %d, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d" + ")", + GetTablePostfix(), /*g_stLocaleNameColumn.c_str(),*/ + + t.dwVnum, t.szName, /*t.szLocaleName, */t.bType, t.bRank, t.bBattleType, t.bLevel, t.bSize, t.dwAIFlag, t.dwRaceFlag, t.dwImmuneFlag, + t.bOnClickType, t.bEmpire, t.dwDropItemVnum, t.dwResurrectionVnum, t.szFolder, + t.bStr, t.bDex, t.bCon, t.bInt, t.dwDamageRange[0], t.dwDamageRange[1], t.dwMaxHP, t.bRegenCycle, t.bRegenPercent, t.dwExp, + + t.dwGoldMin, t.dwGoldMax, t.wDef, t.sAttackSpeed, t.sMovingSpeed, t.bAggresiveHPPct, t.wAggressiveSight, t.wAttackRange, t.dwPolymorphItemVnum, + t.cEnchants[0], t.cEnchants[1], t.cEnchants[2], t.cEnchants[3], t.cEnchants[4], t.cEnchants[5], + t.cResists[0], t.cResists[1], t.cResists[2], t.cResists[3], t.cResists[4], t.cResists[5], + t.cResists[6], t.cResists[7], t.cResists[8], t.cResists[9], t.cResists[10], + t.fDamMultiply, t.dwSummonVnum, t.dwDrainSP, + + t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, + t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, + t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint + ); + } + else + { + snprintf(query, sizeof(query), + "replace into mob_proto%s " + "(" + "vnum, name, %s, type, rank, battle_type, level, size, ai_flag, setRaceFlag, setImmuneFlag, " + "on_click, empire, drop_item, resurrection_vnum, folder, " + "st, dx, ht, iq, damage_min, damage_max, max_hp, regen_cycle, regen_percent, exp, " + "gold_min, gold_max, def, attack_speed, move_speed, aggressive_hp_pct, aggressive_sight, attack_range, polymorph_item, " + + "enchant_curse, enchant_slow, enchant_poison, enchant_stun, enchant_critical, enchant_penetrate, " + "resist_sword, resist_twohand, resist_dagger, resist_bell, resist_fan, resist_bow, " + "resist_fire, resist_elect, resist_magic, resist_wind, resist_poison, " + "dam_multiply, summon, drain_sp, " + + "skill_vnum0, skill_level0, skill_vnum1, skill_level1, skill_vnum2, skill_level2, " + "skill_vnum3, skill_level3, skill_vnum4, skill_level4, " + "sp_berserk, sp_stoneskin, sp_godspeed, sp_deathblow, sp_revive" + ") " + "values (" + + "%d, \"%s\", \"%s\", %d, %d, %d, %d, %d, %u, %u, %u, " + "%d, %d, %d, %d, '%s', " + "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, %d, %d, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%f, %d, %d, " + + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d" + ")", + GetTablePostfix(), g_stLocaleNameColumn.c_str(), + + t.dwVnum, t.szName, t.szLocaleName, t.bType, t.bRank, t.bBattleType, t.bLevel, t.bSize, t.dwAIFlag, t.dwRaceFlag, t.dwImmuneFlag, + t.bOnClickType, t.bEmpire, t.dwDropItemVnum, t.dwResurrectionVnum, t.szFolder, + t.bStr, t.bDex, t.bCon, t.bInt, t.dwDamageRange[0], t.dwDamageRange[1], t.dwMaxHP, t.bRegenCycle, t.bRegenPercent, t.dwExp, + + t.dwGoldMin, t.dwGoldMax, t.wDef, t.sAttackSpeed, t.sMovingSpeed, t.bAggresiveHPPct, t.wAggressiveSight, t.wAttackRange, t.dwPolymorphItemVnum, + t.cEnchants[0], t.cEnchants[1], t.cEnchants[2], t.cEnchants[3], t.cEnchants[4], t.cEnchants[5], + t.cResists[0], t.cResists[1], t.cResists[2], t.cResists[3], t.cResists[4], t.cResists[5], + t.cResists[6], t.cResists[7], t.cResists[8], t.cResists[9], t.cResists[10], + t.fDamMultiply, t.dwSummonVnum, t.dwDrainSP, + + t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, + t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, + t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint + ); + } + + CDBManager::instance().AsyncQuery(query); + } + return true; +} + +bool CClientManager::MirrorItemTableIntoDB() +{ + for (itertype(m_vec_itemTable) it = m_vec_itemTable.begin(); it != m_vec_itemTable.end(); it++) + { + if (g_stLocaleNameColumn != "name") + { + const TItemTable& t = *it; + char query[4096]; + snprintf(query, sizeof(query), + "replace into item_proto%s (" + "vnum, type, subtype, name, %s, gold, shop_buy_price, weight, size, " + "flag, wearflag, antiflag, immuneflag, " + "refined_vnum, refine_set, magic_pct, socket_pct, addon_type, " + "limittype0, limitvalue0, limittype1, limitvalue1, " + "applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, " + "value0, value1, value2, value3, value4, value5 ) " + "values (" + "%d, %d, %d, \"%s\", \"%s\", %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d )", + GetTablePostfix(), g_stLocaleNameColumn.c_str(), + t.dwVnum, t.bType, t.bSubType, t.szName, t.szLocaleName, t.dwGold, t.dwShopBuyPrice, t.bWeight, t.bSize, + t.dwFlags, t.dwWearFlags, t.dwAntiFlags, t.dwImmuneFlag, + t.dwRefinedVnum, t.wRefineSet, t.bAlterToMagicItemPct, t.bGainSocketPct, t.sAddonType, + t.aLimits[0].bType, t.aLimits[0].lValue, t.aLimits[1].bType, t.aLimits[1].lValue, + t.aApplies[0].bType, t.aApplies[0].lValue, t.aApplies[1].bType, t.aApplies[1].lValue, t.aApplies[2].bType, t.aApplies[2].lValue, + t.alValues[0], t.alValues[1], t.alValues[2], t.alValues[3], t.alValues[4], t.alValues[5]); + CDBManager::instance().AsyncQuery(query); + } + else + { + const TItemTable& t = *it; + char query[4096]; + snprintf(query, sizeof(query), + "replace into item_proto%s (" + "vnum, type, subtype, name, gold, shop_buy_price, weight, size, " + "flag, wearflag, antiflag, immuneflag, " + "refined_vnum, refine_set, magic_pct, socket_pct, addon_type, " + "limittype0, limitvalue0, limittype1, limitvalue1, " + "applytype0, applyvalue0, applytype1, applyvalue1, applytype2, applyvalue2, " + "value0, value1, value2, value3, value4, value5 ) " + "values (" + "%d, %d, %d, \"%s\", %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d, " + "%d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d )", + GetTablePostfix(), + t.dwVnum, t.bType, t.bSubType, t.szName, t.dwGold, t.dwShopBuyPrice, t.bWeight, t.bSize, + t.dwFlags, t.dwWearFlags, t.dwAntiFlags, t.dwImmuneFlag, + t.dwRefinedVnum, t.wRefineSet, t.bAlterToMagicItemPct, t.bGainSocketPct, t.sAddonType, + t.aLimits[0].bType, t.aLimits[0].lValue, t.aLimits[1].bType, t.aLimits[1].lValue, + t.aApplies[0].bType, t.aApplies[0].lValue, t.aApplies[1].bType, t.aApplies[1].lValue, t.aApplies[2].bType, t.aApplies[2].lValue, + t.alValues[0], t.alValues[1], t.alValues[2], t.alValues[3], t.alValues[4], t.alValues[5]); + CDBManager::instance().AsyncQuery(query); + } + } + return true; +} diff --git a/db/src/ClientManagerEventFlag.cpp b/db/src/ClientManagerEventFlag.cpp new file mode 100644 index 0000000..e25a868 --- /dev/null +++ b/db/src/ClientManagerEventFlag.cpp @@ -0,0 +1,77 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" + +void CClientManager::LoadEventFlag() +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "SELECT szName, lValue FROM quest%s WHERE dwPID = 0", GetTablePostfix()); + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult* pRes = pmsg->Get(); + if (pRes->uiNumRows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + TPacketSetEventFlag p; + strlcpy(p.szFlagName, row[0], sizeof(p.szFlagName)); + str_to_number(p.lValue, row[1]); + sys_log(0, "EventFlag Load %s %d", p.szFlagName, p.lValue); + m_map_lEventFlag.insert(std::make_pair(std::string(p.szFlagName), p.lValue)); + ForwardPacket(HEADER_DG_SET_EVENT_FLAG, &p, sizeof(TPacketSetEventFlag)); + } + } +} + +void CClientManager::SetEventFlag(TPacketSetEventFlag* p) +{ + ForwardPacket(HEADER_DG_SET_EVENT_FLAG, p, sizeof(TPacketSetEventFlag)); + + bool bChanged = false; + + typeof(m_map_lEventFlag.begin()) it = m_map_lEventFlag.find(p->szFlagName); + if (it == m_map_lEventFlag.end()) + { + bChanged = true; + m_map_lEventFlag.insert(std::make_pair(std::string(p->szFlagName), p->lValue)); + } + else if (it->second != p->lValue) + { + bChanged = true; + it->second = p->lValue; + } + + if (bChanged) + { + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(0, '%s', '', %ld)", + GetTablePostfix(), p->szFlagName, p->lValue); + szQuery[1023] = '\0'; + + //CDBManager::instance().ReturnQuery(szQuery, QID_QUEST_SAVE, 0, NULL); + CDBManager::instance().AsyncQuery(szQuery); + sys_log(0, "HEADER_GD_SET_EVENT_FLAG : Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue); + return; + } + sys_log(0, "HEADER_GD_SET_EVENT_FLAG : No Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue); +} + +void CClientManager::SendEventFlagsOnSetup(CPeer* peer) +{ + typeof(m_map_lEventFlag.begin()) it; + for (it = m_map_lEventFlag.begin(); it != m_map_lEventFlag.end(); ++it) + { + TPacketSetEventFlag p; + strlcpy(p.szFlagName, it->first.c_str(), sizeof(p.szFlagName)); + p.lValue = it->second; + peer->EncodeHeader(HEADER_DG_SET_EVENT_FLAG, 0, sizeof(TPacketSetEventFlag)); + peer->Encode(&p, sizeof(TPacketSetEventFlag)); + } +} + diff --git a/db/src/ClientManagerGuild.cpp b/db/src/ClientManagerGuild.cpp new file mode 100644 index 0000000..a6bdfe0 --- /dev/null +++ b/db/src/ClientManagerGuild.cpp @@ -0,0 +1,244 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Main.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" +#include "GuildManager.h" + + +void CClientManager::GuildCreate(CPeer * peer, DWORD dwGuildID) +{ + sys_log(0, "GuildCreate %u", dwGuildID); + ForwardPacket(HEADER_DG_GUILD_LOAD, &dwGuildID, sizeof(DWORD)); + + CGuildManager::instance().Load(dwGuildID); +} + +void CClientManager::GuildChangeGrade(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildChangeGrade %u %u", p->dwGuild, p->dwInfo); + ForwardPacket(HEADER_DG_GUILD_CHANGE_GRADE, p, sizeof(TPacketGuild)); +} + +void CClientManager::GuildAddMember(CPeer* peer, TPacketGDGuildAddMember * p) +{ + CGuildManager::instance().TouchGuild(p->dwGuild); + sys_log(0, "GuildAddMember %u %u", p->dwGuild, p->dwPID); + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_member%s VALUES(%u, %u, %d, 0, 0)", + GetTablePostfix(), p->dwPID, p->dwGuild, p->bGrade); + + std::auto_ptr pmsg_insert(CDBManager::instance().DirectQuery(szQuery)); + + snprintf(szQuery, sizeof(szQuery), + "SELECT pid, grade, is_general, offer, level, job, name FROM guild_member%s, player%s WHERE guild_id = %u and pid = id and pid = %u", GetTablePostfix(), GetTablePostfix(), p->dwGuild, p->dwPID); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiNumRows == 0) + { + sys_err("Query failed when getting guild member data %s", pmsg->stQuery.c_str()); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); + + if (!row[0] || !row[1]) + return; + + TPacketDGGuildMember dg; + + dg.dwGuild = p->dwGuild; + str_to_number(dg.dwPID, row[0]); + str_to_number(dg.bGrade, row[1]); + str_to_number(dg.isGeneral, row[2]); + str_to_number(dg.dwOffer, row[3]); + str_to_number(dg.bLevel, row[4]); + str_to_number(dg.bJob, row[5]); + strlcpy(dg.szName, row[6], sizeof(dg.szName)); + + ForwardPacket(HEADER_DG_GUILD_ADD_MEMBER, &dg, sizeof(TPacketDGGuildMember)); +} + +void CClientManager::GuildRemoveMember(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildRemoveMember %u %u", p->dwGuild, p->dwInfo); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE pid=%u and guild_id=%u", GetTablePostfix(), p->dwInfo, p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(%u, 'guild_manage', 'withdraw_time', %u)", GetTablePostfix(), p->dwInfo, (DWORD) GetCurrentTime()); + CDBManager::instance().AsyncQuery(szQuery); + + ForwardPacket(HEADER_DG_GUILD_REMOVE_MEMBER, p, sizeof(TPacketGuild)); +} + +void CClientManager::GuildSkillUpdate(CPeer* peer, TPacketGuildSkillUpdate* p) +{ + sys_log(0, "GuildSkillUpdate %d", p->amount); + ForwardPacket(HEADER_DG_GUILD_SKILL_UPDATE, p, sizeof(TPacketGuildSkillUpdate)); +} + +void CClientManager::GuildExpUpdate(CPeer* peer, TPacketGuildExpUpdate* p) +{ + sys_log(0, "GuildExpUpdate %d", p->amount); + ForwardPacket(HEADER_DG_GUILD_EXP_UPDATE, p, sizeof(TPacketGuildExpUpdate), 0, peer); +} + +void CClientManager::GuildChangeMemberData(CPeer* peer, TPacketGuildChangeMemberData* p) +{ + sys_log(0, "GuildChangeMemberData %u %u %d %d", p->pid, p->offer, p->level, p->grade); + ForwardPacket(HEADER_DG_GUILD_CHANGE_MEMBER_DATA, p, sizeof(TPacketGuildChangeMemberData), 0, peer); +} + +void CClientManager::GuildDisband(CPeer* peer, TPacketGuild* p) +{ + sys_log(0, "GuildDisband %u", p->dwGuild); + + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild%s WHERE id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_grade%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) SELECT pid, 'guild_manage', 'withdraw_time', %u FROM guild_member%s WHERE guild_id = %u", GetTablePostfix(), (DWORD) GetCurrentTime(), GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_comment%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild); + CDBManager::instance().AsyncQuery(szQuery); + + ForwardPacket(HEADER_DG_GUILD_DISBAND, p, sizeof(TPacketGuild)); +} + +const char* __GetWarType(int n) +{ + switch (n) + { + case 0 : + return "п"; + case 1 : + return ""; + case 2 : + return "ȣ"; + default : + return " ȣ"; + } +} + +void CClientManager::GuildWar(CPeer* peer, TPacketGuildWar* p) +{ + switch (p->bWar) + { + case GUILD_WAR_SEND_DECLARE: + sys_log(0, "GuildWar: GUILD_WAR_SEND_DECLARE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().AddDeclare(p->bType, p->dwGuildFrom, p->dwGuildTo); + break; + + case GUILD_WAR_REFUSE: + sys_log(0, "GuildWar: GUILD_WAR_REFUSE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + break; + /* + case GUILD_WAR_WAIT_START: + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + + if (!CGuildManager::instance().WaitStart(p)) + p->bWar = GUILD_WAR_CANCEL; + + break; + */ + + case GUILD_WAR_WAIT_START: + sys_log(0, "GuildWar: GUILD_WAR_WAIT_START type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + case GUILD_WAR_RESERVE: // + if (p->bWar != GUILD_WAR_WAIT_START) + sys_log(0, "GuildWar: GUILD_WAR_RESERVE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + + if (!CGuildManager::instance().ReserveWar(p)) + p->bWar = GUILD_WAR_CANCEL; + else + p->bWar = GUILD_WAR_RESERVE; + + break; + + case GUILD_WAR_ON_WAR: // Ų. (ʵ ٷ ) + sys_log(0, "GuildWar: GUILD_WAR_ON_WAR type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().StartWar(p->bType, p->dwGuildFrom, p->dwGuildTo); + break; + + case GUILD_WAR_OVER: // + sys_log(0, "GuildWar: GUILD_WAR_OVER type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RecvWarOver(p->dwGuildFrom, p->dwGuildTo, p->bType, p->lWarPrice); + break; + + case GUILD_WAR_END: // + sys_log(0, "GuildWar: GUILD_WAR_END type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().RecvWarEnd(p->dwGuildFrom, p->dwGuildTo); + return; // NOTE: RecvWarEnd Ŷ Ƿ εij ʴ´. + + case GUILD_WAR_CANCEL : + sys_log(0, "GuildWar: GUILD_WAR_CANCEL type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo); + CGuildManager::instance().CancelWar(p->dwGuildFrom, p->dwGuildTo); + break; + } + + ForwardPacket(HEADER_DG_GUILD_WAR, p, sizeof(TPacketGuildWar)); +} + +void CClientManager::GuildWarScore(CPeer* peer, TPacketGuildWarScore * p) +{ + CGuildManager::instance().UpdateScore(p->dwGuildGainPoint, p->dwGuildOpponent, p->lScore, p->lBetScore); +} + +void CClientManager::GuildChangeLadderPoint(TPacketGuildLadderPoint* p) +{ + sys_log(0, "GuildChangeLadderPoint Recv %u %d", p->dwGuild, p->lChange); + CGuildManager::instance().ChangeLadderPoint(p->dwGuild, p->lChange); +} + +void CClientManager::GuildUseSkill(TPacketGuildUseSkill* p) +{ + sys_log(0, "GuildUseSkill Recv %u %d", p->dwGuild, p->dwSkillVnum); + CGuildManager::instance().UseSkill(p->dwGuild, p->dwSkillVnum, p->dwCooltime); + SendGuildSkillUsable(p->dwGuild, p->dwSkillVnum, false); +} + +void CClientManager::SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable) +{ + sys_log(0, "SendGuildSkillUsable Send %u %d %s", guild_id, dwSkillVnum, bUsable?"true":"false"); + + TPacketGuildSkillUsableChange p; + + p.dwGuild = guild_id; + p.dwSkillVnum = dwSkillVnum; + p.bUsable = bUsable; + + ForwardPacket(HEADER_DG_GUILD_SKILL_USABLE_CHANGE, &p, sizeof(TPacketGuildSkillUsableChange)); +} + +void CClientManager::GuildChangeMaster(TPacketChangeGuildMaster* p) +{ + if (CGuildManager::instance().ChangeMaster(p->dwGuildID, p->idFrom, p->idTo) == true) + { + TPacketChangeGuildMaster packet; + packet.dwGuildID = p->dwGuildID; + packet.idFrom = 0; + packet.idTo = 0; + + ForwardPacket(HEADER_DG_ACK_CHANGE_GUILD_MASTER, &packet, sizeof(packet)); + } +} + diff --git a/db/src/ClientManagerHorseName.cpp b/db/src/ClientManagerHorseName.cpp new file mode 100644 index 0000000..b346ab2 --- /dev/null +++ b/db/src/ClientManagerHorseName.cpp @@ -0,0 +1,40 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" + +void CClientManager::UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer) +{ + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO horse_name VALUES(%u, '%s')", data->dwPlayerID, data->szHorseName); + + std::auto_ptr pmsg_insert(CDBManager::instance().DirectQuery(szQuery)); + + ForwardPacket(HEADER_DG_UPDATE_HORSE_NAME, data, sizeof(TPacketUpdateHorseName), 0, peer); +} + +void CClientManager::AckHorseName(DWORD dwPID, CPeer* peer) +{ + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), "SELECT name FROM horse_name WHERE id = %u", dwPID); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + TPacketUpdateHorseName packet; + packet.dwPlayerID = dwPID; + + if (pmsg->Get()->uiNumRows == 0) + { + memset(packet.szHorseName, 0, sizeof (packet.szHorseName)); + } + else + { + MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); + strlcpy(packet.szHorseName, row[0], sizeof(packet.szHorseName)); + } + + peer->EncodeHeader(HEADER_DG_ACK_HORSE_NAME, 0, sizeof(TPacketUpdateHorseName)); + peer->Encode(&packet, sizeof(TPacketUpdateHorseName)); +} + diff --git a/db/src/ClientManagerLogin.cpp b/db/src/ClientManagerLogin.cpp new file mode 100644 index 0000000..affc48e --- /dev/null +++ b/db/src/ClientManagerLogin.cpp @@ -0,0 +1,544 @@ + +#include "stdafx.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "Config.h" +#include "QID.h" +#include "Cache.h" + +extern std::string g_stLocale; +extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab); +extern int g_test_server; +extern int g_log; + +bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin); + + if (m_map_kLogonAccount.end() != it) + return false; + + CLoginData * pkLD = GetLoginDataByLogin(c_pszLogin); + + if (!pkLD) + return false; + + pkLD->SetConnectedPeerHandle(dwHandle); + pkLD->SetIP(c_pszIP); + + m_map_kLogonAccount.insert(TLogonAccountMap::value_type(szLogin, pkLD)); + return true; +} + +bool CClientManager::DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin); + + if (it == m_map_kLogonAccount.end()) + return false; + + CLoginData * pkLD = it->second; + + if (pkLD->GetConnectedPeerHandle() != dwHandle) + { + sys_err("%s tried to logout in other peer handle %lu, current handle %lu", szLogin, dwHandle, pkLD->GetConnectedPeerHandle()); + return false; + } + + if (pkLD->IsPlay()) + { + pkLD->SetPlay(false); + SendLoginToBilling(pkLD, false); + } + + if (pkLD->IsDeleted()) + { + delete pkLD; + } + + m_map_kLogonAccount.erase(it); + return true; +} + +bool CClientManager::FindLogonAccount(const char * c_pszLogin) +{ + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin)); + + if (m_map_kLogonAccount.end() == m_map_kLogonAccount.find(szLogin)) + return false; + + return true; +} + +void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p) +{ +#ifdef ENABLE_LIMIT_TIME + static int s_updateCount = 0; + static int s_curTime = time(0); + if (s_updateCount > 100) + { + s_curTime = time(0); + s_updateCount = 0; + } + ++s_updateCount; + + if (s_curTime >= GLOBAL_LIMIT_TIME) + { + sys_err("Server life time expired."); + exit(0); + return; + } +#endif + + CLoginData * pkLoginData = GetLoginData(p->dwLoginKey); + char szLogin[LOGIN_MAX_LEN + 1]; + trim_and_lower(p->szLogin, szLogin, sizeof(szLogin)); + + if (!pkLoginData) + { + sys_log(0, "LOGIN_BY_KEY key not exist %s %lu", szLogin, p->dwLoginKey); + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + TAccountTable & r = pkLoginData->GetAccountRef(); + + if (FindLogonAccount(r.login)) + { + sys_log(0, "LOGIN_BY_KEY already login %s %lu", r.login, p->dwLoginKey); + TPacketDGLoginAlready ptog; + strlcpy(ptog.szLogin, szLogin, sizeof(ptog.szLogin)); + pkPeer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, dwHandle, sizeof(TPacketDGLoginAlready)); + pkPeer->Encode(&ptog, sizeof(TPacketDGLoginAlready)); + return; + } + + if (strcasecmp(r.login, szLogin)) + { + sys_log(0, "LOGIN_BY_KEY login differ %s %lu input %s", r.login, p->dwLoginKey, szLogin); + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + if (memcmp(pkLoginData->GetClientKey(), p->adwClientKey, sizeof(DWORD) * 4)) + { + const DWORD * pdwClientKey = pkLoginData->GetClientKey(); + + sys_log(0, "LOGIN_BY_KEY client key differ %s %lu %lu %lu %lu, %lu %lu %lu %lu", + r.login, + p->adwClientKey[0], p->adwClientKey[1], p->adwClientKey[2], p->adwClientKey[3], + pdwClientKey[0], pdwClientKey[1], pdwClientKey[2], pdwClientKey[3]); + + pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle); + return; + } + + TAccountTable * pkTab = new TAccountTable; + memset(pkTab, 0, sizeof(TAccountTable)); + + pkTab->id = r.id; + trim_and_lower(r.login, pkTab->login, sizeof(pkTab->login)); + strlcpy(pkTab->passwd, r.passwd, sizeof(pkTab->passwd)); + strlcpy(pkTab->social_id, r.social_id, sizeof(pkTab->social_id)); + strlcpy(pkTab->status, "OK", sizeof(pkTab->status)); + + ClientHandleInfo * info = new ClientHandleInfo(dwHandle); + info->pAccountTable = pkTab; + strlcpy(info->ip, p->szIP, sizeof(info->ip)); + + sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip); + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); +} + +void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + if (msg->uiSQLErrno != 0) + { + peer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle); + delete info; + return; + } + + char szQuery[QUERY_MAX_LEN]; + + if (msg->Get()->uiNumRows == 0) + { + DWORD account_id = info->pAccountTable->id; + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), account_id); + std::auto_ptr pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER)); + + sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id); + + // PLAYER_INDEX_CREATE_BUG_FIX + //snprintf(szQuery, sizeof(szQuery), "INSERT IGNORE INTO player_index%s (id) VALUES(%lu)", GetTablePostfix(), info->pAccountTable->id); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO player_index%s (id) VALUES(%u)", GetTablePostfix(), info->pAccountTable->id); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_INDEX_CREATE, peer->GetHandle(), info); + // END_PLAYER_INDEX_CREATE_BUF_FIX + } + return; + } + + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + int col = 0; + + for (; col < PLAYER_PER_ACCOUNT; ++col) + str_to_number(info->pAccountTable->players[col].dwID, row[col]); + + str_to_number(info->pAccountTable->bEmpire, row[col++]); + info->account_index = 1; + + extern std::string g_stLocale; + if (g_stLocale == "gb2312") + { + snprintf(szQuery, sizeof(szQuery), + "SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + } + else + { + snprintf(szQuery, sizeof(szQuery), + "SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + } + + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN, peer->GetHandle(), info); +} + +// PLAYER_INDEX_CREATE_BUG_FIX +void CClientManager::RESULT_PLAYER_INDEX_CREATE(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), + info->pAccountTable->id); + CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); +} +// END_PLAYER_INDEX_CREATE_BUG_FIX + +TAccountTable * CreateAccountTableFromRes(MYSQL_RES * res) +{ + char input_pwd[PASSWD_MAX_LEN + 1]; + MYSQL_ROW row = NULL; + DWORD col; + + row = mysql_fetch_row(res); + col = 0; + + TAccountTable * pkTab = new TAccountTable; + memset(pkTab, 0, sizeof(TAccountTable)); + + // ù° ÷ ͸ Ѵ (JOIN QUERY ) + strlcpy(input_pwd, row[col++], sizeof(input_pwd)); + str_to_number(pkTab->id, row[col++]); + strlcpy(pkTab->login, row[col++], sizeof(pkTab->login)); + strlcpy(pkTab->passwd, row[col++], sizeof(pkTab->passwd)); + strlcpy(pkTab->social_id, row[col++], sizeof(pkTab->social_id)); + str_to_number(pkTab->bEmpire, row[col++]); + + for (int j = 0; j < PLAYER_PER_ACCOUNT; ++j) + str_to_number(pkTab->players[j].dwID, row[col++]); + + strlcpy(pkTab->status, row[col++], sizeof(pkTab->status)); + + if (strcmp(pkTab->passwd, input_pwd)) + { + delete pkTab; + return NULL; + } + + return pkTab; +} + +void CreateAccountPlayerDataFromRes(MYSQL_RES * pRes, TAccountTable * pkTab) +{ + if (!pRes) + return; + + for (DWORD i = 0; i < mysql_num_rows(pRes); ++i) + { + MYSQL_ROW row = mysql_fetch_row(pRes); + int col = 0; + + DWORD player_id = 0; + !row[col++] ? 0 : str_to_number(player_id, row[col - 1]); + + if (!player_id) + continue; + + int j; + + for (j = 0; j < PLAYER_PER_ACCOUNT; ++j) + { + if (pkTab->players[j].dwID == player_id) + { + CPlayerTableCache * pc = CClientManager::instance().GetPlayerCache(player_id); + TPlayerTable * pt = pc ? pc->Get(false) : NULL; + + if (pt) + { + strlcpy(pkTab->players[j].szName, pt->name, sizeof(pkTab->players[j].szName)); + + pkTab->players[j].byJob = pt->job; + pkTab->players[j].byLevel = pt->level; + pkTab->players[j].dwPlayMinutes = pt->playtime; + pkTab->players[j].byST = pt->st; + pkTab->players[j].byHT = pt->ht; + pkTab->players[j].byDX = pt->dx; + pkTab->players[j].byIQ = pt->iq; + pkTab->players[j].wMainPart = pt->parts[PART_MAIN]; + pkTab->players[j].wHairPart = pt->parts[PART_HAIR]; + pkTab->players[j].x = pt->x; + pkTab->players[j].y = pt->y; + pkTab->players[j].skill_group = pt->skill_group; + pkTab->players[j].bChangeName = 0; + } + else + { + if (!row[col++]) + *pkTab->players[j].szName = '\0'; + else + strlcpy(pkTab->players[j].szName, row[col - 1], sizeof(pkTab->players[j].szName)); + + pkTab->players[j].byJob = 0; + pkTab->players[j].byLevel = 0; + pkTab->players[j].dwPlayMinutes = 0; + pkTab->players[j].byST = 0; + pkTab->players[j].byHT = 0; + pkTab->players[j].byDX = 0; + pkTab->players[j].byIQ = 0; + pkTab->players[j].wMainPart = 0; + pkTab->players[j].wHairPart = 0; + pkTab->players[j].x = 0; + pkTab->players[j].y = 0; + pkTab->players[j].skill_group = 0; + pkTab->players[j].bChangeName = 0; + + str_to_number(pkTab->players[j].byJob, row[col++]); + str_to_number(pkTab->players[j].byLevel, row[col++]); + str_to_number(pkTab->players[j].dwPlayMinutes, row[col++]); + str_to_number(pkTab->players[j].byST, row[col++]); + str_to_number(pkTab->players[j].byHT, row[col++]); + str_to_number(pkTab->players[j].byDX, row[col++]); + str_to_number(pkTab->players[j].byIQ, row[col++]); + str_to_number(pkTab->players[j].wMainPart, row[col++]); + str_to_number(pkTab->players[j].wHairPart, row[col++]); + str_to_number(pkTab->players[j].x, row[col++]); + str_to_number(pkTab->players[j].y, row[col++]); + str_to_number(pkTab->players[j].skill_group, row[col++]); + str_to_number(pkTab->players[j].bChangeName, row[col++]); + } + + sys_log(0, "%s %lu %lu hair %u", + pkTab->players[j].szName, pkTab->players[j].x, pkTab->players[j].y, pkTab->players[j].wHairPart); + break; + } + } + /* + if (j == PLAYER_PER_ACCOUNT) + sys_err("cannot find player_id on this account (login: %s id %lu account %lu %lu %lu)", + pkTab->login, player_id, + pkTab->players[0].dwID, + pkTab->players[1].dwID, + pkTab->players[2].dwID); + */ + } +} + +void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData; + + if (info->account_index == 0) + { + // ? + if (msg->Get()->uiNumRows == 0) + { + sys_log(0, "RESULT_LOGIN: no account"); + peer->EncodeHeader(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle, 0); + delete info; + return; + } + + info->pAccountTable = CreateAccountTableFromRes(msg->Get()->pSQLResult); + + if (!info->pAccountTable) + { + sys_log(0, "RESULT_LOGIN: no account : WRONG_PASSWD"); + peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle); + delete info; + } + else + { + ++info->account_index; + + char queryStr[512]; + extern std::string g_stLocale; + if (g_stLocale == "gb2312") + { + snprintf(queryStr, sizeof(queryStr), + "SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + } + else + { + snprintf(queryStr, sizeof(queryStr), + "SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u", + GetTablePostfix(), info->pAccountTable->id); + } + + CDBManager::instance().ReturnQuery(queryStr, QID_LOGIN, peer->GetHandle(), info); + } + return; + } + else + { + if (!info->pAccountTable) // ̷ ;; + { + peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle); + delete info; + return; + } + + // ٸ ؼ ̹ α عȴٸ.. ̹ ߴٰ Ѵ. + if (!InsertLogonAccount(info->pAccountTable->login, peer->GetHandle(), info->ip)) + { + sys_log(0, "RESULT_LOGIN: already logon %s", info->pAccountTable->login); + + TPacketDGLoginAlready p; + strlcpy(p.szLogin, info->pAccountTable->login, sizeof(p.szLogin)); + + peer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, info->dwHandle, sizeof(TPacketDGLoginAlready)); + peer->Encode(&p, sizeof(p)); + } + else + { + sys_log(0, "RESULT_LOGIN: login success %s rows: %lu", info->pAccountTable->login, msg->Get()->uiNumRows); + + if (msg->Get()->uiNumRows > 0) + CreateAccountPlayerDataFromRes(msg->Get()->pSQLResult, info->pAccountTable); + + //PREVENT_COPY_ITEM + CLoginData * p = GetLoginDataByLogin(info->pAccountTable->login); + memcpy(&p->GetAccountRef(), info->pAccountTable, sizeof(TAccountTable)); + + //END_PREVENT_COPY_ITEM + peer->EncodeHeader(HEADER_DG_LOGIN_SUCCESS, info->dwHandle, sizeof(TAccountTable)); + peer->Encode(info->pAccountTable, sizeof(TAccountTable)); + + } + + delete info->pAccountTable; + info->pAccountTable = NULL; + delete info; + } +} + +void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data) +{ + TLogoutPacket* packet = (TLogoutPacket*)data; + + if (!*packet->login) + return; + + CLoginData * pLoginData = GetLoginDataByLogin(packet->login); + + if (pLoginData == NULL) + return; + + int pid[PLAYER_PER_ACCOUNT]; + + for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n) + { + if (pLoginData->GetAccountRef().players[n].dwID == 0) + { + if (g_test_server) + sys_log(0, "LOGOUT %s %d", packet->login, pLoginData->GetAccountRef().players[n].dwID); + continue; + } + + pid[n] = pLoginData->GetAccountRef().players[n].dwID; + + if (g_log) + sys_log(0, "LOGOUT InsertLogoutPlayer %s %d", packet->login, pid[n]); + + InsertLogoutPlayer(pid[n]); + } + + if (DeleteLogonAccount(packet->login, peer->GetHandle())) + { + if (g_log) + sys_log(0, "LOGOUT %s ", packet->login); + } +} + +void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p) +{ + char queryStr[QUERY_MAX_LEN]; + + if (g_stLocale == "sjis") + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u", + GetTablePostfix(), p->name, p->pid); + else + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid); + + std::auto_ptr pMsg(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER)); + + if (pMsg->Get()->uiNumRows) + { + if (!pMsg->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + if (*row[0] != '0') + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + snprintf(queryStr, sizeof(queryStr), + "UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid); + + std::auto_ptr pMsg0(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER)); + + TPacketDGChangeName pdg; + peer->EncodeHeader(HEADER_DG_CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName)); + pdg.pid = p->pid; + strlcpy(pdg.name, p->name, sizeof(pdg.name)); + peer->Encode(&pdg, sizeof(TPacketDGChangeName)); +} + diff --git a/db/src/ClientManagerParty.cpp b/db/src/ClientManagerParty.cpp new file mode 100644 index 0000000..2e719a6 --- /dev/null +++ b/db/src/ClientManagerParty.cpp @@ -0,0 +1,135 @@ +// vim:ts=4 sw=4 +#include "stdafx.h" +#include "ClientManager.h" +#include "Config.h" +#include "DBManager.h" +#include "QID.h" + +void CClientManager::QUERY_PARTY_CREATE(CPeer* peer, TPacketPartyCreate* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + + if (pm.find(p->dwLeaderPID) == pm.end()) + { + pm.insert(make_pair(p->dwLeaderPID, TPartyMember())); + ForwardPacket(HEADER_DG_PARTY_CREATE, p, sizeof(TPacketPartyCreate), peer->GetChannel(), peer); + sys_log(0, "PARTY Create [%lu]", p->dwLeaderPID); + } + else + { + sys_err("PARTY Create - Already exists [%lu]", p->dwLeaderPID); + } +} + +void CClientManager::QUERY_PARTY_DELETE(CPeer* peer, TPacketPartyDelete* p) +{ + TPartyMap& pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Delete - Non exists [%lu]", p->dwLeaderPID); + return; + } + + pm.erase(it); + ForwardPacket(HEADER_DG_PARTY_DELETE, p, sizeof(TPacketPartyDelete), peer->GetChannel(), peer); + sys_log(0, "PARTY Delete [%lu]", p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_ADD(CPeer* peer, TPacketPartyAdd* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Add - Non exists [%lu]", p->dwLeaderPID); + return; + } + + if (it->second.find(p->dwPID) == it->second.end()) + { + it->second.insert(std::make_pair(p->dwPID, TPartyInfo())); + ForwardPacket(HEADER_DG_PARTY_ADD, p, sizeof(TPacketPartyAdd), peer->GetChannel(), peer); + sys_log(0, "PARTY Add [%lu] to [%lu]", p->dwPID, p->dwLeaderPID); + } + else + sys_err("PARTY Add - Already [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_REMOVE(CPeer* peer, TPacketPartyRemove* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY Remove - Non exists [%lu] cannot remove [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit != it->second.end()) + { + it->second.erase(pit); + ForwardPacket(HEADER_DG_PARTY_REMOVE, p, sizeof(TPacketPartyRemove), peer->GetChannel(), peer); + sys_log(0, "PARTY Remove [%lu] to [%lu]", p->dwPID, p->dwLeaderPID); + } + else + sys_err("PARTY Remove - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); +} + +void CClientManager::QUERY_PARTY_STATE_CHANGE(CPeer* peer, TPacketPartyStateChange* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY StateChange - Non exists [%lu] cannot state change [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit == it->second.end()) + { + sys_err("PARTY StateChange - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); + return; + } + + if (p->bFlag) + pit->second.bRole = p->bRole; + else + pit->second.bRole = 0; + + ForwardPacket(HEADER_DG_PARTY_STATE_CHANGE, p, sizeof(TPacketPartyStateChange), peer->GetChannel(), peer); + sys_log(0, "PARTY StateChange [%lu] at [%lu] from %d %d",p->dwPID, p->dwLeaderPID, p->bRole, p->bFlag); +} + +void CClientManager::QUERY_PARTY_SET_MEMBER_LEVEL(CPeer* peer, TPacketPartySetMemberLevel* p) +{ + TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()]; + itertype(pm) it = pm.find(p->dwLeaderPID); + + if (it == pm.end()) + { + sys_err("PARTY SetMemberLevel - Non exists [%lu] cannot level change [%lu]",p->dwLeaderPID, p->dwPID); + return; + } + + itertype(it->second) pit = it->second.find(p->dwPID); + + if (pit == it->second.end()) + { + sys_err("PARTY SetMemberLevel - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID); + return; + } + + pit->second.bLevel = p->bLevel; + + ForwardPacket(HEADER_DG_PARTY_SET_MEMBER_LEVEL, p, sizeof(TPacketPartySetMemberLevel), peer->GetChannel()); + sys_log(0, "PARTY SetMemberLevel pid [%lu] level %d",p->dwPID, p->bLevel); +} diff --git a/db/src/ClientManagerPlayer.cpp b/db/src/ClientManagerPlayer.cpp new file mode 100644 index 0000000..c93c5f9 --- /dev/null +++ b/db/src/ClientManagerPlayer.cpp @@ -0,0 +1,1357 @@ + +#include "stdafx.h" + +#include "ClientManager.h" + +#include "Main.h" +#include "QID.h" +#include "ItemAwardManager.h" +#include "HB.h" +#include "Cache.h" + +extern bool g_bHotBackup; + +extern std::string g_stLocale; +extern int g_test_server; +extern int g_log; + +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Check all SELECT syntax on item table before change this function!!! +// +bool CreateItemTableFromRes(MYSQL_RES * res, std::vector * pVec, DWORD dwPID) +{ + if (!res) + { + pVec->clear(); + return true; + } + + int rows; + + if ((rows = mysql_num_rows(res)) <= 0) // + { + pVec->clear(); + return true; + } + + pVec->resize(rows); + + for (int i = 0; i < rows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(res); + TPlayerItem & item = pVec->at(i); + + int cur = 0; + + // Check all SELECT syntax on item table before change this function!!! + // Check all SELECT syntax on item table before change this function!!! + // Check all SELECT syntax on item table before change this function!!! + str_to_number(item.id, row[cur++]); + str_to_number(item.window, row[cur++]); + str_to_number(item.pos, row[cur++]); + str_to_number(item.count, row[cur++]); + str_to_number(item.vnum, row[cur++]); + str_to_number(item.alSockets[0], row[cur++]); + str_to_number(item.alSockets[1], row[cur++]); + str_to_number(item.alSockets[2], row[cur++]); + + for (int j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++) + { + str_to_number(item.aAttr[j].bType, row[cur++]); + str_to_number(item.aAttr[j].sValue, row[cur++]); + } + + item.owner = dwPID; + } + + return true; +} + +size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab) +{ + size_t queryLen; + + queryLen = snprintf(pszQuery, querySize, + "UPDATE player%s SET " + "job = %d, " + "voice = %d, " + "dir = %d, " + "x = %d, " + "y = %d, " + "z = %d, " + "map_index = %d, " + "exit_x = %ld, " + "exit_y = %ld, " + "exit_map_index = %ld, " + "hp = %d, " + "mp = %d, " + "stamina = %d, " + "random_hp = %d, " + "random_sp = %d, " + "playtime = %d, " + "level = %d, " + "level_step = %d, " + "st = %d, " + "ht = %d, " + "dx = %d, " + "iq = %d, " + "gold = %d, " + "exp = %u, " + "stat_point = %d, " + "skill_point = %d, " + "sub_skill_point = %d, " + "stat_reset_count = %d, " + "ip = '%s', " + "part_main = %d, " + "part_hair = %d, " + "last_play = NOW(), " + "skill_group = %d, " + "alignment = %ld, " + "horse_level = %d, " + "horse_riding = %d, " + "horse_hp = %d, " + "horse_hp_droptime = %u, " + "horse_stamina = %d, " + "horse_skill_point = %d, " + , + GetTablePostfix(), + pkTab->job, + pkTab->voice, + pkTab->dir, + pkTab->x, + pkTab->y, + pkTab->z, + pkTab->lMapIndex, + pkTab->lExitX, + pkTab->lExitY, + pkTab->lExitMapIndex, + pkTab->hp, + pkTab->sp, + pkTab->stamina, + pkTab->sRandomHP, + pkTab->sRandomSP, + pkTab->playtime, + pkTab->level, + pkTab->level_step, + pkTab->st, + pkTab->ht, + pkTab->dx, + pkTab->iq, + pkTab->gold, + pkTab->exp, + pkTab->stat_point, + pkTab->skill_point, + pkTab->sub_skill_point, + pkTab->stat_reset_count, + pkTab->ip, + pkTab->parts[PART_MAIN], + pkTab->parts[PART_HAIR], + pkTab->skill_group, + pkTab->lAlignment, + pkTab->horse.bLevel, + pkTab->horse.bRiding, + pkTab->horse.sHealth, + pkTab->horse.dwHorseHealthDropTime, + pkTab->horse.sStamina, + pkTab->horse_skill_point); + + // Binary ٲٱ ӽ + static char text[8192 + 1]; + + CDBManager::instance().EscapeString(text, pkTab->skills, sizeof(pkTab->skills)); + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "skill_level = '%s', ", text); + + CDBManager::instance().EscapeString(text, pkTab->quickslot, sizeof(pkTab->quickslot)); + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "quickslot = '%s' ", text); + + queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, " WHERE id=%d", pkTab->id); + return queryLen; +} + +CPlayerTableCache * CClientManager::GetPlayerCache(DWORD id) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find(id); + + if (it == m_map_playerCache.end()) + return NULL; + + TPlayerTable* pTable = it->second->Get(false); + pTable->logoff_interval = GetCurrentTime() - it->second->GetLastUpdateTime(); + return it->second; +} + +void CClientManager::PutPlayerCache(TPlayerTable * pNew) +{ + CPlayerTableCache * c; + + c = GetPlayerCache(pNew->id); + + if (!c) + { + c = new CPlayerTableCache; + m_map_playerCache.insert(TPlayerTableCacheMap::value_type(pNew->id, c)); + } + + if (g_bHotBackup) + PlayerHB::instance().Put(pNew->id); + + c->Put(pNew); +} + +/* + * PLAYER LOAD + */ +void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket * packet) +{ + CPlayerTableCache * c; + TPlayerTable * pTab; + + // + // ij͵ ijó + // + CLoginData * pLoginData = GetLoginDataByAID(packet->account_id); + + if (pLoginData) + { + for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n) + if (pLoginData->GetAccountRef().players[n].dwID != 0) + DeleteLogoutPlayer(pLoginData->GetAccountRef().players[n].dwID); + } + + //---------------------------------------------------------------- + // 1. DBCache : DBCache + // 2. DBCache : DB + // --------------------------------------------------------------- + + //---------------------------------- + // 1. DBCache : DBCache + //---------------------------------- + if ((c = GetPlayerCache(packet->player_id))) + { + CLoginData * pkLD = GetLoginDataByAID(packet->account_id); + + if (!pkLD || pkLD->IsPlay()) + { + sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0); + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, dwHandle, 0); + return; + } + + pTab = c->Get(); + + pkLD->SetPlay(true); + SendLoginToBilling(pkLD, true); + thecore_memcpy(pTab->aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(pTab->aiPremiumTimes)); + + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_SUCCESS, dwHandle, sizeof(TPlayerTable)); + peer->Encode(pTab, sizeof(TPlayerTable)); + + if (packet->player_id != pkLD->GetLastPlayerID()) + { + TPacketNeedLoginLogInfo logInfo; + logInfo.dwPlayerID = packet->player_id; + + pkLD->SetLastPlayerID( packet->player_id ); + + peer->EncodeHeader( HEADER_DG_NEED_LOGIN_LOG, dwHandle, sizeof(TPacketNeedLoginLogInfo) ); + peer->Encode( &logInfo, sizeof(TPacketNeedLoginLogInfo) ); + } + + char szQuery[1024] = { 0, }; + + TItemCacheSet * pSet = GetItemCacheSet(pTab->id); + + sys_log(0, "[PLAYER_LOAD] ID %s pid %d gold %d ", pTab->name, pTab->id, pTab->gold); + + //-------------------------------------------- + // & AFFECT & QUEST ε : + //-------------------------------------------- + // 1) DBCache : DBCache + // 2) DBCache : DB + + ///////////////////////////////////////////// + // 1) DBCache : DBCache + ///////////////////////////////////////////// + if (pSet) + { + static std::vector s_items; + s_items.resize(pSet->size()); + + DWORD dwCount = 0; + TItemCacheSet::iterator it = pSet->begin(); + + while (it != pSet->end()) + { + CItemCache * c = *it++; + TPlayerItem * p = c->Get(); + + if (p->vnum) // vnum ̴. + thecore_memcpy(&s_items[dwCount++], p, sizeof(TPlayerItem)); + } + + if (g_test_server) + sys_log(0, "ITEM_CACHE: HIT! %s count: %u", pTab->name, dwCount); + + peer->EncodeHeader(HEADER_DG_ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount); + peer->EncodeDWORD(dwCount); + + if (dwCount) + peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount); + + // Quest + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d AND lValue<>0", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle,0,packet->account_id)); + + // Affect + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle)); + } + ///////////////////////////////////////////// + // 2) DBCache : DB + ///////////////////////////////////////////// + else + { + snprintf(szQuery, sizeof(szQuery), + "SELECT id,window+0,pos,count,vnum,socket0,socket1,socket2,attrtype0,attrvalue0,attrtype1,attrvalue1,attrtype2,attrvalue2,attrtype3,attrvalue3,attrtype4,attrvalue4,attrtype5,attrvalue5,attrtype6,attrvalue6 " + "FROM item%s WHERE owner_id=%d AND (window < %d or window = %d)", + GetTablePostfix(), pTab->id, SAFEBOX, DRAGON_SOUL_INVENTORY); + + CDBManager::instance().ReturnQuery(szQuery, + QID_ITEM, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID, szName, szState, lValue FROM quest%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, + QID_QUEST, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + snprintf(szQuery, sizeof(szQuery), + "SELECT dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), pTab->id); + + CDBManager::instance().ReturnQuery(szQuery, + QID_AFFECT, + peer->GetHandle(), + new ClientHandleInfo(dwHandle, pTab->id)); + } + //ljw + //return; + } + //---------------------------------- + // 2. DBCache : DB + //---------------------------------- + else + { + sys_log(0, "[PLAYER_LOAD] Load from PlayerDB pid[%d]", packet->player_id); + + char queryStr[QUERY_MAX_LEN]; + + //-------------------------------------------------------------- + // ij : DB + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT " + "id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime," + "gold,level,level_step,st,ht,dx,iq,exp," + "stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair," + "skill_level,quickslot,skill_group,alignment,mobile,horse_level,horse_riding,horse_hp,horse_hp_droptime,horse_stamina," + "UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_play),horse_skill_point FROM player%s WHERE id=%d", + GetTablePostfix(), packet->player_id); + + ClientHandleInfo * pkInfo = new ClientHandleInfo(dwHandle, packet->player_id); + pkInfo->account_id = packet->account_id; + CDBManager::instance().ReturnQuery(queryStr, QID_PLAYER, peer->GetHandle(), pkInfo); + + //-------------------------------------------------------------- + // + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT id,window+0,pos,count,vnum,socket0,socket1,socket2,attrtype0,attrvalue0,attrtype1,attrvalue1,attrtype2,attrvalue2,attrtype3,attrvalue3,attrtype4,attrvalue4,attrtype5,attrvalue5,attrtype6,attrvalue6 " + "FROM item%s WHERE owner_id=%d AND (window < %d or window = %d)", + GetTablePostfix(), packet->player_id, SAFEBOX, DRAGON_SOUL_INVENTORY); + CDBManager::instance().ReturnQuery(queryStr, QID_ITEM, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id)); + + //-------------------------------------------------------------- + // QUEST + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d", + GetTablePostfix(), packet->player_id); + CDBManager::instance().ReturnQuery(queryStr, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id,packet->account_id)); + // ɿ item_award̺ login account id Ѱش + //-------------------------------------------------------------- + // AFFECT + //-------------------------------------------------------------- + snprintf(queryStr, sizeof(queryStr), + "SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d", + GetTablePostfix(), packet->player_id); + CDBManager::instance().ReturnQuery(queryStr, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id)); + } + + +} +void CClientManager::ItemAward(CPeer * peer,char* login) +{ + char login_t[LOGIN_MAX_LEN + 1] = ""; + strlcpy(login_t,login,LOGIN_MAX_LEN + 1); + std::set * pSet = ItemAwardManager::instance().GetByLogin(login_t); + if(pSet == NULL) + return; + typeof(pSet->begin()) it = pSet->begin(); //taken_time NULLΰ͵ о + while(it != pSet->end() ) + { + TItemAward * pItemAward = *(it++); + char* whyStr = pItemAward->szWhy; //why ݷ б + char cmdStr[100] = ""; //whyݷ뿡 ӽ ڿ ص + strcpy(cmdStr,whyStr); //ɾ ū ūȭ DZ + char command[20] = ""; + strcpy(command,GetCommand(cmdStr)); // command + if( !(strcmp(command,"GIFT") )) // command GIFT̸ + { + TPacketItemAwardInfromer giftData; + strcpy(giftData.login, pItemAward->szLogin); //α ̵ + strcpy(giftData.command, command); //ɾ + giftData.vnum = pItemAward->dwVnum; // vnum + ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer)); + } + } +} +char* CClientManager::GetCommand(char* str) +{ + char command[20] = ""; + char* tok; + + if( str[0] == '[' ) + { + tok = strtok(str,"]"); + strcat(command,&tok[1]); + } + + return command; +} + +bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab) +{ + if (mysql_num_rows(res) == 0) // + return false; + + memset(pkTab, 0, sizeof(TPlayerTable)); + + MYSQL_ROW row = mysql_fetch_row(res); + + int col = 0; + + // "id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime," + // "gold,level,level_step,st,ht,dx,iq,exp," + // "stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair," + // "skill_level,quickslot,skill_group,alignment,mobile,horse_level,horse_riding,horse_hp,horse_stamina FROM player%s WHERE id=%d", + str_to_number(pkTab->id, row[col++]); + strlcpy(pkTab->name, row[col++], sizeof(pkTab->name)); + str_to_number(pkTab->job, row[col++]); + str_to_number(pkTab->voice, row[col++]); + str_to_number(pkTab->dir, row[col++]); + str_to_number(pkTab->x, row[col++]); + str_to_number(pkTab->y, row[col++]); + str_to_number(pkTab->z, row[col++]); + str_to_number(pkTab->lMapIndex, row[col++]); + str_to_number(pkTab->lExitX, row[col++]); + str_to_number(pkTab->lExitY, row[col++]); + str_to_number(pkTab->lExitMapIndex, row[col++]); + str_to_number(pkTab->hp, row[col++]); + str_to_number(pkTab->sp, row[col++]); + str_to_number(pkTab->stamina, row[col++]); + str_to_number(pkTab->sRandomHP, row[col++]); + str_to_number(pkTab->sRandomSP, row[col++]); + str_to_number(pkTab->playtime, row[col++]); + str_to_number(pkTab->gold, row[col++]); + str_to_number(pkTab->level, row[col++]); + str_to_number(pkTab->level_step, row[col++]); + str_to_number(pkTab->st, row[col++]); + str_to_number(pkTab->ht, row[col++]); + str_to_number(pkTab->dx, row[col++]); + str_to_number(pkTab->iq, row[col++]); + str_to_number(pkTab->exp, row[col++]); + str_to_number(pkTab->stat_point, row[col++]); + str_to_number(pkTab->skill_point, row[col++]); + str_to_number(pkTab->sub_skill_point, row[col++]); + str_to_number(pkTab->stat_reset_count, row[col++]); + str_to_number(pkTab->part_base, row[col++]); + str_to_number(pkTab->parts[PART_HAIR], row[col++]); + + if (row[col]) + thecore_memcpy(pkTab->skills, row[col], sizeof(pkTab->skills)); + else + memset(&pkTab->skills, 0, sizeof(pkTab->skills)); + + col++; + + if (row[col]) + thecore_memcpy(pkTab->quickslot, row[col], sizeof(pkTab->quickslot)); + else + memset(pkTab->quickslot, 0, sizeof(pkTab->quickslot)); + + col++; + + str_to_number(pkTab->skill_group, row[col++]); + str_to_number(pkTab->lAlignment, row[col++]); + + if (row[col]) + { + strlcpy(pkTab->szMobile, row[col], sizeof(pkTab->szMobile)); + } + + col++; + + str_to_number(pkTab->horse.bLevel, row[col++]); + str_to_number(pkTab->horse.bRiding, row[col++]); + str_to_number(pkTab->horse.sHealth, row[col++]); + str_to_number(pkTab->horse.dwHorseHealthDropTime, row[col++]); + str_to_number(pkTab->horse.sStamina, row[col++]); + str_to_number(pkTab->logoff_interval, row[col++]); + str_to_number(pkTab->horse_skill_point, row[col++]); + + // reset sub_skill_point + { + pkTab->skills[123].bLevel = 0; // SKILL_CREATE + + if (pkTab->level > 9) + { + int max_point = pkTab->level - 9; + + int skill_point = + MIN(20, pkTab->skills[121].bLevel) + // SKILL_LEADERSHIP ַ + MIN(20, pkTab->skills[124].bLevel) + // SKILL_MINING ä + MIN(10, pkTab->skills[131].bLevel) + // SKILL_HORSE_SUMMON ȯ + MIN(20, pkTab->skills[141].bLevel) + // SKILL_ADD_HP HP + MIN(20, pkTab->skills[142].bLevel); // SKILL_RESIST_PENETRATE + + pkTab->sub_skill_point = max_point - skill_point; + } + else + pkTab->sub_skill_point = 0; + } + + return true; +} + +void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID) +{ + CQueryInfo * qi = (CQueryInfo *) pMsg->pvUserData; + std::auto_ptr info((ClientHandleInfo *) qi->pvData); + + MYSQL_RES * pSQLResult = pMsg->Get()->pSQLResult; + if (!pSQLResult) + { + sys_err("null MYSQL_RES QID %u", dwQID); + return; + } + + switch (dwQID) + { + case QID_PLAYER: + sys_log(0, "QID_PLAYER %u %u", info->dwHandle, info->player_id); + RESULT_PLAYER_LOAD(peer, pSQLResult, info.get()); + + break; + + case QID_ITEM: + sys_log(0, "QID_ITEM %u", info->dwHandle); + RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + break; + + case QID_QUEST: + { + sys_log(0, "QID_QUEST %u", info->dwHandle); + RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + //aid + ClientHandleInfo* temp1 = info.get(); + if (temp1 == NULL) + break; + + CLoginData* pLoginData1 = GetLoginDataByAID(temp1->account_id); // + // + if( pLoginData1->GetAccountRef().login == NULL) + break; + if( pLoginData1 == NULL ) + break; + sys_log(0,"info of pLoginData1 before call ItemAwardfunction %d",pLoginData1); + ItemAward(peer,pLoginData1->GetAccountRef().login); + } + break; + + case QID_AFFECT: + sys_log(0, "QID_AFFECT %u", info->dwHandle); + RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle); + break; + /* + case QID_PLAYER_ITEM_QUEST_AFFECT: + sys_log(0, "QID_PLAYER_ITEM_QUEST_AFFECT %u", info->dwHandle); + RESULT_PLAYER_LOAD(peer, pSQLResult, info->dwHandle); + + if (!pMsg->Next()) + { + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: ITEM FAILED"); + return; + } + + case QID_ITEM_QUEST_AFFECT: + sys_log(0, "QID_ITEM_QUEST_AFFECT %u", info->dwHandle); + RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id); + + if (!pMsg->Next()) + { + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: QUEST FAILED"); + return; + } + + case QID_QUEST_AFFECT: + sys_log(0, "QID_QUEST_AFFECT %u", info->dwHandle); + RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle); + + if (!pMsg->Next()) + sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: AFFECT FAILED"); + else + RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle); + + break; + */ + } + +} + +void CClientManager::RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo) +{ + TPlayerTable tab; + + if (!CreatePlayerTableFromRes(pRes, &tab)) + { + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0); + return; + } + + CLoginData * pkLD = GetLoginDataByAID(pkInfo->account_id); + + if (!pkLD || pkLD->IsPlay()) + { + sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0); + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0); + return; + } + + pkLD->SetPlay(true); + SendLoginToBilling(pkLD, true); + thecore_memcpy(tab.aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(tab.aiPremiumTimes)); + + peer->EncodeHeader(HEADER_DG_PLAYER_LOAD_SUCCESS, pkInfo->dwHandle, sizeof(TPlayerTable)); + peer->Encode(&tab, sizeof(TPlayerTable)); + + if (tab.id != pkLD->GetLastPlayerID()) + { + TPacketNeedLoginLogInfo logInfo; + logInfo.dwPlayerID = tab.id; + + pkLD->SetLastPlayerID( tab.id ); + + peer->EncodeHeader( HEADER_DG_NEED_LOGIN_LOG, pkInfo->dwHandle, sizeof(TPacketNeedLoginLogInfo) ); + peer->Encode( &logInfo, sizeof(TPacketNeedLoginLogInfo) ); + } +} + +void CClientManager::RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID) +{ + static std::vector s_items; + //DB о´. + CreateItemTableFromRes(pRes, &s_items, dwPID); + DWORD dwCount = s_items.size(); + + peer->EncodeHeader(HEADER_DG_ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount); + peer->EncodeDWORD(dwCount); + + //CacheSet + CreateItemCacheSet(dwPID); + + // ITEM_LOAD_LOG_ATTACH_PID + sys_log(0, "ITEM_LOAD: count %u pid %u", dwCount, dwPID); + // END_OF_ITEM_LOAD_LOG_ATTACH_PID + + if (dwCount) + { + peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount); + + for (DWORD i = 0; i < dwCount; ++i) + PutItemCache(&s_items[i], true); // ε ʿ Ƿ, bSkipQuery true ִ´. + } +} + +void CClientManager::RESULT_AFFECT_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle) +{ + int iNumRows; + + if ((iNumRows = mysql_num_rows(pRes)) == 0) // + return; + + static std::vector s_elements; + s_elements.resize(iNumRows); + + DWORD dwPID = 0; + + MYSQL_ROW row; + + for (int i = 0; i < iNumRows; ++i) + { + TPacketAffectElement & r = s_elements[i]; + row = mysql_fetch_row(pRes); + + if (dwPID == 0) + str_to_number(dwPID, row[0]); + + str_to_number(r.dwType, row[1]); + str_to_number(r.bApplyOn, row[2]); + str_to_number(r.lApplyValue, row[3]); + str_to_number(r.dwFlag, row[4]); + str_to_number(r.lDuration, row[5]); + str_to_number(r.lSPCost, row[6]); + } + + sys_log(0, "AFFECT_LOAD: count %d PID %u", s_elements.size(), dwPID); + + DWORD dwCount = s_elements.size(); + + peer->EncodeHeader(HEADER_DG_AFFECT_LOAD, dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount); + peer->Encode(&dwPID, sizeof(DWORD)); + peer->Encode(&dwCount, sizeof(DWORD)); + peer->Encode(&s_elements[0], sizeof(TPacketAffectElement) * dwCount); +} + +void CClientManager::RESULT_QUEST_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD pid) +{ + int iNumRows; + + if ((iNumRows = mysql_num_rows(pRes)) == 0) + { + DWORD dwCount = 0; + peer->EncodeHeader(HEADER_DG_QUEST_LOAD, dwHandle, sizeof(DWORD)); + peer->Encode(&dwCount, sizeof(DWORD)); + return; + } + + static std::vector s_table; + s_table.resize(iNumRows); + + MYSQL_ROW row; + + for (int i = 0; i < iNumRows; ++i) + { + TQuestTable & r = s_table[i]; + + row = mysql_fetch_row(pRes); + + str_to_number(r.dwPID, row[0]); + strlcpy(r.szName, row[1], sizeof(r.szName)); + strlcpy(r.szState, row[2], sizeof(r.szState)); + str_to_number(r.lValue, row[3]); + } + + sys_log(0, "QUEST_LOAD: count %d PID %u", s_table.size(), s_table[0].dwPID); + + DWORD dwCount = s_table.size(); + + peer->EncodeHeader(HEADER_DG_QUEST_LOAD, dwHandle, sizeof(DWORD) + sizeof(TQuestTable) * dwCount); + peer->Encode(&dwCount, sizeof(DWORD)); + peer->Encode(&s_table[0], sizeof(TQuestTable) * dwCount); +} + +/* + * PLAYER SAVE + */ +void CClientManager::QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable * pkTab) +{ + if (g_test_server) + sys_log(0, "PLAYER_SAVE: %s", pkTab->name); + + PutPlayerCache(pkTab); +} + +typedef std::map time_by_id_map_t; +static time_by_id_map_t s_createTimeByAccountID; + +/* + * PLAYER CREATE + */ +void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet) +{ + char queryStr[QUERY_MAX_LEN]; + int queryLen; + int player_id; + + // X ij . + time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id); + + if (it != s_createTimeByAccountID.end()) + { + time_t curtime = time(0); + + if (curtime - it->second < 30) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + } + + queryLen = snprintf(queryStr, sizeof(queryStr), + "SELECT pid%u FROM player_index%s WHERE id=%d", packet->account_index + 1, GetTablePostfix(), packet->account_id); + + std::auto_ptr pMsg0(CDBManager::instance().DirectQuery(queryStr)); + + if (pMsg0->Get()->uiNumRows != 0) + { + if (!pMsg0->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg0->Get()->pSQLResult); + + DWORD dwPID = 0; str_to_number(dwPID, row[0]); + if (row[0] && dwPID > 0) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + sys_log(0, "ALREADY EXIST AccountChrIdx %d ID %d", packet->account_index, dwPID); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + if (g_stLocale == "sjis") + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci", + GetTablePostfix(), packet->player_table.name); + else + snprintf(queryStr, sizeof(queryStr), + "SELECT COUNT(*) as count FROM player%s WHERE name='%s'", GetTablePostfix(), packet->player_table.name); + + std::auto_ptr pMsg1(CDBManager::instance().DirectQuery(queryStr)); + + if (pMsg1->Get()->uiNumRows) + { + if (!pMsg1->Get()->pSQLResult) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg1->Get()->pSQLResult); + + if (*row[0] != '0') + { + sys_log(0, "ALREADY EXIST name %s, row[0] %s query %s", packet->player_table.name, row[0], queryStr); + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } + } + else + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + queryLen = snprintf(queryStr, sizeof(queryStr), + "INSERT INTO player%s " + "(id, account_id, name, level, st, ht, dx, iq, " + "job, voice, dir, x, y, z, " + "hp, mp, random_hp, random_sp, stat_point, stamina, part_base, part_main, part_hair, gold, playtime, " + "skill_level, quickslot) " + "VALUES(0, %u, '%s', %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d, %d, 0, %d, 0, ", + GetTablePostfix(), + packet->account_id, packet->player_table.name, packet->player_table.level, packet->player_table.st, packet->player_table.ht, packet->player_table.dx, packet->player_table.iq, + packet->player_table.job, packet->player_table.voice, packet->player_table.dir, packet->player_table.x, packet->player_table.y, packet->player_table.z, + packet->player_table.hp, packet->player_table.sp, packet->player_table.sRandomHP, packet->player_table.sRandomSP, packet->player_table.stat_point, packet->player_table.stamina, packet->player_table.part_base, packet->player_table.part_base, packet->player_table.gold); + + sys_log(0, "PlayerCreate accountid %d name %s level %d gold %d, st %d ht %d job %d", + packet->account_id, + packet->player_table.name, + packet->player_table.level, + packet->player_table.gold, + packet->player_table.st, + packet->player_table.ht, + packet->player_table.job); + + static char text[4096 + 1]; + + CDBManager::instance().EscapeString(text, packet->player_table.skills, sizeof(packet->player_table.skills)); + queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s', ", text); + if (g_test_server) + sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text); + + CDBManager::instance().EscapeString(text, packet->player_table.quickslot, sizeof(packet->player_table.quickslot)); + queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s')", text); + + std::auto_ptr pMsg2(CDBManager::instance().DirectQuery(queryStr)); + if (g_test_server) + sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text); + + if (pMsg2->Get()->uiAffectedRows <= 0) + { + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); + sys_log(0, "ALREADY EXIST3 query: %s AffectedRows %lu", queryStr, pMsg2->Get()->uiAffectedRows); + return; + } + + player_id = pMsg2->Get()->uiInsertID; + + snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%d=%d WHERE id=%d", + GetTablePostfix(), packet->account_index + 1, player_id, packet->account_id); + std::auto_ptr pMsg3(CDBManager::instance().DirectQuery(queryStr)); + + if (pMsg3->Get()->uiAffectedRows <= 0) + { + sys_err("QUERY_ERROR: %s", queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), player_id); + CDBManager::instance().DirectQuery(queryStr); + + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } + + TPacketDGCreateSuccess pack; + memset(&pack, 0, sizeof(pack)); + + pack.bAccountCharacterIndex = packet->account_index; + + pack.player.dwID = player_id; + strlcpy(pack.player.szName, packet->player_table.name, sizeof(pack.player.szName)); + pack.player.byJob = packet->player_table.job; + pack.player.byLevel = 1; + pack.player.dwPlayMinutes = 0; + pack.player.byST = packet->player_table.st; + pack.player.byHT = packet->player_table.ht; + pack.player.byDX = packet->player_table.dx; + pack.player.byIQ = packet->player_table.iq; + pack.player.wMainPart = packet->player_table.part_base; + pack.player.x = packet->player_table.x; + pack.player.y = packet->player_table.y; + + peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_SUCCESS, dwHandle, sizeof(TPacketDGCreateSuccess)); + peer->Encode(&pack, sizeof(TPacketDGCreateSuccess)); + + sys_log(0, "7 name %s job %d", pack.player.szName, pack.player.byJob); + + s_createTimeByAccountID[packet->account_id] = time(0); +} + +/* + * PLAYER DELETE + */ +void CClientManager::__QUERY_PLAYER_DELETE(CPeer* peer, DWORD dwHandle, TPlayerDeletePacket* packet) +{ + if (!packet->login[0] || !packet->player_id || packet->account_index >= PLAYER_PER_ACCOUNT) + return; + + CLoginData * ld = GetLoginDataByLogin(packet->login); + + if (!ld) + { + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + TAccountTable & r = ld->GetAccountRef(); + + // block for japan + if (g_stLocale != "sjis") + { + if (!IsChinaEventServer()) + { + if (strlen(r.social_id) < 7 || strncmp(packet->private_code, r.social_id + strlen(r.social_id) - 7, 7)) + { + sys_log(0, "PLAYER_DELETE FAILED len(%d)", strlen(r.social_id)); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + CPlayerTableCache * pkPlayerCache = GetPlayerCache(packet->player_id); + if (pkPlayerCache) + { + TPlayerTable * pTab = pkPlayerCache->Get(); + + if (pTab->level >= m_iPlayerDeleteLevelLimit) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimit); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + + if (pTab->level < m_iPlayerDeleteLevelLimitLower) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimitLower); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, dwHandle, 1); + peer->EncodeBYTE(packet->account_index); + return; + } + } + } + } + + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "SELECT p.id, p.level, p.name FROM player_index%s AS i, player%s AS p WHERE pid%u=%u AND pid%u=p.id", + GetTablePostfix(), GetTablePostfix(), packet->account_index + 1, packet->player_id, packet->account_index + 1); + + ClientHandleInfo * pi = new ClientHandleInfo(dwHandle, packet->player_id); + pi->account_index = packet->account_index; + + sys_log(0, "PLAYER_DELETE TRY: %s %d pid%d", packet->login, packet->player_id, packet->account_index + 1); + CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_DELETE, peer->GetHandle(), pi); +} + +// +// @version 05/06/10 Bang2ni - ÷̾ Ʈ ߰. +// +void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; + + if (msg->Get() && msg->Get()->uiNumRows) + { + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + DWORD dwPID = 0; + str_to_number(dwPID, row[0]); + + int deletedLevelLimit = 0; + str_to_number(deletedLevelLimit, row[1]); + + char szName[64]; + strlcpy(szName, row[2], sizeof(szName)); + + if (deletedLevelLimit >= m_iPlayerDeleteLevelLimit && !IsChinaEventServer()) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimit); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + if (deletedLevelLimit < m_iPlayerDeleteLevelLimitLower) + { + sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimitLower); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + char queryStr[QUERY_MAX_LEN]; + + snprintf(queryStr, sizeof(queryStr), "INSERT INTO player%s_deleted SELECT * FROM player%s WHERE id=%d", + GetTablePostfix(), GetTablePostfix(), pi->player_id); + std::auto_ptr pIns(CDBManager::instance().DirectQuery(queryStr)); + + if (pIns->Get()->uiAffectedRows == 0 || pIns->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "PLAYER_DELETE FAILED %u CANNOT INSERT TO player%s_deleted", dwPID, GetTablePostfix()); + + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + // + sys_log(0, "PLAYER_DELETE SUCCESS %u", dwPID); + + char account_index_string[16]; + + snprintf(account_index_string, sizeof(account_index_string), "player_id%d", m_iPlayerIDStart + pi->account_index); + + // ÷̾ ̺ ij Ѵ. + CPlayerTableCache * pkPlayerCache = GetPlayerCache(pi->player_id); + + if (pkPlayerCache) + { + m_map_playerCache.erase(pi->player_id); + delete pkPlayerCache; + } + + // ۵ ij Ѵ. + TItemCacheSet * pSet = GetItemCacheSet(pi->player_id); + + if (pSet) + { + TItemCacheSet::iterator it = pSet->begin(); + + while (it != pSet->end()) + { + CItemCache * pkItemCache = *it++; + DeleteItemCache(pkItemCache->Get()->id); + } + + pSet->clear(); + delete pSet; + + m_map_pkItemCacheSetPtr.erase(pi->player_id); + } + + snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%u=0 WHERE pid%u=%d", + GetTablePostfix(), + pi->account_index + 1, + pi->account_index + 1, + pi->player_id); + + std::auto_ptr pMsg(CDBManager::instance().DirectQuery(queryStr)); + + if (pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "PLAYER_DELETE FAIL WHEN UPDATE account table"); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + return; + } + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), pi->player_id); + delete CDBManager::instance().DirectQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM item%s WHERE owner_id=%d AND (window < %d or window = %d)", GetTablePostfix(), pi->player_id, SAFEBOX, DRAGON_SOUL_INVENTORY); + delete CDBManager::instance().DirectQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM quest%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM affect%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM guild_member%s WHERE pid=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + + // MYSHOP_PRICE_LIST + snprintf(queryStr, sizeof(queryStr), "DELETE FROM myshop_pricelist%s WHERE owner_id=%d", GetTablePostfix(), pi->player_id); + CDBManager::instance().AsyncQuery(queryStr); + // END_OF_MYSHOP_PRICE_LIST + + snprintf(queryStr, sizeof(queryStr), "DELETE FROM messenger_list%s WHERE account='%s' OR companion='%s'", GetTablePostfix(), szName, szName); + CDBManager::instance().AsyncQuery(queryStr); + + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_SUCCESS, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + } + else + { + // + sys_log(0, "PLAYER_DELETE FAIL NO ROW"); + peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1); + peer->EncodeBYTE(pi->account_index); + } +} + +void CClientManager::QUERY_ADD_AFFECT(CPeer * peer, TPacketGDAddAffect * p) +{ + char queryStr[QUERY_MAX_LEN]; + /* + snprintf(queryStr, sizeof(queryStr), + "INSERT INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) " + "VALUES(%u, %u, %u, %d, %u, %d, %d) " + "ON DUPLICATE KEY UPDATE lApplyValue=%d, dwFlag=%u, lDuration=%d, lSPCost=%d", + GetTablePostfix(), + p->dwPID, + p->elem.dwType, + p->elem.bApplyOn, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost); + */ + snprintf(queryStr, sizeof(queryStr), + "REPLACE INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) " + "VALUES(%u, %u, %u, %ld, %u, %ld, %ld)", + GetTablePostfix(), + p->dwPID, + p->elem.dwType, + p->elem.bApplyOn, + p->elem.lApplyValue, + p->elem.dwFlag, + p->elem.lDuration, + p->elem.lSPCost); + + CDBManager::instance().AsyncQuery(queryStr); +} + +void CClientManager::QUERY_REMOVE_AFFECT(CPeer * peer, TPacketGDRemoveAffect * p) +{ + char queryStr[QUERY_MAX_LEN]; + + snprintf(queryStr, sizeof(queryStr), + "DELETE FROM affect%s WHERE dwPID=%u AND bType=%u AND bApplyOn=%u", + GetTablePostfix(), p->dwPID, p->dwType, p->bApplyOn); + + CDBManager::instance().AsyncQuery(queryStr); +} + + +void CClientManager::QUERY_HIGHSCORE_REGISTER(CPeer* peer, TPacketGDHighscore * data) +{ + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "SELECT value FROM highscore%s WHERE board='%s' AND pid = %u", GetTablePostfix(), data->szBoard, data->dwPID); + + sys_log(0, "HEADER_GD_HIGHSCORE_REGISTER: PID %u", data->dwPID); + + ClientHandleInfo * pi = new ClientHandleInfo(0); + strlcpy(pi->login, data->szBoard, sizeof(pi->login)); + pi->account_id = (DWORD)data->lValue; + pi->player_id = data->dwPID; + pi->account_index = (data->cDir > 0); + + CDBManager::instance().ReturnQuery(szQuery, QID_HIGHSCORE_REGISTER, peer->GetHandle(), pi); +} + +void CClientManager::RESULT_HIGHSCORE_REGISTER(CPeer * pkPeer, SQLMsg * msg) +{ + CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; + ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; + //DWORD dwHandle = pi->dwHandle; + + char szBoard[21]; + strlcpy(szBoard, pi->login, sizeof(szBoard)); + int value = (int)pi->account_id; + + SQLResult * res = msg->Get(); + + if (res->uiNumRows == 0) + { + // ο ̽ھ + char buf[256]; + snprintf(buf, sizeof(buf), "INSERT INTO highscore%s VALUES('%s', %u, %d)", GetTablePostfix(), szBoard, pi->player_id, value); + CDBManager::instance().AsyncQuery(buf); + } + else + { + if (!res->pSQLResult) + { + delete pi; + return; + } + + MYSQL_ROW row = mysql_fetch_row(res->pSQLResult); + if (row && row[0]) + { + int current_value = 0; str_to_number(current_value, row[0]); + if (pi->account_index && current_value >= value || !pi->account_index && current_value <= value) + { + value = current_value; + } + else + { + char buf[256]; + snprintf(buf, sizeof(buf), "REPLACE INTO highscore%s VALUES('%s', %u, %d)", GetTablePostfix(), szBoard, pi->player_id, value); + CDBManager::instance().AsyncQuery(buf); + } + } + else + { + char buf[256]; + snprintf(buf, sizeof(buf), "INSERT INTO highscore%s VALUES('%s', %u, %d)", GetTablePostfix(), szBoard, pi->player_id, value); + CDBManager::instance().AsyncQuery(buf); + } + } + // TODO: ̰ ̽ھ Ʈ Ǿ üũϿ ѷѴ. + delete pi; +} + +void CClientManager::InsertLogoutPlayer(DWORD pid) +{ + TLogoutPlayerMap::iterator it = m_map_logout.find(pid); + + // ߰ + if (it != m_map_logout.end()) + { + // Ұ ð + if (g_log) + sys_log(0, "LOGOUT: Update player time pid(%d)", pid); + + it->second->time = time(0); + return; + } + + TLogoutPlayer * pLogout = new TLogoutPlayer; + pLogout->pid = pid; + pLogout->time = time(0); + m_map_logout.insert(std::make_pair(pid, pLogout)); + + if (g_log) + sys_log(0, "LOGOUT: Insert player pid(%d)", pid); +} + +void CClientManager::DeleteLogoutPlayer(DWORD pid) +{ + TLogoutPlayerMap::iterator it = m_map_logout.find(pid); + + if (it != m_map_logout.end()) + { + delete it->second; + m_map_logout.erase(it); + } +} + +extern int g_iLogoutSeconds; + +void CClientManager::UpdateLogoutPlayer() +{ + time_t now = time(0); + + TLogoutPlayerMap::iterator it = m_map_logout.begin(); + + while (it != m_map_logout.end()) + { + TLogoutPlayer* pLogout = it->second; + + if (now - g_iLogoutSeconds > pLogout->time) + { + FlushItemCacheSet(pLogout->pid); + FlushPlayerCacheSet(pLogout->pid); + + delete pLogout; + m_map_logout.erase(it++); + } + else + ++it; + } +} + +void CClientManager::FlushPlayerCacheSet(DWORD pid) +{ + TPlayerTableCacheMap::iterator it = m_map_playerCache.find(pid); + + if (it != m_map_playerCache.end()) + { + CPlayerTableCache * c = it->second; + m_map_playerCache.erase(it); + + c->Flush(); + delete c; + } +} + diff --git a/db/src/Config.cpp b/db/src/Config.cpp new file mode 100644 index 0000000..d35633b --- /dev/null +++ b/db/src/Config.cpp @@ -0,0 +1,250 @@ +#include "stdafx.h" +#include "Config.h" + +CConfig::CConfig() +{ +} + +CConfig::~CConfig() +{ + Destroy(); +} + +void CConfig::Destroy() +{ + m_valueMap.clear(); +} + +void CConfig::NextLine(FILE *fp) +{ + int c; + + while ((c = fgetc(fp)) != EOF) + { + if (c == '\n') + return; + } +} + +bool CConfig::GetWord(FILE *fp, char *tar) +{ + int i = 0; + int c; + + int semicolon_mode = 0; + + while ((c = fgetc(fp)) != EOF) + { + if (c == 13) + continue; + + if (semicolon_mode) + { + if (c == '"') + { + tar[i] = '\0'; + return true; + } + + tar[i++] = c; + continue; + } + else + { + if (i == 0) + { + if (c == '"') + { + semicolon_mode = 1; + continue; + } + + if (c == ' ' || c == '\t' || c == '\n') + { + continue; + } + } + + if ((c == ' ' || c == '\t' || c == '\n')) + { + // . + tar[i] = '\0'; + return true; + } + + tar[i] = c; + ++i; + } + } + + return (i != 0); +} + +bool CConfig::GetLine(FILE* fp, char*dest) +{ + int c; + int i = 0; + while ((c = fgetc(fp)) != EOF) + { + if (c == '\n') + return true; + dest[i] = c; + ++i; + } + return true; +} + +bool CConfig::LoadFile(const char* filename) +{ + char szTmp[256]; + char comment[256]; + + FILE * fp = fopen(filename, "rb"); + + if (fp == NULL) + return false; + + int mode = 0; + + while (GetWord(fp, szTmp)) + { + if (strcmp(szTmp, "//") == 0) + { + NextLine(fp); + continue; + } + + switch (mode) + { + case 0: + strlcpy(comment, szTmp, sizeof(comment)); + ++mode; + break; + + case 1: + if (*szTmp == '=') + ++mode; + break; + + case 2: + mode = 0; + m_valueMap.insert(TValueMap::value_type(comment, szTmp)); + break; + } + + // ITEM_ID_RANGE + if (mode == 2 && strcmp(comment, "ITEM_ID_RANGE") == 0) + { + GetLine(fp, szTmp); + m_valueMap.insert(TValueMap::value_type(comment, szTmp)); + mode = 0; + + } + // ITEM_ID_RANGE_END + } + + + // ݴ κ. + fclose(fp); + return true; +} + +std::string * CConfig::Search(const char* key) +{ + itertype(m_valueMap) i = m_valueMap.find(key); + + if (i == m_valueMap.end()) + return NULL; + else + return (&i->second); +} + +bool CConfig::GetParam(const char*key, int index, DWORD *Param) +{ + std::string * pstStr = Search(key); + if (!pstStr) + return false; + + char szParam[5][32]; + + sscanf(pstStr->c_str(), "%s %s %s %s %s", szParam[0],szParam[1],szParam[2],szParam[3],szParam[4]); + + str_to_number(*Param, szParam[index]); + + sys_log(0, "GetParam %d", *Param); + return true; +} +const char * CConfig::Get(const char* key) +{ + std::string * pstStr = Search(key); + + if (!pstStr) + { + static std::string stTemp = ""; + return stTemp.c_str(); + } + + return pstStr->c_str(); +} + + +bool CConfig::GetValue(const char * key, int* dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, float *dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, DWORD *dest) +{ + if (!Search(key)) + return false; + + str_to_number(*dest, Get(key)); + return true; +} + +bool CConfig::GetValue(const char * key, BYTE *dest) +{ + if (!Search(key)) + return false; + + *dest = *(BYTE *) Get(key); + return true; +} + +bool CConfig::GetValue(const char * key, char *dest, size_t destSize) +{ + if (!Search(key)) + return false; + + strlcpy(dest, Get(key), destSize); + + if (!*dest) + return false; + + return true; +} + +bool CConfig::GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2) +{ + if (!GetParam(key, 0, dest1)) + return false; + + if (!GetParam(key, 1, dest2)) + return false; + + return true; +} + diff --git a/db/src/Config.h b/db/src/Config.h new file mode 100644 index 0000000..4508832 --- /dev/null +++ b/db/src/Config.h @@ -0,0 +1,34 @@ +#ifndef __INC_CONFIG_H__ +#define __INC_CONFIG_H__ + +typedef std::map TValueMap; + +class CConfig : public singleton +{ + public: + CConfig(); + ~CConfig(); + + bool LoadFile(const char* filename); + bool GetValue(const char* key, int* dest); + bool GetValue(const char* key, float* dest); + bool GetValue(const char* key, DWORD* dest); + bool GetValue(const char* key, BYTE* dest); + bool GetValue(const char* key, char* dest, size_t destSize); + bool GetWord(FILE* fp, char* dest); + bool GetLine(FILE* fp, char* dest); + bool GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2); + void NextLine(FILE* fp); + + private: + void Destroy(); + bool GetParam(const char*key,int index, DWORD *Param); + + const char * Get(const char* key); + std::string * Search(const char* key); + + private: + TValueMap m_valueMap; +}; + +#endif diff --git a/db/src/CsvReader.cpp b/db/src/CsvReader.cpp new file mode 100644 index 0000000..6087c81 --- /dev/null +++ b/db/src/CsvReader.cpp @@ -0,0 +1,430 @@ +#include "stdafx.h" +#include "CsvReader.h" +#include +#include + +#ifndef Assert + #include + #define Assert assert + #define LogToFile (void)(0); +#endif + +namespace +{ + /// Ľ̿ state Ű + enum ParseState + { + STATE_NORMAL = 0, ///< Ϲ + STATE_QUOTE ///< ǥ + }; + + /// ڿ ¿ ؼ ȯѴ. + std::string Trim(std::string str) + { + str = str.erase(str.find_last_not_of(" \t\r\n") + 1); + str = str.erase(0, str.find_first_not_of(" \t\r\n")); + return str; + } + + /// \brief ־ 忡 ִ ĺ ҹڷ ٲ۴. + std::string Lower(std::string original) + { + std::transform(original.begin(), original.end(), original.begin(), tolower); + return original; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ׼ , ̸ Ѵ. +/// \param name ̸ +/// \param index ε +//////////////////////////////////////////////////////////////////////////////// +void cCsvAlias::AddAlias(const char* name, size_t index) +{ + std::string converted(Lower(name)); + + Assert(m_Name2Index.find(converted) == m_Name2Index.end()); + Assert(m_Index2Name.find(index) == m_Index2Name.end()); + + m_Name2Index.insert(NAME2INDEX_MAP::value_type(converted, index)); + m_Index2Name.insert(INDEX2NAME_MAP::value_type(index, name)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ͸ Ѵ. +//////////////////////////////////////////////////////////////////////////////// +void cCsvAlias::Destroy() +{ + m_Name2Index.clear(); + m_Index2Name.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ε ̸ ȯѴ. +/// \param index ε +/// \return const char* ̸ +//////////////////////////////////////////////////////////////////////////////// +const char* cCsvAlias::operator [] (size_t index) const +{ + INDEX2NAME_MAP::const_iterator itr(m_Index2Name.find(index)); + if (itr == m_Index2Name.end()) + { + LogToFile(NULL, "cannot find suitable conversion for %d", index); + Assert(false && "cannot find suitable conversion"); + return NULL; + } + + return itr->second.c_str(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ̸ ε ȯѴ. +/// \param name ̸ +/// \return size_t ε +//////////////////////////////////////////////////////////////////////////////// +size_t cCsvAlias::operator [] (const char* name) const +{ + NAME2INDEX_MAP::const_iterator itr(m_Name2Index.find(Lower(name))); + if (itr == m_Name2Index.end()) + { + LogToFile(NULL, "cannot find suitable conversion for %s", name); + Assert(false && "cannot find suitable conversion"); + return 0; + } + + return itr->second; +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ̸ CSV εѴ. +/// \param fileName CSV ̸ +/// \param seperator ʵ иڷ . ⺻ ','̴. +/// \param quote ǥ . ⺻ '"'̴. +/// \return bool εߴٸ true, ƴ϶ false +//////////////////////////////////////////////////////////////////////////////// +bool cCsvFile::Load(const char* fileName, const char seperator, const char quote) +{ + Assert(seperator != quote); + + std::ifstream file(fileName, std::ios::in); + if (!file) return false; + + Destroy(); // ͸ + + cCsvRow* row = NULL; + ParseState state = STATE_NORMAL; + std::string token = ""; + char buf[2048+1] = {0,}; + + while (file.good()) + { + file.getline(buf, 2048); + buf[sizeof(buf)-1] = 0; + + std::string line(Trim(buf)); + if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue; + + std::string text = std::string(line) + " "; // Ľ lookahead ٿش. + size_t cur = 0; + + while (cur < text.size()) + { + // 尡 QUOTE , + if (state == STATE_QUOTE) + { + // '"' ̴. + // 1. ο Ư ڰ ̸ ˸ ¿ + // 2. '"' ڰ '"' 2 ġȯ + // ù° ִ CSV ̶, + // STATE_NORMAL ɸ Ǿִ. + // ׷Ƿ ⼭ ɸ 1 쳪, 2 ̴. + // 2 쿡 '"' ڰ 2 Ÿ. 1 + // 쿡 ƴϴ. ̸ ؼ ڵ带 ¥... + if (text[cur] == quote) + { + // ڰ '"' ڶ, ӵ '"' ڶ + // ̴ '"' ڰ ġȯ ̴. + if (text[cur+1] == quote) + { + token += quote; + ++cur; + } + // ڰ '"' ڰ ƴ϶ + // '"'ڴ ˸ ڶ ִ. + else + { + state = STATE_NORMAL; + } + } + else + { + token += text[cur]; + } + } + // 尡 NORMAL , + else if (state == STATE_NORMAL) + { + if (row == NULL) + row = new cCsvRow(); + + // ',' ڸ ٸ ǹѴ. + // ūμ Ʈٰ ְ, ū ʱȭѴ. + if (text[cur] == seperator) + { + row->push_back(token); + token.clear(); + } + // '"' ڸ ٸ, QUOTE ȯѴ. + else if (text[cur] == quote) + { + state = STATE_QUOTE; + } + // ٸ Ϲ ڶ ūٰ δ. + else + { + token += text[cur]; + } + } + + ++cur; + } + + // ',' ڰ ⼭ ߰Ѵ. + // , ó Ľ lookahead ̽ . + if (state == STATE_NORMAL) + { + Assert(row != NULL); + row->push_back(token.substr(0, token.size()-2)); + m_Rows.push_back(row); + token.clear(); + row = NULL; + } + else + { + token = token.substr(0, token.size()-2) + "\r\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ִ CSV Ͽ Ѵ. +/// \param fileName CSV ̸ +/// \param append true , Ͽ δ. false 쿡 +/// ϰ, . +/// \param seperator ʵ иڷ . ⺻ ','̴. +/// \param quote ǥ . ⺻ '"'̴. +/// \return bool ߴٸ true, 쿡 false +//////////////////////////////////////////////////////////////////////////////// +bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quote) const +{ + Assert(seperator != quote); + + // 忡 ÷׷ Ѵ. + std::ofstream file; + if (append) { file.open(fileName, std::ios::out | std::ios::app); } + else { file.open(fileName, std::ios::out | std::ios::trunc); } + + // ߴٸ, false Ѵ. + if (!file) return false; + + char special_chars[5] = { seperator, quote, '\r', '\n', 0 }; + char quote_escape_string[3] = { quote, quote, 0 }; + + // Ⱦϸ鼭... + for (size_t i=0; isize(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ε ̿ int ȯѴ. +/// \param index ε +/// \return int +//////////////////////////////////////////////////////////////////////////////// +int cCsvTable::AsInt(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + Assert(index < row->size()); + return row->AsInt(index); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ε ̿ double ȯѴ. +/// \param index ε +/// \return double +//////////////////////////////////////////////////////////////////////////////// +double cCsvTable::AsDouble(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + Assert(index < row->size()); + return row->AsDouble(index); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ε ̿ std::string ȯѴ. +/// \param index ε +/// \return const char* +//////////////////////////////////////////////////////////////////////////////// +const char* cCsvTable::AsStringByIndex(size_t index) const +{ + const cCsvRow* const row = CurRow(); + Assert(row); + Assert(index < row->size()); + return row->AsString(index); +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief alias ͸ Ѵ. +//////////////////////////////////////////////////////////////////////////////// +void cCsvTable::Destroy() +{ + m_File.Destroy(); + m_Alias.Destroy(); + m_CurRow = -1; +} + +//////////////////////////////////////////////////////////////////////////////// +/// \brief ȯѴ. +/// \return const cCsvRow* ׼ ϴ 쿡 +/// ͸ ȯϰ, ̻ ׼ 쿡 NULL +/// ȯѴ. +//////////////////////////////////////////////////////////////////////////////// +const cCsvRow* const cCsvTable::CurRow() const +{ + if (m_CurRow < 0) + { + Assert(false && "call Next() first!"); + return NULL; + } + else if (m_CurRow >= (int)m_File.GetRowCount()) + { + Assert(false && "no more rows!"); + return NULL; + } + + return m_File[m_CurRow]; +} + diff --git a/db/src/CsvReader.h b/db/src/CsvReader.h new file mode 100644 index 0000000..e8f0919 --- /dev/null +++ b/db/src/CsvReader.h @@ -0,0 +1,322 @@ +#ifndef __CSVFILE_H__ +#define __CSVFILE_H__ + +#include +#include + +#if _MSC_VER + #include +#else + #include +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvAlias +/// \brief CSV ߻ϴ ε ̱ +/// ü. +/// +/// 0 ÷ A ϰ, 1 ÷ B +/// ϰ ־µ... +/// +///
+/// int a = row.AsInt(0);
+/// int b = row.AsInt(1);
+/// 
+/// +/// ̿ C ϴ ÷ , ϵڵǾ ִ +/// 1 ãƼ ľ ϴµ, ߻ϱ ۾̴. +/// +///
+/// int a = row.AsInt(0);
+/// int c = row.AsInt(1);
+/// int b = row.AsInt(2); <--  κ  Ű Ѵ.
+/// 
+/// +/// κ ڿ óϸ  ణ̳ +/// ִ. +//////////////////////////////////////////////////////////////////////////////// + +class cCsvAlias +{ +private: +#if _MSC_VER + typedef stdext::hash_map NAME2INDEX_MAP; + typedef stdext::hash_map INDEX2NAME_MAP; +#else + typedef std::map NAME2INDEX_MAP; + typedef std::map INDEX2NAME_MAP; +#endif + + NAME2INDEX_MAP m_Name2Index; ///< ε ϱ ̸ + INDEX2NAME_MAP m_Index2Name; ///< ߸ alias ˻ϱ ߰ + + +public: + /// \brief + cCsvAlias() {} + + /// \brief Ҹ + virtual ~cCsvAlias() {} + + +public: + /// \brief ׼ , ̸ Ѵ. + void AddAlias(const char* name, size_t index); + + /// \brief ͸ Ѵ. + void Destroy(); + + /// \brief ε ̸ ȯѴ. + const char* operator [] (size_t index) const; + + /// \brief ̸ ε ȯѴ. + size_t operator [] (const char* name) const; + + +private: + /// \brief + cCsvAlias(const cCsvAlias&) {} + + /// \brief + const cCsvAlias& operator = (const cCsvAlias&) { return *this; } +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvRow +/// \brief CSV ĸȭ Ŭ +/// +/// CSV ⺻ ̴ ϳ ',' ڷ ̴. +/// , ȿ Ư ڷ ̴ ',' ڳ '"' ڰ  , +/// ణ ̻ϰ Ѵ. ȭ ̴. +/// +///
+///  ̴  |  CSV Ͽ ִ 
+/// ---------------------+----------------------------------------------------
+/// ItemPrice            | ItemPrice
+/// Item,Price           | "Item,Price"
+/// Item"Price           | "Item""Price"
+/// "ItemPrice"          | """ItemPrice"""
+/// "Item,Price"         | """Item,Price"""
+/// Item",Price          | "Item"",Price"
+/// 
+/// +/// μ ִ. +/// - ο ',' Ǵ '"' ڰ  , ¿쿡 '"' ڰ . +/// - '"' ڴ 2 ġȯȴ. +/// +/// \sa cCsvFile +//////////////////////////////////////////////////////////////////////////////// + +class cCsvRow : public std::vector +{ +public: + /// \brief ⺻ + cCsvRow() {} + + /// \brief Ҹ + ~cCsvRow() {} + + +public: + /// \brief ش ͸ int ȯѴ. + int AsInt(size_t index) const { return atoi(at(index).c_str()); } + + /// \brief ش ͸ double ȯѴ. + double AsDouble(size_t index) const { return atof(at(index).c_str()); } + + /// \brief ش ͸ ڿ ȯѴ. + const char* AsString(size_t index) const { return at(index).c_str(); } + + /// \brief شϴ ̸ ͸ int ȯѴ. + int AsInt(const char* name, const cCsvAlias& alias) const { + return atoi( at(alias[name]).c_str() ); + } + + /// \brief شϴ ̸ ͸ int ȯѴ. + double AsDouble(const char* name, const cCsvAlias& alias) const { + return atof( at(alias[name]).c_str() ); + } + + /// \brief شϴ ̸ ͸ ڿ ȯѴ. + const char* AsString(const char* name, const cCsvAlias& alias) const { + return at(alias[name]).c_str(); + } + + +private: + /// \brief + cCsvRow(const cCsvRow&) {} + + /// \brief + const cCsvRow& operator = (const cCsvRow&) { return *this; } +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvFile +/// \brief CSV(Comma Seperated Values) read/writeϱ Ŭ +/// +/// sample +///
+/// cCsvFile file;
+///
+/// cCsvRow row1, row2, row3;
+/// row1.push_back("ItemPrice");
+/// row1.push_back("Item,Price");
+/// row1.push_back("Item\"Price");
+///
+/// row2.reserve(3);
+/// row2[0] = "\"ItemPrice\"";
+/// row2[1] = "\"Item,Price\"";
+/// row2[2] = "Item\",Price\"";
+///
+/// row3 = "\"ItemPrice\"\"Item,Price\"Item\",Price\"";
+///
+/// file.add(row1);
+/// file.add(row2);
+/// file.add(row3);
+/// file.save("test.csv", false);
+/// 
+/// +/// \todo Ͽ о ƴ϶, ޸ ҽκ д Լ +/// ־ ϴ. +//////////////////////////////////////////////////////////////////////////////// + +class cCsvFile +{ +private: + typedef std::vector ROWS; + + ROWS m_Rows; ///< ÷ + + +public: + /// \brief + cCsvFile() {} + + /// \brief Ҹ + virtual ~cCsvFile() { Destroy(); } + + +public: + /// \brief ̸ CSV εѴ. + bool Load(const char* fileName, const char seperator=',', const char quote='"'); + + /// \brief ִ CSV Ͽ Ѵ. + bool Save(const char* fileName, bool append=false, char seperator=',', char quote='"') const; + + /// \brief ͸ ޸𸮿 Ѵ. + void Destroy(); + + /// \brief شϴ ε ȯѴ. + cCsvRow* operator [] (size_t index); + + /// \brief شϴ ε ȯѴ. + const cCsvRow* operator [] (size_t index) const; + + /// \brief ȯѴ. + size_t GetRowCount() const { return m_Rows.size(); } + + +private: + /// \brief + cCsvFile(const cCsvFile&) {} + + /// \brief + const cCsvFile& operator = (const cCsvFile&) { return *this; } +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// \class cCsvTable +/// \brief CSV ̿ ̺ ͸ εϴ 찡 , Ŭ +/// ۾ ϱ ƿƼ Ŭ. +/// +/// CSV εϴ , ڸ ̿ ׼ؾ ϴµ, CSV +/// ٲ , ڵ Ѵ. ۾ +/// Ű 䱸ϴ ٰ, ߻ϱ . ׷Ƿ ڷ +/// ׼ϱ⺸ٴ ڿ ׼ϴ ణ ٰ ִ. +/// +/// sample +///
+/// cCsvTable table;
+///
+/// table.alias(0, "ItemClass");
+/// table.alias(1, "ItemType");
+///
+/// if (table.load("test.csv"))
+/// {
+///     while (table.next())
+///     {
+///         std::string item_class = table.AsString("ItemClass");
+///         int         item_type  = table.AsInt("ItemType"); 
+///     }
+/// }
+/// 
+//////////////////////////////////////////////////////////////////////////////// + +class cCsvTable +{ +public : + cCsvFile m_File; ///< CSV ü +private: + cCsvAlias m_Alias; ///< ڿ ε ȯϱ ü + int m_CurRow; ///< Ⱦ ȣ + + +public: + /// \brief + cCsvTable(); + + /// \brief Ҹ + virtual ~cCsvTable(); + + +public: + /// \brief ̸ CSV εѴ. + bool Load(const char* fileName, const char seperator=',', const char quote='"'); + + /// \brief ׼ , ̸ Ѵ. + void AddAlias(const char* name, size_t index) { m_Alias.AddAlias(name, index); } + + /// \brief Ѿ. + bool Next(); + + /// \brief ڸ ȯѴ. + size_t ColCount() const; + + /// \brief ε ̿ int ȯѴ. + int AsInt(size_t index) const; + + /// \brief ε ̿ double ȯѴ. + double AsDouble(size_t index) const; + + /// \brief ε ̿ std::string ȯѴ. + const char* AsStringByIndex(size_t index) const; + + /// \brief ̸ ̿ int ȯѴ. + int AsInt(const char* name) const { return AsInt(m_Alias[name]); } + + /// \brief ̸ ̿ double ȯѴ. + double AsDouble(const char* name) const { return AsDouble(m_Alias[name]); } + + /// \brief ̸ ̿ std::string ȯѴ. + const char* AsString(const char* name) const { return AsStringByIndex(m_Alias[name]); } + + /// \brief alias ͸ Ѵ. + void Destroy(); + + +private: + /// \brief ȯѴ. + const cCsvRow* const CurRow() const; + + /// \brief + cCsvTable(const cCsvTable&) {} + + /// \brief + const cCsvTable& operator = (const cCsvTable&) { return *this; } +}; + +#endif //__CSVFILE_H__ diff --git a/db/src/DBManager.cpp b/db/src/DBManager.cpp new file mode 100644 index 0000000..c3058c7 --- /dev/null +++ b/db/src/DBManager.cpp @@ -0,0 +1,188 @@ +#include "stdafx.h" +#include "DBManager.h" +#include "ClientManager.h" + +extern std::string g_stLocale; + +CDBManager::CDBManager() +{ + Initialize(); +} + +CDBManager::~CDBManager() +{ + Destroy(); +} + +void CDBManager::Initialize() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + m_mainSQL[i] = NULL; + m_directSQL[i] = NULL; + m_asyncSQL[i] = NULL; + } +} + +void CDBManager::Destroy() +{ + Clear(); +} + +void CDBManager::Clear() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + if (m_mainSQL[i]) + { + delete m_mainSQL[i]; + m_mainSQL[i] = NULL; + } + + if (m_directSQL[i]) + { + delete m_directSQL[i]; + m_directSQL[i] = NULL; + } + + if (m_asyncSQL[i]) + { + delete m_asyncSQL[i]; + m_asyncSQL[i] = NULL; + } + } + + Initialize(); +} + +void CDBManager::Quit() +{ + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + if (m_mainSQL[i]) + m_mainSQL[i]->Quit(); + + if (m_asyncSQL[i]) + m_asyncSQL[i]->Quit(); + + if (m_directSQL[i]) + m_directSQL[i]->Quit(); + } +} + +SQLMsg * CDBManager::PopResult() +{ + SQLMsg * p; + + for (int i = 0; i < SQL_MAX_NUM; ++i) + if (m_mainSQL[i] && m_mainSQL[i]->PopResult(&p)) + return p; + + return NULL; +} + +SQLMsg * CDBManager::PopResult(eSQL_SLOT slot) +{ + SQLMsg * p; + + if (m_mainSQL[slot] && m_mainSQL[slot]->PopResult(&p)) + return p; + + return NULL; +} +int CDBManager::Connect(int iSlot, const char * db_address, const int db_port, const char * db_name, const char * user, const char * pwd) +{ + if (db_address == NULL || db_name == NULL) + return false; + + if (iSlot < 0 || iSlot >= SQL_MAX_NUM) + return false; + + sys_log(0, "CREATING DIRECT_SQL"); + m_directSQL[iSlot] = new CAsyncSQL2; + if (!m_directSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), true, db_port)) + { + Clear(); + return false; + } + + + sys_log(0, "CREATING MAIN_SQL"); + m_mainSQL[iSlot] = new CAsyncSQL2; + if (!m_mainSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port)) + { + Clear(); + return false; + } + + sys_log(0, "CREATING ASYNC_SQL"); + m_asyncSQL[iSlot] = new CAsyncSQL2; + if (!m_asyncSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port)) + { + Clear(); + return false; + } + + return true; +} + +SQLMsg * CDBManager::DirectQuery(const char * c_pszQuery, int iSlot) +{ + return m_directSQL[iSlot]->DirectQuery(c_pszQuery); +} + +extern CPacketInfo g_query_info; +extern int g_query_count[2]; + +void CDBManager::ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * udata, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + //sys_log(0, "ReturnQuery %s", c_pszQuery); + CQueryInfo * p = new CQueryInfo; + + p->iType = iType; + p->dwIdent = dwIdent; + p->pvData = udata; + + m_mainSQL[iSlot]->ReturnQuery(c_pszQuery, p); + + //g_query_info.Add(iType); + ++g_query_count[0]; +} + +void CDBManager::AsyncQuery(const char * c_pszQuery, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + m_asyncSQL[iSlot]->AsyncQuery(c_pszQuery); + ++g_query_count[1]; +} + +unsigned long CDBManager::EscapeString(void *to, const void *from, unsigned long length, int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + return mysql_real_escape_string(m_directSQL[iSlot]->GetSQLHandle(), (char *) to, (const char *) from, length); +} + +void CDBManager::SetLocale(const char * szLocale) +{ + const std::string stLocale(szLocale); + sys_log(0, "SetLocale start" ); + for (int n = 0; n < SQL_MAX_NUM; ++n) + { + m_mainSQL[n]->SetLocale(stLocale); + m_directSQL[n]->SetLocale(stLocale); + m_asyncSQL[n]->SetLocale(stLocale); + } + sys_log(0, "End setlocale %s", szLocale); +} + +void CDBManager::QueryLocaleSet() +{ + for (int n = 0; n < SQL_MAX_NUM; ++n) + { + m_mainSQL[n]->QueryLocaleSet(); + m_directSQL[n]->QueryLocaleSet(); + m_asyncSQL[n]->QueryLocaleSet(); + } +} + diff --git a/db/src/DBManager.h b/db/src/DBManager.h new file mode 100644 index 0000000..b7b544d --- /dev/null +++ b/db/src/DBManager.h @@ -0,0 +1,100 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN2_DB_DBMANAGER_H__ +#define __INC_METIN2_DB_DBMANAGER_H__ + +// Ŀؼ Ŭ ... ؼ ޾ƿ +// ϵ óѴ. +// ڵ by ķα׷ Ƴ~ = _=)b +#include + +#include "../../libsql/AsyncSQL.h" + +#define SQL_SAFE_LENGTH(size) (size * 2 + 1) +#define QUERY_SAFE_LENGTH(size) (1024 + SQL_SAFE_LENGTH(size)) + +class CQueryInfo +{ + public: + int iType; + DWORD dwIdent; + void * pvData; +}; + +enum eSQL_SLOT +{ + SQL_PLAYER, + SQL_ACCOUNT, + SQL_COMMON, + SQL_HOTBACKUP, + SQL_MAX_NUM, +}; + +class CDBManager : public singleton +{ + protected: + void Initialize(); + void Destroy(); + + public: + CDBManager(); + virtual ~CDBManager(); + + void Clear(); + void Quit(); + + int Connect(int iSlot, const char * host, int port, const char* dbname, const char* user, const char* pass); + + void ReturnQuery(const char * c_pszQuery, int iType, DWORD dwIdent, void * pvData, int iSlot = SQL_PLAYER); + void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); + SQLMsg * DirectQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); + + SQLMsg * PopResult(); + SQLMsg * PopResult(eSQL_SLOT slot ); + + unsigned long EscapeString(void * to, const void * from, unsigned long length, int iSlot = SQL_PLAYER); + + DWORD CountReturnQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQuery() : 0; } + DWORD CountReturnResult(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountResult() : 0; } + DWORD CountReturnQueryFinished(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQueryFinished() : 0; } + DWORD CountReturnCopiedQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->GetCopiedQueryCount() : 0; } + + DWORD CountAsyncQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQuery() : 0; } + DWORD CountAsyncResult(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountResult() : 0; } + DWORD CountAsyncQueryFinished(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQueryFinished() : 0; } + DWORD CountAsyncCopiedQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->GetCopiedQueryCount() : 0; } + + void ResetCounter() + { + for (int i = 0; i < SQL_MAX_NUM; ++i) + { + if (m_mainSQL[i]) + { + m_mainSQL[i]->ResetQueryFinished(); + m_mainSQL[i]->ResetCopiedQueryCount(); + } + + if (m_asyncSQL[i]) + { + m_asyncSQL[i]->ResetQueryFinished(); + m_asyncSQL[i]->ResetCopiedQueryCount(); + } + } + } + + private: + CAsyncSQL2 * m_mainSQL[SQL_MAX_NUM]; + CAsyncSQL2 * m_directSQL[SQL_MAX_NUM]; + CAsyncSQL2 * m_asyncSQL[SQL_MAX_NUM]; + + int m_quit; // looping flag + + //CHARSET + public: + void SetLocale(const char * szLocale ); + void QueryLocaleSet(); + private: + + //END_CHARSET +}; + +#endif diff --git a/db/src/GuildManager.cpp b/db/src/GuildManager.cpp new file mode 100644 index 0000000..c2b73f8 --- /dev/null +++ b/db/src/GuildManager.cpp @@ -0,0 +1,1491 @@ +#include "stdafx.h" +#include "GuildManager.h" +#include "Main.h" +#include "ClientManager.h" +#include "QID.h" +#include "Config.h" +#include + +extern std::string g_stLocale; + +const int GUILD_RANK_MAX_NUM = 20; + +bool isEurope() +{ + do + { + if (g_stLocale.compare("germany") == 0) break; + if (g_stLocale.compare("france") == 0) break; + if (g_stLocale.compare("italy") == 0) break; + if (g_stLocale.compare("spain") == 0) break; + if (g_stLocale.compare("uk") == 0) break; + if (g_stLocale.compare("turkey") == 0) break; + if (g_stLocale.compare("poland") == 0) break; + if (g_stLocale.compare("portugal") == 0) break; + if (g_stLocale.compare("greek") == 0) break; + + return false; + } while (false); + + return true; +} + +DWORD GetGuildWarWaitStartDuration() +{ + // const int GUILD_WAR_WAIT_START_DURATION = 60; + // const int GUILD_WAR_WAIT_START_DURATION = 5; + + if (isEurope() == true) return 60; + else return 5; +} + +DWORD GetGuildWarReserveSeconds() +{ + // const int GUILD_WAR_RESERVE_SECONDS = 180; + // const int GUILD_WAR_RESERVE_SECONDS = 10; + + if (isEurope() == true) return 180; + else return 10; +} + +namespace +{ + struct FSendPeerWar + { + FSendPeerWar(BYTE bType, BYTE bWar, DWORD GID1, DWORD GID2) + { + if (number(0, 1)) + std::swap(GID1, GID2); + + memset(&p, 0, sizeof(TPacketGuildWar)); + + p.bWar = bWar; + p.bType = bType; + p.dwGuildFrom = GID1; + p.dwGuildTo = GID2; + } + + void operator() (CPeer* peer) + { + if (peer->GetChannel() == 0) + return; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR, 0, sizeof(TPacketGuildWar)); + peer->Encode(&p, sizeof(TPacketGuildWar)); + } + + TPacketGuildWar p; + }; + + struct FSendGuildWarScore + { + FSendGuildWarScore(DWORD guild_gain, DWORD dwOppGID, int iScore, int iBetScore) + { + pck.dwGuildGainPoint = guild_gain; + pck.dwGuildOpponent = dwOppGID; + pck.lScore = iScore; + pck.lBetScore = iBetScore; + } + + void operator() (CPeer* peer) + { + if (peer->GetChannel() == 0) + return; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_SCORE, 0, sizeof(pck)); + peer->Encode(&pck, sizeof(pck)); + } + + TPacketGuildWarScore pck; + }; +} + +CGuildManager::CGuildManager() +{ +} + +CGuildManager::~CGuildManager() +{ + while (!m_pqOnWar.empty()) + { + if (!m_pqOnWar.top().second->bEnd) + delete m_pqOnWar.top().second; + + m_pqOnWar.pop(); + } +} + +TGuild & CGuildManager::TouchGuild(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it != m_map_kGuild.end()) + return it->second; + + TGuild info; + m_map_kGuild.insert(std::map::value_type(GID, info)); + return m_map_kGuild[GID]; +} + +void CGuildManager::ParseResult(SQLResult * pRes) +{ + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes->pSQLResult))) + { + DWORD GID = strtoul(row[0], NULL, 10); + + TGuild & r_info = TouchGuild(GID); + + strlcpy(r_info.szName, row[1], sizeof(r_info.szName)); + str_to_number(r_info.ladder_point, row[2]); + str_to_number(r_info.win, row[3]); + str_to_number(r_info.draw, row[4]); + str_to_number(r_info.loss, row[5]); + str_to_number(r_info.gold, row[6]); + str_to_number(r_info.level, row[7]); + + sys_log(0, + "GuildWar: %-24s ladder %-5d win %-3d draw %-3d loss %-3d", + r_info.szName, + r_info.ladder_point, + r_info.win, + r_info.draw, + r_info.loss); + } +} + +void CGuildManager::Initialize() +{ + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "SELECT id, name, ladder_point, win, draw, loss, gold, level FROM guild%s", GetTablePostfix()); + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiNumRows) + ParseResult(pmsg->Get()); + + char str[128 + 1]; + + if (!CConfig::instance().GetValue("POLY_POWER", str, sizeof(str))) + *str = '\0'; + + if (!polyPower.Analyze(str)) + sys_err("cannot set power poly: %s", str); + else + sys_log(0, "POWER_POLY: %s", str); + + if (!CConfig::instance().GetValue("POLY_HANDICAP", str, sizeof(str))) + *str = '\0'; + + if (!polyHandicap.Analyze(str)) + sys_err("cannot set handicap poly: %s", str); + else + sys_log(0, "HANDICAP_POLY: %s", str); + + QueryRanking(); +} + +void CGuildManager::Load(DWORD dwGuildID) +{ + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), "SELECT id, name, ladder_point, win, draw, loss, gold, level FROM guild%s WHERE id=%u", GetTablePostfix(), dwGuildID); + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiNumRows) + ParseResult(pmsg->Get()); +} + +void CGuildManager::QueryRanking() +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT id,name,ladder_point FROM guild%s ORDER BY ladder_point DESC LIMIT 20", GetTablePostfix()); + + CDBManager::instance().ReturnQuery(szQuery, QID_GUILD_RANKING, 0, 0); +} + +int CGuildManager::GetRanking(DWORD dwGID) +{ + itertype(map_kLadderPointRankingByGID) it = map_kLadderPointRankingByGID.find(dwGID); + + if (it == map_kLadderPointRankingByGID.end()) + return GUILD_RANK_MAX_NUM; + + return MINMAX(0, it->second, GUILD_RANK_MAX_NUM); +} + +void CGuildManager::ResultRanking(MYSQL_RES * pRes) +{ + if (!pRes) + return; + + int iLastLadderPoint = -1; + int iRank = 0; + + map_kLadderPointRankingByGID.clear(); + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pRes))) + { + DWORD dwGID = 0; str_to_number(dwGID, row[0]); + int iLadderPoint = 0; str_to_number(iLadderPoint, row[2]); + + if (iLadderPoint != iLastLadderPoint) + ++iRank; + + sys_log(0, "GUILD_RANK: %-24s %2d %d", row[1], iRank, iLadderPoint); + + map_kLadderPointRankingByGID.insert(std::make_pair(dwGID, iRank)); + } +} + +void CGuildManager::Update() +{ + ProcessReserveWar(); // ó + + time_t now = CClientManager::instance().GetCurrentTime(); + + if (!m_pqOnWar.empty()) + { + // UNKNOWN_GUILD_MANAGE_UPDATE_LOG + /* + sys_log(0, "GuildManager::Update size %d now %d top %d, %s(%u) vs %s(%u)", + m_WarMap.size(), + now, + m_pqOnWar.top().first, + m_map_kGuild[m_pqOnWar.top().second->GID[0]].szName, + m_pqOnWar.top().second->GID[0], + m_map_kGuild[m_pqOnWar.top().second->GID[1]].szName, + m_pqOnWar.top().second->GID[1]); + */ + // END_OF_UNKNOWN_GUILD_MANAGE_UPDATE_LOG + + while (!m_pqOnWar.empty() && (m_pqOnWar.top().first <= now || (m_pqOnWar.top().second && m_pqOnWar.top().second->bEnd))) + { + TGuildWarPQElement * e = m_pqOnWar.top().second; + + m_pqOnWar.pop(); + + if (e) + { + if (!e->bEnd) + WarEnd(e->GID[0], e->GID[1], false); + + delete e; + } + } + } + + // GUILD_SKILL_COOLTIME_BUG_FIX + while (!m_pqSkill.empty() && m_pqSkill.top().first <= now) + { + const TGuildSkillUsed& s = m_pqSkill.top().second; + CClientManager::instance().SendGuildSkillUsable(s.GID, s.dwSkillVnum, true); + m_pqSkill.pop(); + } + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX + + while (!m_pqWaitStart.empty() && m_pqWaitStart.top().first <= now) + { + const TGuildWaitStartInfo & ws = m_pqWaitStart.top().second; + m_pqWaitStart.pop(); + + StartWar(ws.bType, ws.GID[0], ws.GID[1], ws.pkReserve); // insert new element to m_WarMap and m_pqOnWar + + if (ws.lInitialScore) + { + UpdateScore(ws.GID[0], ws.GID[1], ws.lInitialScore, 0); + UpdateScore(ws.GID[1], ws.GID[0], ws.lInitialScore, 0); + } + + TPacketGuildWar p; + + p.bType = ws.bType; + p.bWar = GUILD_WAR_ON_WAR; + p.dwGuildFrom = ws.GID[0]; + p.dwGuildTo = ws.GID[1]; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR, &p, sizeof(p)); + sys_log(0, "GuildWar: GUILD sending start of wait start war %d %d", ws.GID[0], ws.GID[1]); + } +} + +#define for_all(cont, it) for (typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); ++it) + +void CGuildManager::OnSetup(CPeer* peer) +{ + for_all(m_WarMap, it_cont) + for_all(it_cont->second, it) + { + DWORD g1 = it_cont->first; + DWORD g2 = it->first; + TGuildWarPQElement* p = it->second.pElement; + + if (!p || p->bEnd) + continue; + + FSendPeerWar(p->bType, GUILD_WAR_ON_WAR, g1, g2) (peer); + FSendGuildWarScore(p->GID[0], p->GID[1], p->iScore[0], p->iBetScore[0]); + FSendGuildWarScore(p->GID[1], p->GID[0], p->iScore[1], p->iBetScore[1]); + } + + for_all(m_DeclareMap, it) + { + FSendPeerWar(it->bType, GUILD_WAR_SEND_DECLARE, it->dwGuildID[0], it->dwGuildID[1]) (peer); + } + + for_all(m_map_kWarReserve, it) + { + it->second->OnSetup(peer); + } +} + +void CGuildManager::GuildWarWin(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.win; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET win=%d WHERE id=%u", GetTablePostfix(), it->second.win, GID); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::GuildWarLose(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.loss; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET loss=%d WHERE id=%u", GetTablePostfix(), it->second.loss, GID); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::GuildWarDraw(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + ++it->second.draw; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET draw=%d WHERE id=%u", GetTablePostfix(), it->second.draw, GID); + CDBManager::instance().AsyncQuery(buf); +} + +bool CGuildManager::IsHalfWinLadderPoint(DWORD dwGuildWinner, DWORD dwGuildLoser) +{ + DWORD GID1 = dwGuildWinner; + DWORD GID2 = dwGuildLoser; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_mapGuildWarEndTime[GID1]) it = m_mapGuildWarEndTime[GID1].find(GID2); + + if (it != m_mapGuildWarEndTime[GID1].end() && + it->second + GUILD_WAR_LADDER_HALF_PENALTY_TIME > CClientManager::instance().GetCurrentTime()) + return true; + + return false; +} + +void CGuildManager::ProcessDraw(DWORD dwGuildID1, DWORD dwGuildID2) +{ + sys_log(0, "GuildWar: \tThe war between %d and %d is ended in draw", dwGuildID1, dwGuildID2); + + GuildWarDraw(dwGuildID1); + GuildWarDraw(dwGuildID2); + ChangeLadderPoint(dwGuildID1, 0); + ChangeLadderPoint(dwGuildID2, 0); + + QueryRanking(); +} + +void CGuildManager::ProcessWinLose(DWORD dwGuildWinner, DWORD dwGuildLoser) +{ + GuildWarWin(dwGuildWinner); + GuildWarLose(dwGuildLoser); + sys_log(0, "GuildWar: \tWinner : %d Loser : %d", dwGuildWinner, dwGuildLoser); + + int iPoint = GetLadderPoint(dwGuildLoser); + int gain = (int)(iPoint * 0.05); + int loss = (int)(iPoint * 0.07); + + if (IsHalfWinLadderPoint(dwGuildWinner, dwGuildLoser)) + gain /= 2; + + sys_log(0, "GuildWar: \tgain : %d loss : %d", gain, loss); + + ChangeLadderPoint(dwGuildWinner, gain); + ChangeLadderPoint(dwGuildLoser, -loss); + + QueryRanking(); +} + +void CGuildManager::RemoveWar(DWORD GID1, DWORD GID2) +{ + sys_log(0, "GuildWar: RemoveWar(%d, %d)", GID1, GID2); + + if (GID1 > GID2) + std::swap(GID2, GID1); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it == m_WarMap[GID1].end()) + { + if (m_WarMap[GID1].empty()) + m_WarMap.erase(GID1); + + return; + } + + if (it->second.pElement) + it->second.pElement->bEnd = true; + + m_mapGuildWarEndTime[GID1][GID2] = CClientManager::instance().GetCurrentTime(); + + m_WarMap[GID1].erase(it); + + if (m_WarMap[GID1].empty()) + m_WarMap.erase(GID1); +} + +// +// ʵ +// +void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw) +{ + if (GID1 > GID2) + std::swap(GID2, GID1); + + sys_log(0, "GuildWar: WarEnd %d %d", GID1, GID2); + + itertype(m_WarMap[GID1]) itWarMap = m_WarMap[GID1].find(GID2); + + if (itWarMap == m_WarMap[GID1].end()) + { + sys_err("GuildWar: war not exist or already ended. [1]"); + return; + } + + TGuildWarInfo gwi = itWarMap->second; + TGuildWarPQElement * pData = gwi.pElement; + + if (!pData || pData->bEnd) + { + sys_err("GuildWar: war not exist or already ended. [2]"); + return; + } + + DWORD win_guild = pData->GID[0]; + DWORD lose_guild = pData->GID[1]; + + bool bDraw = false; + + if (!bForceDraw) // ºΰ ƴ 쿡 üũѴ. + { + if (pData->iScore[0] > pData->iScore[1]) + { + win_guild = pData->GID[0]; + lose_guild = pData->GID[1]; + } + else if (pData->iScore[1] > pData->iScore[0]) + { + win_guild = pData->GID[1]; + lose_guild = pData->GID[0]; + } + else + bDraw = true; + } + else // º 쿡 º + bDraw = true; + + if (bDraw) + ProcessDraw(win_guild, lose_guild); + else + ProcessWinLose(win_guild, lose_guild); + + // DB ü ֱ Ŷ Ѵ. + CClientManager::instance().for_each_peer(FSendPeerWar(0, GUILD_WAR_END, GID1, GID2)); + + RemoveWar(GID1, GID2); +} + +// +// +// +void CGuildManager::RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bDraw, long lWarPrice) +{ + sys_log(0, "GuildWar: RecvWarOver : winner %u vs %u draw? %d war_price %d", dwGuildWinner, dwGuildLoser, bDraw ? 1 : 0, lWarPrice); + + DWORD GID1 = dwGuildWinner; + DWORD GID2 = dwGuildLoser; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it == m_WarMap[GID1].end()) + return; + + TGuildWarInfo & gw = it->second; + + // Award + if (bDraw) + { + // give bet money / 2 to both guild + DepositMoney(dwGuildWinner, lWarPrice / 2); + DepositMoney(dwGuildLoser, lWarPrice / 2); + ProcessDraw(dwGuildWinner, dwGuildLoser); + } + else + { + // give bet money to winner guild + DepositMoney(dwGuildWinner, lWarPrice); + ProcessWinLose(dwGuildWinner, dwGuildLoser); + } + + if (gw.pkReserve) + { + if (bDraw || !gw.pElement) + gw.pkReserve->Draw(); + else if (gw.pElement->bType == GUILD_WAR_TYPE_BATTLE) + gw.pkReserve->End(gw.pElement->iBetScore[0], gw.pElement->iBetScore[1]); + } + + RemoveWar(GID1, GID2); +} + +void CGuildManager::RecvWarEnd(DWORD GID1, DWORD GID2) +{ + sys_log(0, "GuildWar: RecvWarEnded : %u vs %u", GID1, GID2); + WarEnd(GID1, GID2, true); // Ѿ Ѵ. +} + +void CGuildManager::StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve) +{ + sys_log(0, "GuildWar: StartWar(%d,%d,%d)", bType, GID1, GID2); + + if (GID1 > GID2) + std::swap(GID1, GID2); + + TGuildWarInfo & gw = m_WarMap[GID1][GID2]; // map insert + + if (bType == GUILD_WAR_TYPE_FIELD) + gw.tEndTime = CClientManager::instance().GetCurrentTime() + GUILD_WAR_DURATION; + else + gw.tEndTime = CClientManager::instance().GetCurrentTime() + 172800; + + gw.pElement = new TGuildWarPQElement(bType, GID1, GID2); + gw.pkReserve = pkReserve; + + m_pqOnWar.push(std::make_pair(gw.tEndTime, gw.pElement)); +} + +void CGuildManager::UpdateScore(DWORD dwGainGID, DWORD dwOppGID, int iScoreDelta, int iBetScoreDelta) +{ + DWORD GID1 = dwGainGID; + DWORD GID2 = dwOppGID; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + itertype(m_WarMap[GID1]) it = m_WarMap[GID1].find(GID2); + + if (it != m_WarMap[GID1].end()) + { + TGuildWarPQElement * p = it->second.pElement; + + if (!p || p->bEnd) + { + sys_err("GuildWar: war not exist or already ended."); + return; + } + + int iNewScore = 0; + int iNewBetScore = 0; + + if (p->GID[0] == dwGainGID) + { + p->iScore[0] += iScoreDelta; + p->iBetScore[0] += iBetScoreDelta; + + iNewScore = p->iScore[0]; + iNewBetScore = p->iBetScore[0]; + } + else + { + p->iScore[1] += iScoreDelta; + p->iBetScore[1] += iBetScoreDelta; + + iNewScore = p->iScore[1]; + iNewBetScore = p->iBetScore[1]; + } + + sys_log(0, "GuildWar: SendGuildWarScore guild %u wartype %u score_delta %d betscore_delta %d result %u, %u", + dwGainGID, p->bType, iScoreDelta, iBetScoreDelta, iNewScore, iNewBetScore); + + CClientManager::instance().for_each_peer(FSendGuildWarScore(dwGainGID, dwOppGID, iNewScore, iNewBetScore)); + } +} + +void CGuildManager::AddDeclare(BYTE bType, DWORD guild_from, DWORD guild_to) +{ + TGuildDeclareInfo di(bType, guild_from, guild_to); + + if (m_DeclareMap.find(di) == m_DeclareMap.end()) + m_DeclareMap.insert(di); + + sys_log(0, "GuildWar: AddDeclare(Type:%d,from:%d,to:%d)", bType, guild_from, guild_to); +} + +void CGuildManager::RemoveDeclare(DWORD guild_from, DWORD guild_to) +{ + typeof(m_DeclareMap.begin()) it = m_DeclareMap.find(TGuildDeclareInfo(0, guild_from, guild_to)); + + if (it != m_DeclareMap.end()) + m_DeclareMap.erase(it); + + it = m_DeclareMap.find(TGuildDeclareInfo(0,guild_to, guild_from)); + + if (it != m_DeclareMap.end()) + m_DeclareMap.erase(it); + + sys_log(0, "GuildWar: RemoveDeclare(from:%d,to:%d)", guild_from, guild_to); +} + +bool CGuildManager::TakeBetPrice(DWORD dwGuildTo, DWORD dwGuildFrom, long lWarPrice) +{ + itertype(m_map_kGuild) it_from = m_map_kGuild.find(dwGuildFrom); + itertype(m_map_kGuild) it_to = m_map_kGuild.find(dwGuildTo); + + if (it_from == m_map_kGuild.end() || it_to == m_map_kGuild.end()) + { + sys_log(0, "TakeBetPrice: guild not exist %u %u", + dwGuildFrom, dwGuildTo); + return false; + } + + if (it_from->second.gold < lWarPrice || it_to->second.gold < lWarPrice) + { + sys_log(0, "TakeBetPrice: not enough gold %u %d to %u %d", + dwGuildFrom, it_from->second.gold, dwGuildTo, it_to->second.gold); + return false; + } + + it_from->second.gold -= lWarPrice; + it_to->second.gold -= lWarPrice; + + MoneyChange(dwGuildFrom, it_from->second.gold); + MoneyChange(dwGuildTo, it_to->second.gold); + return true; +} + +bool CGuildManager::WaitStart(TPacketGuildWar * p) +{ + if (p->lWarPrice > 0) + if (!TakeBetPrice(p->dwGuildFrom, p->dwGuildTo, p->lWarPrice)) + return false; + + DWORD dwCurTime = CClientManager::instance().GetCurrentTime(); + + TGuildWaitStartInfo info(p->bType, p->dwGuildFrom, p->dwGuildTo, p->lWarPrice, p->lInitialScore, NULL); + m_pqWaitStart.push(std::make_pair(dwCurTime + GetGuildWarWaitStartDuration(), info)); + + sys_log(0, + "GuildWar: WaitStart g1 %d g2 %d price %d start at %u", + p->dwGuildFrom, + p->dwGuildTo, + p->lWarPrice, + dwCurTime + GetGuildWarWaitStartDuration()); + + return true; +} + +int CGuildManager::GetLadderPoint(DWORD GID) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return 0; + + return it->second.ladder_point; +} + +void CGuildManager::ChangeLadderPoint(DWORD GID, int change) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(GID); + + if (it == m_map_kGuild.end()) + return; + + TGuild & r = it->second; + + r.ladder_point += change; + + if (r.ladder_point < 0) + r.ladder_point = 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET ladder_point=%d WHERE id=%u", GetTablePostfix(), r.ladder_point, GID); + CDBManager::instance().AsyncQuery(buf); + + sys_log(0, "GuildManager::ChangeLadderPoint %u %d", GID, r.ladder_point); + sys_log(0, "%s", buf); + + // Packet + TPacketGuildLadder p; + + p.dwGuild = GID; + p.lLadderPoint = r.ladder_point; + p.lWin = r.win; + p.lDraw = r.draw; + p.lLoss = r.loss; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_LADDER, &p, sizeof(TPacketGuildLadder)); +} + +void CGuildManager::UseSkill(DWORD GID, DWORD dwSkillVnum, DWORD dwCooltime) +{ + // GUILD_SKILL_COOLTIME_BUG_FIX + sys_log(0, "UseSkill(gid=%d, skill=%d) CoolTime(%d:%d)", GID, dwSkillVnum, dwCooltime, CClientManager::instance().GetCurrentTime() + dwCooltime); + m_pqSkill.push(std::make_pair(CClientManager::instance().GetCurrentTime() + dwCooltime, TGuildSkillUsed(GID, dwSkillVnum))); + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX +} + +void CGuildManager::MoneyChange(DWORD dwGuild, DWORD dwGold) +{ + sys_log(0, "GuildManager::MoneyChange %d %d", dwGuild, dwGold); + + TPacketDGGuildMoneyChange p; + p.dwGuild = dwGuild; + p.iTotalGold = dwGold; + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_MONEY_CHANGE, &p, sizeof(p)); + + char buf[1024]; + snprintf(buf, sizeof(buf), "UPDATE guild%s SET gold=%u WHERE id = %u", GetTablePostfix(), dwGold, dwGuild); + CDBManager::instance().AsyncQuery(buf); +} + +void CGuildManager::DepositMoney(DWORD dwGuild, INT iGold) +{ + if (iGold <= 0) + return; + + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + { + sys_err("No guild by id %u", dwGuild); + return; + } + + it->second.gold += iGold; + sys_log(0, "GUILD: %u Deposit %u Total %d", dwGuild, iGold, it->second.gold); + + MoneyChange(dwGuild, it->second.gold); +} + +void CGuildManager::WithdrawMoney(CPeer* peer, DWORD dwGuild, INT iGold) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + { + sys_err("No guild by id %u", dwGuild); + return; + } + + // ϰ ÷ش + if (it->second.gold >= iGold) + { + it->second.gold -= iGold; + sys_log(0, "GUILD: %u Withdraw %d Total %d", dwGuild, iGold, it->second.gold); + + TPacketDGGuildMoneyWithdraw p; + p.dwGuild = dwGuild; + p.iChangeGold = iGold; + + peer->EncodeHeader(HEADER_DG_GUILD_WITHDRAW_MONEY_GIVE, 0, sizeof(TPacketDGGuildMoneyWithdraw)); + peer->Encode(&p, sizeof(TPacketDGGuildMoneyWithdraw)); + } +} + +void CGuildManager::WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT iGold) +{ + itertype(m_map_kGuild) it = m_map_kGuild.find(dwGuild); + + if (it == m_map_kGuild.end()) + return; + + sys_log(0, "GuildManager::WithdrawMoneyReply : guild %u success %d gold %d", dwGuild, bGiveSuccess, iGold); + + if (!bGiveSuccess) + it->second.gold += iGold; + else + MoneyChange(dwGuild, it->second.gold); +} + +// +// (ڰ ִ) +// +const int c_aiScoreByLevel[GUILD_MAX_LEVEL+1] = +{ + 500, // level 0 = 500 probably error + 500, // 1 + 1000, + 2000, + 3000, + 4000, + 6000, + 8000, + 10000, + 12000, + 15000, // 10 + 18000, + 21000, + 24000, + 28000, + 32000, + 36000, + 40000, + 45000, + 50000, + 55000, +}; + +const int c_aiScoreByRanking[GUILD_RANK_MAX_NUM+1] = +{ + 0, + 55000, // 1 + 50000, + 45000, + 40000, + 36000, + 32000, + 28000, + 24000, + 21000, + 18000, // 10 + 15000, + 12000, + 10000, + 8000, + 6000, + 4000, + 3000, + 2000, + 1000, + 500 // 20 +}; + +void CGuildManager::BootReserveWar() +{ + const char * c_apszQuery[2] = + { + "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=1 AND winner=-1", + "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=0" + }; + + for (int i = 0; i < 2; ++i) + { + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(c_apszQuery[i])); + + if (pmsg->Get()->uiNumRows == 0) + continue; + + MYSQL_ROW row; + + while ((row = mysql_fetch_row(pmsg->Get()->pSQLResult))) + { + int col = 0; + + TGuildWarReserve t; + + str_to_number(t.dwID, row[col++]); + str_to_number(t.dwGuildFrom, row[col++]); + str_to_number(t.dwGuildTo, row[col++]); + str_to_number(t.dwTime, row[col++]); + str_to_number(t.bType, row[col++]); + str_to_number(t.lWarPrice, row[col++]); + str_to_number(t.lInitialScore, row[col++]); + str_to_number(t.dwBetFrom, row[col++]); + str_to_number(t.dwBetTo, row[col++]); + str_to_number(t.lPowerFrom, row[col++]); + str_to_number(t.lPowerTo, row[col++]); + str_to_number(t.lHandicap, row[col++]); + t.bStarted = 0; + + CGuildWarReserve * pkReserve = new CGuildWarReserve(t); + + char buf[512]; + snprintf(buf, sizeof(buf), "GuildWar: BootReserveWar : step %d id %u GID1 %u GID2 %u", i, t.dwID, t.dwGuildFrom, t.dwGuildTo); + // i == 0 ̸ DB ƨ ̹Ƿ º óѴ. + // Ǵ, 5 º óѴ. ( þ ش) + //if (i == 0 || (int) t.dwTime - CClientManager::instance().GetCurrentTime() < 60 * 5) + if (i == 0 || (int) t.dwTime - CClientManager::instance().GetCurrentTime() < 0) + { + if (i == 0) + sys_log(0, "%s : DB was shutdowned while war is being.", buf); + else + sys_log(0, "%s : left time lower than 5 minutes, will be canceled", buf); + + pkReserve->Draw(); + delete pkReserve; + } + else + { + sys_log(0, "%s : OK", buf); + m_map_kWarReserve.insert(std::make_pair(t.dwID, pkReserve)); + } + } + } +} + +int GetAverageGuildMemberLevel(DWORD dwGID) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "SELECT AVG(level) FROM guild_member%s, player%s AS p WHERE guild_id=%u AND guild_member%s.pid=p.id", + GetTablePostfix(), GetTablePostfix(), dwGID, GetTablePostfix()); + + std::auto_ptr msg(CDBManager::instance().DirectQuery(szQuery)); + + MYSQL_ROW row; + row = mysql_fetch_row(msg->Get()->pSQLResult); + + int nAverageLevel = 0; str_to_number(nAverageLevel, row[0]); + return nAverageLevel; +} + +int GetGuildMemberCount(DWORD dwGID) +{ + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM guild_member%s WHERE guild_id=%u", GetTablePostfix(), dwGID); + + std::auto_ptr msg(CDBManager::instance().DirectQuery(szQuery)); + + MYSQL_ROW row; + row = mysql_fetch_row(msg->Get()->pSQLResult); + + DWORD dwCount = 0; str_to_number(dwCount, row[0]); + return dwCount; +} + +bool CGuildManager::ReserveWar(TPacketGuildWar * p) +{ + DWORD GID1 = p->dwGuildFrom; + DWORD GID2 = p->dwGuildTo; + + if (GID1 > GID2) + std::swap(GID1, GID2); + + if (p->lWarPrice > 0) + if (!TakeBetPrice(GID1, GID2, p->lWarPrice)) + return false; + + TGuildWarReserve t; + memset(&t, 0, sizeof(TGuildWarReserve)); + + t.dwGuildFrom = GID1; + t.dwGuildTo = GID2; + t.dwTime = CClientManager::instance().GetCurrentTime() + GetGuildWarReserveSeconds(); + t.bType = p->bType; + t.lWarPrice = p->lWarPrice; + t.lInitialScore = p->lInitialScore; + + int lvp, rkp, alv, mc; + + // Ŀ + TGuild & k1 = TouchGuild(GID1); + + lvp = c_aiScoreByLevel[MIN(GUILD_MAX_LEVEL, k1.level)]; + rkp = c_aiScoreByRanking[GetRanking(GID1)]; + alv = GetAverageGuildMemberLevel(GID1); + mc = GetGuildMemberCount(GID1); + + polyPower.SetVar("lvp", lvp); + polyPower.SetVar("rkp", rkp); + polyPower.SetVar("alv", alv); + polyPower.SetVar("mc", mc); + + t.lPowerFrom = (long) polyPower.Eval(); + sys_log(0, "GuildWar: %u lvp %d rkp %d alv %d mc %d power %d", GID1, lvp, rkp, alv, mc, t.lPowerFrom); + + // Ŀ + TGuild & k2 = TouchGuild(GID2); + + lvp = c_aiScoreByLevel[MIN(GUILD_MAX_LEVEL, k2.level)]; + rkp = c_aiScoreByRanking[GetRanking(GID2)]; + alv = GetAverageGuildMemberLevel(GID2); + mc = GetGuildMemberCount(GID2); + + polyPower.SetVar("lvp", lvp); + polyPower.SetVar("rkp", rkp); + polyPower.SetVar("alv", alv); + polyPower.SetVar("mc", mc); + + t.lPowerTo = (long) polyPower.Eval(); + sys_log(0, "GuildWar: %u lvp %d rkp %d alv %d mc %d power %d", GID2, lvp, rkp, alv, mc, t.lPowerTo); + + // ڵĸ + if (t.lPowerTo > t.lPowerFrom) + { + polyHandicap.SetVar("pA", t.lPowerTo); + polyHandicap.SetVar("pB", t.lPowerFrom); + } + else + { + polyHandicap.SetVar("pA", t.lPowerFrom); + polyHandicap.SetVar("pB", t.lPowerTo); + } + + t.lHandicap = (long) polyHandicap.Eval(); + sys_log(0, "GuildWar: handicap %d", t.lHandicap); + + // + char szQuery[512]; + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_war_reservation (guild1, guild2, time, type, warprice, initscore, power1, power2, handicap) " + "VALUES(%u, %u, DATE_ADD(NOW(), INTERVAL 180 SECOND), %u, %ld, %ld, %ld, %ld, %ld)", + GID1, GID2, p->bType, p->lWarPrice, p->lInitialScore, t.lPowerFrom, t.lPowerTo, t.lHandicap); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiInsertID == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_err("GuildWar: Cannot insert row"); + return false; + } + + t.dwID = pmsg->Get()->uiInsertID; + + m_map_kWarReserve.insert(std::make_pair(t.dwID, new CGuildWarReserve(t))); + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_ADD, &t, sizeof(TGuildWarReserve)); + return true; +} + +void CGuildManager::ProcessReserveWar() +{ + DWORD dwCurTime = CClientManager::instance().GetCurrentTime(); + + itertype(m_map_kWarReserve) it = m_map_kWarReserve.begin(); + + while (it != m_map_kWarReserve.end()) + { + itertype(m_map_kWarReserve) it2 = it++; + + CGuildWarReserve * pk = it2->second; + TGuildWarReserve & r = pk->GetDataRef(); + + if (!r.bStarted && r.dwTime - 1800 <= dwCurTime) // 30 ˸. + { + int iMin = (int) ceil((int)(r.dwTime - dwCurTime) / 60.0); + + TGuild & r_1 = m_map_kGuild[r.dwGuildFrom]; + TGuild & r_2 = m_map_kGuild[r.dwGuildTo]; + + sys_log(0, "GuildWar: started GID1 %u GID2 %u %d time %d min %d", r.dwGuildFrom, r.dwGuildTo, r.bStarted, dwCurTime - r.dwTime, iMin); + + if (iMin <= 0) + { + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1 WHERE id=%u", r.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_DEL, &r.dwID, sizeof(DWORD)); + + r.bStarted = true; + + TGuildWaitStartInfo info(r.bType, r.dwGuildFrom, r.dwGuildTo, r.lWarPrice, r.lInitialScore, pk); + m_pqWaitStart.push(std::make_pair(dwCurTime + GetGuildWarWaitStartDuration(), info)); + + TPacketGuildWar pck; + + pck.bType = r.bType; + pck.bWar = GUILD_WAR_WAIT_START; + pck.dwGuildFrom = r.dwGuildFrom; + pck.dwGuildTo = r.dwGuildTo; + pck.lWarPrice = r.lWarPrice; + pck.lInitialScore = r.lInitialScore; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR, &pck, sizeof(TPacketGuildWar)); + //m_map_kWarReserve.erase(it2); + } + else + { + if (iMin != pk->GetLastNoticeMin()) + { + pk->SetLastNoticeMin(iMin); + + if (!g_stLocale.compare("euckr")) + CClientManager::instance().SendNotice("%s %s %d ˴ϴ!", r_1.szName, r_2.szName, iMin); + else if (!g_stLocale.compare("gb2312")) + CClientManager::instance().SendNotice("%s %s İս %dӺʼ!", r_1.szName, r_2.szName, iMin); + } + } + } + } +} + +bool CGuildManager::Bet(DWORD dwID, const char * c_pszLogin, DWORD dwGold, DWORD dwGuild) +{ + itertype(m_map_kWarReserve) it = m_map_kWarReserve.find(dwID); + + char szQuery[1024]; + + if (it == m_map_kWarReserve.end()) + { + sys_log(0, "WAR_RESERVE: Bet: cannot find reserve war by id %u", dwID); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES('%s', %d, %u, NOW())", + c_pszLogin, ITEM_ELK_VNUM, dwGold); + CDBManager::instance().AsyncQuery(szQuery); + return false; + } + + if (!it->second->Bet(c_pszLogin, dwGold, dwGuild)) + { + sys_log(0, "WAR_RESERVE: Bet: cannot bet id %u, login %s, gold %u, guild %u", dwID, c_pszLogin, dwGold, dwGuild); + snprintf(szQuery, sizeof(szQuery), "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES('%s', %d, %u, NOW())", + c_pszLogin, ITEM_ELK_VNUM, dwGold); + CDBManager::instance().AsyncQuery(szQuery); + return false; + } + + return true; +} + +void CGuildManager::CancelWar(DWORD GID1, DWORD GID2) +{ + RemoveDeclare(GID1, GID2); + RemoveWar(GID1, GID2); +} + +bool CGuildManager::ChangeMaster(DWORD dwGID, DWORD dwFrom, DWORD dwTo) +{ + itertype(m_map_kGuild) iter = m_map_kGuild.find(dwGID); + + if (iter == m_map_kGuild.end()) + return false; + + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild%s SET master=%u WHERE id=%u", GetTablePostfix(), dwTo, dwGID); + delete CDBManager::instance().DirectQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_member%s SET grade=1 WHERE pid=%u", GetTablePostfix(), dwTo); + delete CDBManager::instance().DirectQuery(szQuery); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_member%s SET grade=15 WHERE pid=%u", GetTablePostfix(), dwFrom); + delete CDBManager::instance().DirectQuery(szQuery); + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Guild War Reserve Class +////////////////////////////////////////////////////////////////////////////////////////// +CGuildWarReserve::CGuildWarReserve(const TGuildWarReserve & rTable) +{ + thecore_memcpy(&m_data, &rTable, sizeof(TGuildWarReserve)); + m_iLastNoticeMin = -1; + + Initialize(); +} + +void CGuildWarReserve::Initialize() +{ + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT login, guild, gold FROM guild_war_bet WHERE war_id=%u", m_data.dwID); + + std::auto_ptr msgbet(CDBManager::instance().DirectQuery(szQuery)); + + if (msgbet->Get()->uiNumRows) + { + MYSQL_RES * res = msgbet->Get()->pSQLResult; + MYSQL_ROW row; + + char szLogin[LOGIN_MAX_LEN+1]; + DWORD dwGuild; + DWORD dwGold; + + while ((row = mysql_fetch_row(res))) + { + dwGuild = dwGold = 0; + strlcpy(szLogin, row[0], sizeof(szLogin)); + str_to_number(dwGuild, row[1]); + str_to_number(dwGold, row[2]); + + mapBet.insert(std::make_pair(szLogin, std::make_pair(dwGuild, dwGold))); + } + } +} + +void CGuildWarReserve::OnSetup(CPeer * peer) +{ + if (m_data.bStarted) // ̹ ۵ ʴ´. + return; + + FSendPeerWar(m_data.bType, GUILD_WAR_RESERVE, m_data.dwGuildFrom, m_data.dwGuildTo) (peer); + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_RESERVE_ADD, 0, sizeof(TGuildWarReserve)); + peer->Encode(&m_data, sizeof(TGuildWarReserve)); + + TPacketGDGuildWarBet pckBet; + pckBet.dwWarID = m_data.dwID; + + itertype(mapBet) it = mapBet.begin(); + + while (it != mapBet.end()) + { + strlcpy(pckBet.szLogin, it->first.c_str(), sizeof(pckBet.szLogin)); + pckBet.dwGuild = it->second.first; + pckBet.dwGold = it->second.second; + + peer->EncodeHeader(HEADER_DG_GUILD_WAR_BET, 0, sizeof(TPacketGDGuildWarBet)); + peer->Encode(&pckBet, sizeof(TPacketGDGuildWarBet)); + + ++it; + } +} + +bool CGuildWarReserve::Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild) +{ + char szQuery[1024]; + + if (m_data.dwGuildFrom != dwGuild && m_data.dwGuildTo != dwGuild) + { + sys_log(0, "GuildWarReserve::Bet: invalid guild id"); + return false; + } + + if (m_data.bStarted) + { + sys_log(0, "GuildWarReserve::Bet: war is already started"); + return false; + } + + if (mapBet.find(pszLogin) != mapBet.end()) + { + sys_log(0, "GuildWarReserve::Bet: failed. already bet"); + return false; + } + + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO guild_war_bet (war_id, login, gold, guild) VALUES(%u, '%s', %u, %u)", + m_data.dwID, pszLogin, dwGold, dwGuild); + + std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + { + sys_log(0, "GuildWarReserve::Bet: failed. cannot insert row to guild_war_bet table"); + return false; + } + + if (m_data.dwGuildFrom == dwGuild) + m_data.dwBetFrom += dwGold; + else + m_data.dwBetTo += dwGold; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_RESERVE_ADD, &m_data, sizeof(TGuildWarReserve)); + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET bet_from=%u,bet_to=%u WHERE id=%u", + m_data.dwBetFrom, m_data.dwBetTo, m_data.dwID); + + CDBManager::instance().AsyncQuery(szQuery); + + sys_log(0, "GuildWarReserve::Bet: success. %s %u war_id %u bet %u : %u", pszLogin, dwGuild, m_data.dwID, m_data.dwBetFrom, m_data.dwBetTo); + mapBet.insert(std::make_pair(pszLogin, std::make_pair(dwGuild, dwGold))); + + TPacketGDGuildWarBet pckBet; + pckBet.dwWarID = m_data.dwID; + strlcpy(pckBet.szLogin, pszLogin, sizeof(pckBet.szLogin)); + pckBet.dwGuild = dwGuild; + pckBet.dwGold = dwGold; + + CClientManager::instance().ForwardPacket(HEADER_DG_GUILD_WAR_BET, &pckBet, sizeof(TPacketGDGuildWarBet)); + return true; +} + +// +// º ó: κ ºΰ , Ư Ȳ 쿡 +// º ó ־ Ѵ. +// +void CGuildWarReserve::Draw() +{ + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1,winner=0 WHERE id=%u", m_data.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + if (mapBet.empty()) + return; + + sys_log(0, "WAR_REWARD: Draw. war_id %u", m_data.dwID); + + itertype(mapBet) it = mapBet.begin(); + + while (1) + { + int iLen = 0; + int iRow = 0; + + iLen += snprintf(szQuery, sizeof(szQuery) - iLen, "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES"); + + while (it != mapBet.end()) + { + if (iRow == 0) + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, "('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, it->second.second); + else + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, ",('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, it->second.second); + + it++; + + if (iLen > 384) + break; + + ++iRow; + } + + if (iRow > 0) + { + sys_log(0, "WAR_REWARD: QUERY: %s", szQuery); + CDBManager::instance().AsyncQuery(szQuery); + } + + if (it == mapBet.end()) + break; + } +} + +void CGuildWarReserve::End(int iScoreFrom, int iScoreTo) +{ + DWORD dwWinner; + + sys_log(0, "WAR_REWARD: End: From %u %d To %u %d", m_data.dwGuildFrom, iScoreFrom, m_data.dwGuildTo, iScoreTo); + + if (m_data.lPowerFrom > m_data.lPowerTo) + { + if (m_data.lHandicap > iScoreFrom - iScoreTo) + { + sys_log(0, "WAR_REWARD: End: failed to overcome handicap, From is strong but To won"); + dwWinner = m_data.dwGuildTo; + } + else + { + sys_log(0, "WAR_REWARD: End: success to overcome handicap, From win!"); + dwWinner = m_data.dwGuildFrom; + } + } + else + { + if (m_data.lHandicap > iScoreTo - iScoreFrom) + { + sys_log(0, "WAR_REWARD: End: failed to overcome handicap, To is strong but From won"); + dwWinner = m_data.dwGuildFrom; + } + else + { + sys_log(0, "WAR_REWARD: End: success to overcome handicap, To win!"); + dwWinner = m_data.dwGuildTo; + } + } + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE guild_war_reservation SET started=1,winner=%u,result1=%d,result2=%d WHERE id=%u", + dwWinner, iScoreFrom, iScoreTo, m_data.dwID); + CDBManager::instance().AsyncQuery(szQuery); + + if (mapBet.empty()) + return; + + DWORD dwTotalBet = m_data.dwBetFrom + m_data.dwBetTo; + DWORD dwWinnerBet = 0; + + if (dwWinner == m_data.dwGuildFrom) + dwWinnerBet = m_data.dwBetFrom; + else if (dwWinner == m_data.dwGuildTo) + dwWinnerBet = m_data.dwBetTo; + else + { + sys_err("WAR_REWARD: fatal error, winner does not exist!"); + return; + } + + if (dwWinnerBet == 0) + { + sys_err("WAR_REWARD: total bet money on winner is zero"); + return; + } + + sys_log(0, "WAR_REWARD: End: Total bet: %u, Winner bet: %u", dwTotalBet, dwWinnerBet); + + itertype(mapBet) it = mapBet.begin(); + + while (1) + { + int iLen = 0; + int iRow = 0; + + iLen += snprintf(szQuery, sizeof(szQuery) - iLen, "INSERT INTO item_award (login, vnum, socket0, given_time) VALUES"); + + while (it != mapBet.end()) + { + if (it->second.first != dwWinner) + { + ++it; + continue; + } + + double ratio = (double) it->second.second / dwWinnerBet; + + // 10% й + sys_log(0, "WAR_REWARD: %s %u ratio %f", it->first.c_str(), it->second.second, ratio); + + DWORD dwGold = (DWORD) (dwTotalBet * ratio * 0.9); + + if (iRow == 0) + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, "('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, dwGold); + else + iLen += snprintf(szQuery + iLen, sizeof(szQuery) - iLen, ",('%s', %d, %u, NOW())", + it->first.c_str(), ITEM_ELK_VNUM, dwGold); + + ++it; + + if (iLen > 384) + break; + + ++iRow; + } + + if (iRow > 0) + { + sys_log(0, "WAR_REWARD: query: %s", szQuery); + CDBManager::instance().AsyncQuery(szQuery); + } + + if (it == mapBet.end()) + break; + } +} + diff --git a/db/src/GuildManager.h b/db/src/GuildManager.h new file mode 100644 index 0000000..fe99e1a --- /dev/null +++ b/db/src/GuildManager.h @@ -0,0 +1,260 @@ +// vim:ts=8 sw=4 +#ifndef __INC_GUILD_MANAGER_H +#define __INC_GUILD_MANAGER_H + +#include "Peer.h" +#include +#include +#include "../../libsql/libsql.h" +#include "../../libpoly/Poly.h" + +enum +{ + GUILD_WARP_WAR_CHANNEL = 99 +}; + +class CGuildWarReserve; + +struct TGuildDeclareInfo +{ + BYTE bType; + DWORD dwGuildID[2]; + + TGuildDeclareInfo(BYTE _bType, DWORD _dwGuildID1, DWORD _dwGuildID2) + : bType(_bType) + { + dwGuildID[0] = _dwGuildID1; + dwGuildID[1] = _dwGuildID2; + } + + bool operator < (const TGuildDeclareInfo& r) const + { + return dwGuildID[0] < r.dwGuildID[0] || dwGuildID[0] == r.dwGuildID[0] && dwGuildID[1] < r.dwGuildID[1]; + } + + TGuildDeclareInfo& operator = (const TGuildDeclareInfo& r) + { + bType = r.bType; + dwGuildID[0] = r.dwGuildID[0]; + dwGuildID[1] = r.dwGuildID[1]; + return *this; + } +}; + +struct TGuildWaitStartInfo +{ + BYTE bType; + DWORD GID[2]; + long lWarPrice; + long lInitialScore; + CGuildWarReserve * pkReserve; + + TGuildWaitStartInfo(BYTE _bType, + DWORD _g1, + DWORD _g2, + long _lWarPrice, + long _lInitialScore, + CGuildWarReserve * _pkReserve) + : bType(_bType), lWarPrice(_lWarPrice), lInitialScore(_lInitialScore), pkReserve(_pkReserve) + { + GID[0] = _g1; + GID[1] = _g2; + } + + bool operator < (const TGuildWaitStartInfo& r) const + { + return GID[0] < r.GID[0] || GID[0] == r.GID[0] && GID[1] < r.GID[1]; + } +}; + +struct TGuildWarPQElement +{ + bool bEnd; + BYTE bType; + DWORD GID[2]; + DWORD iScore[2]; + DWORD iBetScore[2]; + + TGuildWarPQElement(BYTE _bType, DWORD GID1, DWORD GID2) : bEnd(false), bType(_bType) + { + bType = _bType; + GID[0] = GID1; + GID[1] = GID2; + iScore[0] = iScore[1] = 0; + iBetScore[0] = iBetScore[1] = 0; + } +}; + +struct TGuildSkillUsed +{ + DWORD GID; + DWORD dwSkillVnum; + + // GUILD_SKILL_COOLTIME_BUG_FIX + TGuildSkillUsed(DWORD _GID, DWORD _dwSkillVnum) : GID(_GID), dwSkillVnum(_dwSkillVnum) + { + } + // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX +}; + +inline bool operator < (const TGuildSkillUsed& a, const TGuildSkillUsed& b) +{ + return a.GID < b.GID || a.GID == b.GID && a.dwSkillVnum < b.dwSkillVnum; +} + +typedef struct SGuild +{ + SGuild() : ladder_point(0), win(0), draw(0), loss(0), gold(0), level(0) + { + memset(szName, 0, sizeof(szName)); + } + + char szName[GUILD_NAME_MAX_LEN+1]; + int ladder_point; + int win; + int draw; + int loss; + int gold; + int level; +} TGuild; + +typedef struct SGuildWarInfo +{ + time_t tEndTime; + TGuildWarPQElement * pElement; + CGuildWarReserve * pkReserve; + + SGuildWarInfo() : pElement(NULL) + { + } +} TGuildWarInfo; + +class CGuildWarReserve +{ + public: + CGuildWarReserve(const TGuildWarReserve& rTable); + + void Initialize(); + + TGuildWarReserve & GetDataRef() + { + return m_data; + } + + void OnSetup(CPeer * peer); + bool Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild); + void Draw(); + void End(int iScoreFrom, int iScoreTo); + + int GetLastNoticeMin() { return m_iLastNoticeMin; } + void SetLastNoticeMin(int iMin) { m_iLastNoticeMin = iMin; } + + private: + CGuildWarReserve(); // ⺻ ڸ ϵ ǵ + + TGuildWarReserve m_data; + // > + std::map > mapBet; + int m_iLastNoticeMin; +}; + +class CGuildManager : public singleton +{ + public: + CGuildManager(); + virtual ~CGuildManager(); + + void Initialize(); + + void Load(DWORD dwGuildID); + + TGuild & TouchGuild(DWORD GID); + + void Update(); + + void OnSetup(CPeer * peer); + void StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve = NULL); + + void UpdateScore(DWORD guild_gain_point, DWORD guild_opponent, int iScore, int iBetScore); + + void AddDeclare(BYTE bType, DWORD guild_from, DWORD guild_to); + void RemoveDeclare(DWORD guild_from, DWORD guild_to); + + bool TakeBetPrice(DWORD dwGuildTo, DWORD dwGuildFrom, long lWarPrice); + + bool WaitStart(TPacketGuildWar * p); + + void RecvWarEnd(DWORD GID1, DWORD GID2); + void RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bDraw, long lWarPrice); + + void ChangeLadderPoint(DWORD GID, int change); + + void UseSkill(DWORD dwGuild, DWORD dwSkillVnum, DWORD dwCooltime); + + INT GetGuildGold(DWORD dwGuild); + void DepositMoney(DWORD dwGuild, INT lGold); + void WithdrawMoney(CPeer* peer, DWORD dwGuild, INT lGold); + void WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT lGold); + + void MoneyChange(DWORD dwGuild, DWORD dwGold); + + void QueryRanking(); + void ResultRanking(MYSQL_RES * pRes); + int GetRanking(DWORD dwGID); + + // + // Reserve War + // + void BootReserveWar(); + bool ReserveWar(TPacketGuildWar * p); + void ProcessReserveWar(); + bool Bet(DWORD dwID, const char * c_pszLogin, DWORD dwGold, DWORD dwGuild); + + void CancelWar(DWORD GID1, DWORD GID2); + + bool ChangeMaster(DWORD dwGID, DWORD dwFrom, DWORD dwTo); + + private: + void ParseResult(SQLResult * pRes); + + void RemoveWar(DWORD GID1, DWORD GID2); // erase war from m_WarMap and set end on priority queue + + void WarEnd(DWORD GID1, DWORD GID2, bool bDraw = false); + + int GetLadderPoint(DWORD GID); + + void GuildWarWin(DWORD GID); + void GuildWarDraw(DWORD GID); + void GuildWarLose(DWORD GID); + + void ProcessDraw(DWORD dwGuildID1, DWORD dwGuildID2); + void ProcessWinLose(DWORD dwGuildWinner, DWORD dwGuildLoser); + + bool IsHalfWinLadderPoint(DWORD dwGuildWinner, DWORD dwGuildLoser); + + std::map m_map_kGuild; + std::map > m_mapGuildWarEndTime; + + std::set m_DeclareMap; // ¸ + std::map > m_WarMap; + + typedef std::pair stPairGuildWar; + typedef std::pair stPairSkillUsed; + typedef std::pair stPairWaitStart; + + std::priority_queue, std::greater > + m_pqOnWar; + std::priority_queue, std::greater > + m_pqWaitStart; + std::priority_queue, std::greater > + m_pqSkill; + + std::map m_map_kWarReserve; + CPoly polyPower; + CPoly polyHandicap; + + // GID Ranking + std::map map_kLadderPointRankingByGID; +}; + +#endif diff --git a/db/src/HB.cpp b/db/src/HB.cpp new file mode 100644 index 0000000..1a2498f --- /dev/null +++ b/db/src/HB.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "HB.h" +#include "Main.h" +#include "DBManager.h" + +#include + +PlayerHB::PlayerHB() +{ + m_iExpireTime = 3600; // 1 hour hotbackup default. +} + +PlayerHB::~PlayerHB() +{ +} + +bool PlayerHB::Initialize() +{ + char szQuery[128]; + snprintf(szQuery, sizeof(szQuery), "SHOW CREATE TABLE player%s", GetTablePostfix()); + + std::auto_ptr pMsg(CDBManager::instance().DirectQuery(szQuery)); + + if (pMsg->Get()->uiNumRows == 0) + return false; + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + m_stCreateTableQuery = row[1]; + return true; +} + +// +// @version 05/07/05 Bang2ni - id شϴ data ϰ data insert ϴڵ ߰. +// +void PlayerHB::Put(DWORD id) +{ + itertype(m_map_data) it = m_map_data.find(id); + + if (it == m_map_data.end()) + { + Query(id); + m_map_data.insert(std::pair< DWORD, time_t >(id, get_dword_time())); + return; + } + + if (time(0) - it->second > m_iExpireTime) + Query(id); +} + +// +// @version 05/07/05 Bang2ni - Query string ۰ ۾Ƽ ÷. +// +bool PlayerHB::Query(DWORD id) +{ + time_t ct = time(0); + struct tm curr_tm = *localtime(&ct); + char szTableName[64]; + snprintf(szTableName, sizeof(szTableName), "hb_%02d%02d%02d%02d_player%s", + curr_tm.tm_year - 100, curr_tm.tm_mon + 1, curr_tm.tm_mday, curr_tm.tm_hour, GetTablePostfix()); + + char szQuery[4096]; + + if (m_stTableName.compare(szTableName)) + { + char szFind[32]; + snprintf(szFind, sizeof(szFind), "CREATE TABLE `player%s`", GetTablePostfix()); + int pos = m_stCreateTableQuery.find(szFind); + + if (pos < 0) + { + sys_err("cannot find %s ", szFind); + // sys_err("cannot find %s in %s", szFind, m_stCreateTableQuery.c_str()); + return false; + } + + snprintf(szQuery, sizeof(szQuery), "CREATE TABLE IF NOT EXISTS %s%s", szTableName, m_stCreateTableQuery.c_str() + strlen(szFind)); + // sys_log(0, "%s", szQuery); + std::auto_ptr pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_HOTBACKUP)); + m_stTableName = szTableName; + } + + snprintf(szQuery, sizeof(szQuery), "REPLACE INTO %s SELECT * FROM %splayer%s WHERE id=%u", m_stTableName.c_str(), GetPlayerDBName(), GetTablePostfix(), id); + CDBManager::instance().AsyncQuery(szQuery, SQL_HOTBACKUP); + return true; +} + diff --git a/db/src/HB.h b/db/src/HB.h new file mode 100644 index 0000000..8c5da77 --- /dev/null +++ b/db/src/HB.h @@ -0,0 +1,24 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN_II_PLAYERHB_H__ +#define __INC_METIN_II_PLAYERHB_H__ + +class PlayerHB : public singleton +{ + public: + PlayerHB(); + virtual ~PlayerHB(); + + bool Initialize(); + + void Put(DWORD id); + + private: + bool Query(DWORD id); + + std::map m_map_data; + std::string m_stCreateTableQuery; + std::string m_stTableName; + int m_iExpireTime; +}; + +#endif diff --git a/db/src/ItemAwardManager.cpp b/db/src/ItemAwardManager.cpp new file mode 100644 index 0000000..54f4b1a --- /dev/null +++ b/db/src/ItemAwardManager.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "QID.h" +#include "DBManager.h" +#include "ItemAwardManager.h" +#include "Peer.h" + +#include "ClientManager.h" + + + +DWORD g_dwLastCachedItemAwardID = 0; +ItemAwardManager::ItemAwardManager() +{ +} + +ItemAwardManager::~ItemAwardManager() +{ +} + +void ItemAwardManager::RequestLoad() +{ + char szQuery[QUERY_MAX_LEN]; + snprintf(szQuery, sizeof(szQuery), "SELECT id,login,vnum,count,socket0,socket1,socket2,mall,why FROM item_award WHERE taken_time IS NULL and id > %d", g_dwLastCachedItemAwardID); + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_LOAD, 0, NULL); +} + +void ItemAwardManager::Load(SQLMsg * pMsg) +{ + MYSQL_RES * pRes = pMsg->Get()->pSQLResult; + + for (uint i = 0; i < pMsg->Get()->uiNumRows; ++i) + { + MYSQL_ROW row = mysql_fetch_row(pRes); + int col = 0; + + DWORD dwID = 0; + str_to_number(dwID, row[col++]); + + if (m_map_award.find(dwID) != m_map_award.end()) + continue; + + TItemAward * kData = new TItemAward; + memset(kData, 0, sizeof(TItemAward)); + + kData->dwID = dwID; + trim_and_lower(row[col++], kData->szLogin, sizeof(kData->szLogin)); + str_to_number(kData->dwVnum, row[col++]); + str_to_number(kData->dwCount, row[col++]); + str_to_number(kData->dwSocket0, row[col++]); + str_to_number(kData->dwSocket1, row[col++]); + str_to_number(kData->dwSocket2, row[col++]); + str_to_number(kData->bMall, row[col++]); + + if (row[col]) + { + strlcpy(kData->szWhy, row[col], sizeof(kData->szWhy)); + // ߿ whyݷ뿡 + char* whyStr = kData->szWhy; //why ݷ б + char cmdStr[100] = ""; //whyݷ뿡 ӽ ڿ ص + strcpy(cmdStr,whyStr); //ɾ ū ūȭ DZ + char command[20] = ""; + strcpy(command,CClientManager::instance().GetCommand(cmdStr)); // command + //sys_err("%d, %s",pItemAward->dwID,command); + if( !(strcmp(command,"GIFT") )) // command GIFT̸ + { + TPacketItemAwardInfromer giftData; + strcpy(giftData.login, kData->szLogin); //α ̵ + strcpy(giftData.command, command); //ɾ + giftData.vnum = kData->dwVnum; // vnum + CClientManager::instance().ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer)); + } + } + + m_map_award.insert(std::make_pair(dwID, kData)); + + printf("ITEM_AWARD load id %u bMall %d \n", kData->dwID, kData->bMall); + sys_log(0, "ITEM_AWARD: load id %lu login %s vnum %lu count %u socket %lu", kData->dwID, kData->szLogin, kData->dwVnum, kData->dwCount, kData->dwSocket0); + std::set & kSet = m_map_kSetAwardByLogin[kData->szLogin]; + kSet.insert(kData); + + if (dwID > g_dwLastCachedItemAwardID) + g_dwLastCachedItemAwardID = dwID; + } +} + +std::set * ItemAwardManager::GetByLogin(const char * c_pszLogin) +{ + itertype(m_map_kSetAwardByLogin) it = m_map_kSetAwardByLogin.find(c_pszLogin); + + if (it == m_map_kSetAwardByLogin.end()) + return NULL; + + return &it->second; +} + +void ItemAwardManager::Taken(DWORD dwAwardID, DWORD dwItemID) +{ + itertype(m_map_award) it = m_map_award.find(dwAwardID); + + if (it == m_map_award.end()) + { + sys_log(0, "ITEM_AWARD: Taken ID not exist %lu", dwAwardID); + return; + } + + TItemAward * k = it->second; + k->bTaken = true; + + // + // Update taken_time in database to prevent not to give him again. + // + char szQuery[QUERY_MAX_LEN]; + + snprintf(szQuery, sizeof(szQuery), + "UPDATE item_award SET taken_time=NOW(),item_id=%u WHERE id=%u AND taken_time IS NULL", + dwItemID, dwAwardID); + + CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_TAKEN, 0, NULL); +} + +std::map& ItemAwardManager::GetMapAward() +{ + return m_map_award; +} + +std::map >& ItemAwardManager::GetMapkSetAwardByLogin() +{ + return m_map_kSetAwardByLogin; +} \ No newline at end of file diff --git a/db/src/ItemAwardManager.h b/db/src/ItemAwardManager.h new file mode 100644 index 0000000..db6e42d --- /dev/null +++ b/db/src/ItemAwardManager.h @@ -0,0 +1,43 @@ +// vim:ts=8 sw=4 +#ifndef __INC_ITEM_AWARD_H +#define __INC_ITEM_AWARD_H +#include +#include +#include "Peer.h" + +typedef struct SItemAward +{ + DWORD dwID; + char szLogin[LOGIN_MAX_LEN+1]; + DWORD dwVnum; + DWORD dwCount; + DWORD dwSocket0; + DWORD dwSocket1; + DWORD dwSocket2; + char szWhy[ITEM_AWARD_WHY_MAX_LEN+1]; + bool bTaken; + bool bMall; +} TItemAward; + +class ItemAwardManager : public singleton +{ + public: + ItemAwardManager(); + virtual ~ItemAwardManager(); + + void RequestLoad(); + void Load(SQLMsg * pMsg); + std::set * GetByLogin(const char * c_pszLogin); + + void Taken(DWORD dwAwardID, DWORD dwItemID); + // gift notify + std::map& GetMapAward(); + std::map >& GetMapkSetAwardByLogin(); + private: + // ID, ItemAward pair + std::map m_map_award; + // PID, ItemAward pair + std::map > m_map_kSetAwardByLogin; +}; + +#endif diff --git a/db/src/ItemIDRangeManager.cpp b/db/src/ItemIDRangeManager.cpp new file mode 100644 index 0000000..099765b --- /dev/null +++ b/db/src/ItemIDRangeManager.cpp @@ -0,0 +1,165 @@ +#include "stdafx.h" +#include "ItemIDRangeManager.h" +#include "Main.h" +#include "DBManager.h" +#include "ClientManager.h" +#include "Peer.h" + +CItemIDRangeManager::CItemIDRangeManager() +{ + m_listData.clear(); +} + +void CItemIDRangeManager::Build() +{ + DWORD dwMin = 0; + DWORD dwMax = 0; + TItemIDRangeTable range; + + for (int i = 0; ; ++i) + { + dwMin = cs_dwMinimumRange * (i + 1) + 1; + dwMax = cs_dwMinimumRange * (i + 2); + + if (dwMax == cs_dwMaxItemID) + break; + + if (CClientManager::instance().GetItemRange().dwMin <= dwMin && + CClientManager::instance().GetItemRange().dwMax >= dwMax) + { + continue; + } + + if (BuildRange(dwMin, dwMax, range) == true) + { + m_listData.push_back(range); + } + } +} + +struct FCheckCollision +{ + bool hasCollision; + TItemIDRangeTable range; + + FCheckCollision(TItemIDRangeTable data) + { + hasCollision = false; + range = data; + } + + void operator() (CPeer* peer) + { + if (hasCollision == false) + { + hasCollision = peer->CheckItemIDRangeCollision(range); + } + } +}; + +TItemIDRangeTable CItemIDRangeManager::GetRange() +{ + TItemIDRangeTable ret; + ret.dwMin = 0; + ret.dwMax = 0; + ret.dwUsableItemIDMin = 0; + + if (m_listData.size() > 0) + { + while (m_listData.size() > 0) + { + ret = m_listData.front(); + m_listData.pop_front(); + + FCheckCollision f(ret); + CClientManager::instance().for_each_peer(f); + + if (f.hasCollision == false) return ret; + } + } + + for (int i = 0; i < 10; ++i) + sys_err("ItemIDRange: NO MORE ITEM ID RANGE"); + + return ret; +} + +bool CItemIDRangeManager::BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range) +{ + char szQuery[1024]; + DWORD dwItemMaxID = 0; + SQLMsg* pMsg = NULL; + MYSQL_ROW row; + + snprintf(szQuery, sizeof(szQuery), "SELECT MAX(id) FROM item%s WHERE id >= %u and id <= %u", GetTablePostfix(), dwMin, dwMax); + + pMsg = CDBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + row = mysql_fetch_row(pMsg->Get()->pSQLResult); + str_to_number(dwItemMaxID, row[0]); + } + delete pMsg; + } + + if (dwItemMaxID == 0) + dwItemMaxID = dwMin; + else + dwItemMaxID++; + + if ((dwMax < dwItemMaxID) || (dwMax - dwItemMaxID < cs_dwMinimumRemainCount)) + { + sys_log(0, "ItemIDRange: Build %u ~ %u start: %u\tNOT USE remain count is below %u", + dwMin, dwMax, dwItemMaxID, cs_dwMinimumRemainCount); + } + else + { + range.dwMin = dwMin; + range.dwMax = dwMax; + range.dwUsableItemIDMin = dwItemMaxID; + + snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM item%s WHERE id >= %u AND id <= %u", + GetTablePostfix(), range.dwUsableItemIDMin, range.dwMax); + + pMsg = CDBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + DWORD count = 0; + row = mysql_fetch_row(pMsg->Get()->pSQLResult); + str_to_number(count, row[0]); + + if (count > 0) + { + sys_err("ItemIDRange: Build: %u ~ %u\thave a item", range.dwUsableItemIDMin, range.dwMax); + return false; + } + else + { + sys_log(0, "ItemIDRange: Build: %u ~ %u start:%u", range.dwMin, range.dwMax, range.dwUsableItemIDMin); + return true; + } + } + + delete pMsg; + } + } + + return false; +} + +void CItemIDRangeManager::UpdateRange(DWORD dwMin, DWORD dwMax) +{ + TItemIDRangeTable range; + + if (BuildRange(dwMin, dwMax, range) == true) + { + m_listData.push_back(range); + } +} + diff --git a/db/src/ItemIDRangeManager.h b/db/src/ItemIDRangeManager.h new file mode 100644 index 0000000..d15cd01 --- /dev/null +++ b/db/src/ItemIDRangeManager.h @@ -0,0 +1,24 @@ +// vim:ts=4 sw=4 +#ifndef __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ +#define __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ + +class CItemIDRangeManager : public singleton +{ + private : + const static DWORD cs_dwMaxItemID = 4290000000UL; + const static DWORD cs_dwMinimumRange = 10000000UL; + const static DWORD cs_dwMinimumRemainCount = 10000UL; + + std::list m_listData; + + public : + CItemIDRangeManager(); + + void Build(); + bool BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range); + void UpdateRange(DWORD dwMin, DWORD dwMax); + + TItemIDRangeTable GetRange(); +}; + +#endif diff --git a/db/src/Lock.cpp b/db/src/Lock.cpp new file mode 100644 index 0000000..876836f --- /dev/null +++ b/db/src/Lock.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "Lock.h" + +CLock::CLock() +{ +} + +CLock::~CLock() +{ +} + +void CLock::Initialize() +{ + m_bLocked = false; +#ifndef __WIN32__ + pthread_mutex_init(&m_lock, NULL); +#else + ::InitializeCriticalSection(&m_lock); +#endif +} + +void CLock::Destroy() +{ + assert(!m_bLocked && "lock didn't released"); +#ifndef __WIN32__ + pthread_mutex_destroy(&m_lock); +#else + ::DeleteCriticalSection(&m_lock); +#endif +} + +int CLock::Trylock() +{ +#ifndef __WIN32__ + return pthread_mutex_trylock(&m_lock); +#else + return ::TryEnterCriticalSection(&m_lock); +#endif +} + +void CLock::Lock() +{ +#ifndef __WIN32__ + pthread_mutex_lock(&m_lock); +#else + ::EnterCriticalSection(&m_lock); +#endif + m_bLocked = true; +} + +void CLock::Unlock() +{ + assert(m_bLocked && "lock didn't issued"); + m_bLocked = false; +#ifndef __WIN32__ + pthread_mutex_unlock(&m_lock); +#else + ::LeaveCriticalSection(&m_lock); +#endif +} diff --git a/db/src/Lock.h b/db/src/Lock.h new file mode 100644 index 0000000..d0ae58a --- /dev/null +++ b/db/src/Lock.h @@ -0,0 +1,27 @@ +// vim:ts=8 sw=4 +#ifndef __INC_LOCK_H__ +#define __INC_LOCK_H__ + +#ifdef __WIN32__ +typedef CRITICAL_SECTION lock_t; +#else +typedef pthread_mutex_t lock_t; +#endif + +class CLock +{ + public: + CLock(); + ~CLock(); + + void Initialize(); + void Destroy(); + int Trylock(); + void Lock(); + void Unlock(); + + private: + lock_t m_lock; + bool m_bLocked; +}; +#endif diff --git a/db/src/LoginData.cpp b/db/src/LoginData.cpp new file mode 100644 index 0000000..3bba1c5 --- /dev/null +++ b/db/src/LoginData.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" +#include "LoginData.h" +#include "ClientManager.h" + +CLoginData::CLoginData() +{ + m_dwKey = 0; + memset(m_adwClientKey, 0, sizeof(m_adwClientKey)); + m_dwConnectedPeerHandle = 0; + m_dwLogonTime = 0; + memset(m_szIP, 0, sizeof(m_szIP)); + m_bPlay = false; + m_bDeleted = false; + m_bBillType = 0; + m_dwBillID = 0; + m_lastPlayTime = 0; + m_dwLastPlayerID = 0; + + memset(&m_data, 0, sizeof(TAccountTable)); +} + +TAccountTable & CLoginData::GetAccountRef() +{ + return m_data; +} + +void CLoginData::SetClientKey(const DWORD * c_pdwClientKey) +{ + thecore_memcpy(&m_adwClientKey, c_pdwClientKey, sizeof(DWORD) * 4); +} + +const DWORD * CLoginData::GetClientKey() +{ + return &m_adwClientKey[0]; +} + +void CLoginData::SetKey(DWORD dwKey) +{ + m_dwKey = dwKey; +} + +DWORD CLoginData::GetKey() +{ + return m_dwKey; +} + +void CLoginData::SetConnectedPeerHandle(DWORD dwHandle) +{ + m_dwConnectedPeerHandle = dwHandle; +} + +DWORD CLoginData::GetConnectedPeerHandle() +{ + return m_dwConnectedPeerHandle; +} + +void CLoginData::SetLogonTime() +{ + m_dwLogonTime = get_dword_time(); +} + +DWORD CLoginData::GetLogonTime() +{ + return m_dwLogonTime; +} + +void CLoginData::SetIP(const char * c_pszIP) +{ + strlcpy(m_szIP, c_pszIP, sizeof(m_szIP)); +} + +const char * CLoginData::GetIP() +{ + return m_szIP; +} + +void CLoginData::SetPlay(bool bOn) +{ + if (bOn) + { + sys_log(0, "SetPlay on %lu %s", GetKey(), m_data.login); + SetLogonTime(); + } + else + sys_log(0, "SetPlay off %lu %s", GetKey(), m_data.login); + + m_bPlay = bOn; + m_lastPlayTime = CClientManager::instance().GetCurrentTime(); +} + +bool CLoginData::IsPlay() +{ + return m_bPlay; +} + +void CLoginData::SetDeleted(bool bSet) +{ + m_bDeleted = bSet; +} + +bool CLoginData::IsDeleted() +{ + return m_bDeleted; +} + +void CLoginData::SetPremium(int * paiPremiumTimes) +{ + thecore_memcpy(m_aiPremiumTimes, paiPremiumTimes, sizeof(m_aiPremiumTimes)); +} + +int CLoginData::GetPremium(BYTE type) +{ + if (type >= PREMIUM_MAX_NUM) + return 0; + + return m_aiPremiumTimes[type]; +} + +int * CLoginData::GetPremiumPtr() +{ + return &m_aiPremiumTimes[0]; +} + diff --git a/db/src/LoginData.h b/db/src/LoginData.h new file mode 100644 index 0000000..73e20a7 --- /dev/null +++ b/db/src/LoginData.h @@ -0,0 +1,66 @@ +// vim:ts=8 sw=4 +#ifndef __INC_METIN_II_DB_LOGINDATA_H__ +#define __INC_METIN_II_DB_LOGINDATA_H__ + +class CLoginData +{ + public: + CLoginData(); + + TAccountTable & GetAccountRef(); + void SetClientKey(const DWORD * c_pdwClientKey); + + const DWORD * GetClientKey(); + void SetKey(DWORD dwKey); + DWORD GetKey(); + + void SetConnectedPeerHandle(DWORD dwHandle); + DWORD GetConnectedPeerHandle(); + + void SetLogonTime(); + DWORD GetLogonTime(); + + void SetIP(const char * c_pszIP); + const char * GetIP(); + + void SetPlay(bool bOn); + bool IsPlay(); + + void SetDeleted(bool bSet); + bool IsDeleted(); + + void SetBillID(DWORD id) { m_dwBillID = id; } + DWORD GetBillID() { return m_dwBillID; } + + void SetBillType(BYTE type) { m_bBillType = type; } + BYTE GetBillType() { return m_bBillType; } + + time_t GetLastPlayTime() { return m_lastPlayTime; } + + void SetPremium(int * paiPremiumTimes); + int GetPremium(BYTE type); + int * GetPremiumPtr(); + + DWORD GetLastPlayerID() const { return m_dwLastPlayerID; } + void SetLastPlayerID(DWORD id) { m_dwLastPlayerID = id; } + + private: + DWORD m_dwKey; + DWORD m_adwClientKey[4]; + DWORD m_dwConnectedPeerHandle; + DWORD m_dwLogonTime; + char m_szIP[MAX_HOST_LENGTH+1]; + bool m_bPlay; + bool m_bDeleted; + + BYTE m_bBillType; + DWORD m_dwBillID; + time_t m_lastPlayTime; + int m_aiPremiumTimes[PREMIUM_MAX_NUM]; + + DWORD m_dwLastPlayerID; + + TAccountTable m_data; +}; + +#endif diff --git a/db/src/Main.cpp b/db/src/Main.cpp new file mode 100644 index 0000000..461782e --- /dev/null +++ b/db/src/Main.cpp @@ -0,0 +1,430 @@ +#include "stdafx.h" +#include "Config.h" +#include "Peer.h" +#include "DBManager.h" +#include "ClientManager.h" +#include "GuildManager.h" +#include "ItemAwardManager.h" +#include "HB.h" +#include "PrivManager.h" +#include "MoneyLog.h" +#include "Marriage.h" +#include "Monarch.h" +#include "BlockCountry.h" +#include "ItemIDRangeManager.h" +#ifdef __AUCTION__ +#include "AuctionManager.h" +#endif +#include + +void SetPlayerDBName(const char* c_pszPlayerDBName); +void SetTablePostfix(const char* c_pszTablePostfix); +int Start(); + +std::string g_stTablePostfix; +std::string g_stLocaleNameColumn = "name"; +std::string g_stLocale = "euckr"; +std::string g_stPlayerDBName = ""; + + +bool g_bHotBackup = false; +BOOL g_test_server = false; + +// +int g_iPlayerCacheFlushSeconds = 60*7; +int g_iItemCacheFlushSeconds = 60*5; + +//g_iLogoutSeconds ġ g_iPlayerCacheFlushSeconds g_iItemCacheFlushSeconds Ѵ. +int g_iLogoutSeconds = 60*10; + +int g_log = 1; + + +// MYSHOP_PRICE_LIST +int g_iItemPriceListTableCacheFlushSeconds = 540; +// END_OF_MYSHOP_PRICE_LIST + +#ifdef __FreeBSD__ +extern const char * _malloc_options; +#endif + +extern void WriteVersion(); + +void emergency_sig(int sig) +{ + if (sig == SIGSEGV) + sys_log(0, "SIGNAL: SIGSEGV"); + else if (sig == SIGUSR1) + sys_log(0, "SIGNAL: SIGUSR1"); + + if (sig == SIGSEGV) + abort(); +} + +int main() +{ + WriteVersion(); + +#ifdef __FreeBSD__ + _malloc_options = "A"; +#endif + + CConfig Config; + CNetPoller poller; + CDBManager DBManager; + CClientManager ClientManager; + PlayerHB player_hb; + CGuildManager GuildManager; + CPrivManager PrivManager; + CMoneyLog MoneyLog; + ItemAwardManager ItemAwardManager; + marriage::CManager MarriageManager; + CMonarch Monarch; + CBlockCountry BlockCountry; + CItemIDRangeManager ItemIDRangeManager; +#ifdef __AUCTION__ + AuctionManager auctionManager; +#endif + if (!Start()) + return 1; + + GuildManager.Initialize(); + MarriageManager.Initialize(); + BlockCountry.Load(); + ItemIDRangeManager.Build(); +#ifdef __AUCTION__ + AuctionManager::instance().Initialize(); +#endif + sys_log(0, "Metin2DBCacheServer Start\n"); + + CClientManager::instance().MainLoop(); + + signal_timer_disable(); + + DBManager.Quit(); + int iCount; + + while (1) + { + iCount = 0; + + iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER); + iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER); + + if (iCount == 0) + break; + + usleep(1000); + sys_log(0, "WAITING_QUERY_COUNT %d", iCount); + } + + return 1; +} + +void emptybeat(LPHEART heart, int pulse) +{ + if (!(pulse % heart->passes_per_sec)) // 1ʿ ѹ + { + } +} + +// +// @version 05/06/13 Bang2ni - ij flush timeout ߰. +// +int Start() +{ + if (!CConfig::instance().LoadFile("conf.txt")) + { + fprintf(stderr, "Loading conf.txt failed.\n"); + return false; + } + + if (!CConfig::instance().GetValue("TEST_SERVER", &g_test_server)) + { + fprintf(stderr, "Real Server\n"); + } + else + fprintf(stderr, "Test Server\n"); + + if (!CConfig::instance().GetValue("LOG", &g_log)) + { + fprintf(stderr, "Log Off"); + g_log= 0; + } + else + { + g_log = 1; + fprintf(stderr, "Log On"); + } + + + int tmpValue; + + int heart_beat = 50; + if (!CConfig::instance().GetValue("CLIENT_HEART_FPS", &heart_beat)) + { + fprintf(stderr, "Cannot find CLIENT_HEART_FPS configuration.\n"); + return false; + } + + log_set_expiration_days(3); + + if (CConfig::instance().GetValue("LOG_KEEP_DAYS", &tmpValue)) + { + tmpValue = MINMAX(3, tmpValue, 30); + log_set_expiration_days(tmpValue); + fprintf(stderr, "Setting log keeping days to %d\n", tmpValue); + } + + thecore_init(heart_beat, emptybeat); + signal_timer_enable(60); + + char szBuf[256+1]; + + if (CConfig::instance().GetValue("LOCALE", szBuf, 256)) + { + g_stLocale = szBuf; + sys_log(0, "LOCALE set to %s", g_stLocale.c_str()); + + // CHINA_DISABLE_HOTBACKUP + if ("gb2312" == g_stLocale) + { + sys_log(0, "CIBN_LOCALE: DISABLE_HOTBACKUP"); + g_bHotBackup = false; + } + // END_OF_CHINA_DISABLE_HOTBACKUP + } + + int iDisableHotBackup; + if (CConfig::instance().GetValue("DISABLE_HOTBACKUP", &iDisableHotBackup)) + { + if (iDisableHotBackup) + { + sys_log(0, "CONFIG: DISABLE_HOTBACKUP"); + g_bHotBackup = false; + } + } + + + if (!CConfig::instance().GetValue("TABLE_POSTFIX", szBuf, 256)) + { + sys_err("TABLE_POSTFIX not configured use default"); + szBuf[0] = '\0'; + } + + SetTablePostfix(szBuf); + + if (CConfig::instance().GetValue("PLAYER_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iPlayerCacheFlushSeconds, szBuf); + sys_log(0, "PLAYER_CACHE_FLUSH_SECONDS: %d", g_iPlayerCacheFlushSeconds); + } + + if (CConfig::instance().GetValue("ITEM_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iItemCacheFlushSeconds, szBuf); + sys_log(0, "ITEM_CACHE_FLUSH_SECONDS: %d", g_iItemCacheFlushSeconds); + } + + // MYSHOP_PRICE_LIST + if (CConfig::instance().GetValue("ITEM_PRICELIST_CACHE_FLUSH_SECONDS", szBuf, 256)) + { + str_to_number(g_iItemPriceListTableCacheFlushSeconds, szBuf); + sys_log(0, "ITEM_PRICELIST_CACHE_FLUSH_SECONDS: %d", g_iItemPriceListTableCacheFlushSeconds); + } + // END_OF_MYSHOP_PRICE_LIST + // + if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256)) + { + DWORD dwVal = 0; str_to_number(dwVal, szBuf); + CClientManager::instance().SetCacheFlushCountLimit(dwVal); + } + + int iIDStart; + if (!CConfig::instance().GetValue("PLAYER_ID_START", &iIDStart)) + { + sys_err("PLAYER_ID_START not configured"); + return false; + } + + CClientManager::instance().SetPlayerIDStart(iIDStart); + + if (CConfig::instance().GetValue("NAME_COLUMN", szBuf, 256)) + { + fprintf(stderr, "%s %s", g_stLocaleNameColumn.c_str(), szBuf); + g_stLocaleNameColumn = szBuf; + } + + char szAddr[64], szDB[64], szUser[64], szPassword[64]; + int iPort; + char line[256+1]; + + if (CConfig::instance().GetValue("SQL_PLAYER", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (player)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_PLAYER, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success PLAYER\n"); + SetPlayerDBName(szDB); + } + else + { + sys_err("SQL_PLAYER not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_ACCOUNT", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (account)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_ACCOUNT, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success ACCOUNT\n"); + } + else + { + sys_err("SQL_ACCOUNT not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_COMMON", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (common)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } while (iRetry--); + fprintf(stderr, "Success COMMON\n"); + } + else + { + sys_err("SQL_COMMON not configured"); + return false; + } + + if (CConfig::instance().GetValue("SQL_HOTBACKUP", line, 256)) + { + sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); + sys_log(0, "connecting to MySQL server (hotbackup)"); + + int iRetry = 5; + + do + { + if (CDBManager::instance().Connect(SQL_HOTBACKUP, szAddr, iPort, szDB, szUser, szPassword)) + { + sys_log(0, " OK"); + break; + } + + sys_log(0, " failed, retrying in 5 seconds"); + fprintf(stderr, " failed, retrying in 5 seconds"); + sleep(5); + } + while (iRetry--); + + fprintf(stderr, "Success HOTBACKUP\n"); + } + else + { + sys_err("SQL_HOTBACKUP not configured"); + return false; + } + + if (!CNetPoller::instance().Create()) + { + sys_err("Cannot create network poller"); + return false; + } + + sys_log(0, "ClientManager initialization.. "); + + if (!CClientManager::instance().Initialize()) + { + sys_log(0, " failed"); + return false; + } + + sys_log(0, " OK"); + + if (!PlayerHB::instance().Initialize()) + { + sys_err("cannot initialize player hotbackup"); + return false; + } + +#ifndef __WIN32__ + signal(SIGUSR1, emergency_sig); +#endif + signal(SIGSEGV, emergency_sig); + return true; +} + +void SetTablePostfix(const char* c_pszTablePostfix) +{ + if (!c_pszTablePostfix || !*c_pszTablePostfix) + g_stTablePostfix = ""; + else + g_stTablePostfix = c_pszTablePostfix; +} + +const char * GetTablePostfix() +{ + return g_stTablePostfix.c_str(); +} + +void SetPlayerDBName(const char* c_pszPlayerDBName) +{ + if (! c_pszPlayerDBName || ! *c_pszPlayerDBName) + g_stPlayerDBName = ""; + else + { + g_stPlayerDBName = c_pszPlayerDBName; + g_stPlayerDBName += "."; + } +} + +const char * GetPlayerDBName() +{ + return g_stPlayerDBName.c_str(); +} + diff --git a/db/src/Main.h b/db/src/Main.h new file mode 100644 index 0000000..8d489b4 --- /dev/null +++ b/db/src/Main.h @@ -0,0 +1,9 @@ +#ifndef __INC_MAIN_H__ +#define __INC_MAIN_H__ + +int Start(); +void End(); +const char * GetTablePostfix(); +const char * GetPlayerDBName(); + +#endif diff --git a/db/src/Marriage.cpp b/db/src/Marriage.cpp new file mode 100644 index 0000000..ef429f6 --- /dev/null +++ b/db/src/Marriage.cpp @@ -0,0 +1,382 @@ +#include "stdafx.h" +#include "Marriage.h" +#include "Main.h" +#include "DBManager.h" +#include "ClientManager.h" + +namespace marriage +{ + const DWORD WEDDING_LENGTH = 60 * 60; // sec + bool operator < (const TWedding& lhs, const TWedding& rhs) + { + return lhs.dwTime < rhs.dwTime; + } + + bool operator > (const TWedding& lhs, const TWedding& rhs) + { + return lhs.dwTime > rhs.dwTime; + } + + bool operator > (const TWeddingInfo &lhs, const TWeddingInfo& rhs) + { + return lhs.dwTime > rhs.dwTime; + } + + using namespace std; + + CManager::CManager() + { + } + + CManager::~CManager() + { + } + + bool CManager::Initialize() + { + char szQuery[1024]; + + snprintf(szQuery, sizeof(szQuery), + "SELECT pid1, pid2, love_point, time, is_married, p1.name, p2.name FROM marriage, player%s as p1, player%s as p2 WHERE p1.id = pid1 AND p2.id = pid2", + GetTablePostfix(), GetTablePostfix()); + + auto_ptr pmsg_delete(CDBManager::instance().DirectQuery("DELETE FROM marriage WHERE is_married = 0")); + auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult * pRes = pmsg->Get(); + sys_log(0, "MarriageList(size=%lu)", pRes->uiNumRows); + + if (pRes->uiNumRows > 0) + { + for (uint uiRow = 0; uiRow != pRes->uiNumRows; ++uiRow) + { + MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); + + DWORD pid1 = 0; str_to_number(pid1, row[0]); + DWORD pid2 = 0; str_to_number(pid2, row[1]); + int love_point = 0; str_to_number(love_point, row[2]); + DWORD time = 0; str_to_number(time, row[3]); + BYTE is_married = 0; str_to_number(is_married, row[4]); + const char* name1 = row[5]; + const char* name2 = row[6]; + + TMarriage* pMarriage = new TMarriage(pid1, pid2, love_point, time, is_married, name1, name2); + m_Marriages.insert(pMarriage); + m_MarriageByPID.insert(make_pair(pid1, pMarriage)); + m_MarriageByPID.insert(make_pair(pid2, pMarriage)); + + sys_log(0, "Marriage %lu: LP:%d TM:%u ST:%d %10lu:%16s %10lu:%16s ", uiRow, love_point, time, is_married, pid1, name1, pid2, name2); + } + } + return true; + } + + TMarriage* CManager::Get(DWORD dwPlayerID) + { + itertype(m_MarriageByPID) it = m_MarriageByPID.find(dwPlayerID); + + if (it != m_MarriageByPID.end()) + return it->second; + + return NULL; + } + + void Align(DWORD& rPID1, DWORD& rPID2) + { + if (rPID1 > rPID2) + std::swap(rPID1, rPID2); + } + + void CManager::Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2) + { + DWORD now = CClientManager::instance().GetCurrentTime(); + if (IsMarried(dwPID1) || IsMarried(dwPID2)) + { + sys_err("cannot marry already married character. %d - %d", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "INSERT INTO marriage(pid1, pid2, love_point, time) VALUES (%u, %u, 0, %u)", dwPID1, dwPID2, now); + + auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot insert marriage"); + return; + } + + sys_log(0, "MARRIAGE ADD %u %u", dwPID1, dwPID2); + + TMarriage* pMarriage = new TMarriage(dwPID1, dwPID2, 0, now, 0, szName1, szName2); + m_Marriages.insert(pMarriage); + m_MarriageByPID.insert(make_pair(dwPID1, pMarriage)); + m_MarriageByPID.insert(make_pair(dwPID2, pMarriage)); + + TPacketMarriageAdd p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.tMarryTime = now; + strlcpy(p.szName1, szName1, sizeof(p.szName1)); + strlcpy(p.szName2, szName2, sizeof(p.szName2)); + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_ADD, &p, sizeof(p)); + } + + void CManager::Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried) + { + TMarriage* pMarriage = Get(dwPID1); + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + sys_err("not under marriage : %u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET love_point = %d, is_married = %d WHERE pid1 = %u AND pid2 = %u", + iLovePoint, byMarried, pMarriage->pid1, pMarriage->pid2); + + auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot update marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + sys_log(0, "MARRIAGE UPDATE PID:%u %u LP:%u ST:%d", dwPID1, dwPID2, iLovePoint, byMarried); + pMarriage->love_point = iLovePoint; + pMarriage->is_married = byMarried; + + TPacketMarriageUpdate p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p)); + } + + void CManager::Remove(DWORD dwPID1, DWORD dwPID2) + { + TMarriage* pMarriage = Get(dwPID1); + + if (pMarriage) + { + sys_log(0, "Break Marriage pid1 %d pid2 %d Other %d", dwPID1, dwPID2, pMarriage->GetOther(dwPID1)); + } + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + itertype(m_MarriageByPID) it = m_MarriageByPID.begin(); + + for (; it != m_MarriageByPID.end(); ++it) + { + sys_log(0, "Marriage List pid1 %d pid2 %d", it->second->pid1, it->second->pid2); + } + sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "DELETE FROM marriage WHERE pid1 = %u AND pid2 = %u", dwPID1, dwPID2); + + auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot delete marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + sys_log(0, "MARRIAGE REMOVE PID:%u %u", dwPID1, dwPID2); + + m_Marriages.erase(pMarriage); + m_MarriageByPID.erase(dwPID1); + m_MarriageByPID.erase(dwPID2); + + TPacketMarriageRemove p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_REMOVE, &p, sizeof(p)); + + delete pMarriage; + } + + void CManager::EngageToMarriage(DWORD dwPID1, DWORD dwPID2) + { + TMarriage* pMarriage = Get(dwPID1); + if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2) + { + sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + if (pMarriage->is_married) + { + sys_err("already married, cannot change engage to marry : PID:%u %u", dwPID1, dwPID2); + return; + } + + Align(dwPID1, dwPID2); + + char szQuery[512]; + snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET is_married = 1 WHERE pid1 = %u AND pid2 = %u", + pMarriage->pid1, pMarriage->pid2); + + auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); + + SQLResult* res = pmsg->Get(); + if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1) + { + sys_err("cannot change engage to marriage : PID:%u %u", dwPID1, dwPID2); + return; + } + + sys_log(0, "MARRIAGE ENGAGE->MARRIAGE PID:%u %u", dwPID1, dwPID2); + pMarriage->is_married = 1; + + TPacketMarriageUpdate p; + p.dwPID1 = dwPID1; + p.dwPID2 = dwPID2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p)); + } + + void CManager::OnSetup(CPeer* peer) + { + // ȥ + for (itertype(m_Marriages) it = m_Marriages.begin(); it != m_Marriages.end(); ++it) + { + TMarriage* pMarriage = *it; + + { + TPacketMarriageAdd p; + p.dwPID1 = pMarriage->pid1; + p.dwPID2 = pMarriage->pid2; + p.tMarryTime = pMarriage->time; + strlcpy(p.szName1, pMarriage->name1.c_str(), sizeof(p.szName1)); + strlcpy(p.szName2, pMarriage->name2.c_str(), sizeof(p.szName2)); + peer->EncodeHeader(HEADER_DG_MARRIAGE_ADD, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + + { + TPacketMarriageUpdate p; + p.dwPID1 = pMarriage->pid1; + p.dwPID2 = pMarriage->pid2; + p.iLovePoint = pMarriage->love_point; + p.byMarried = pMarriage->is_married; + peer->EncodeHeader(HEADER_DG_MARRIAGE_UPDATE, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + } + + // ȥ + for (itertype(m_mapRunningWedding) it = m_mapRunningWedding.begin(); it != m_mapRunningWedding.end(); ++it) + { + const TWedding& t = it->second; + + TPacketWeddingReady p; + p.dwPID1 = t.dwPID1; + p.dwPID2 = t.dwPID2; + p.dwMapIndex = t.dwMapIndex; + + peer->EncodeHeader(HEADER_DG_WEDDING_READY, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + + TPacketWeddingStart p2; + p2.dwPID1 = t.dwPID1; + p2.dwPID2 = t.dwPID2; + + peer->EncodeHeader(HEADER_DG_WEDDING_START, 0, sizeof(p2)); + peer->Encode(&p2, sizeof(p2)); + } + } + + void CManager::ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2) + { + DWORD dwStartTime = CClientManager::instance().GetCurrentTime(); + m_pqWeddingStart.push(TWedding(dwStartTime + 5, dwMapIndex, dwPID1, dwPID2)); + } + + void CManager::EndWedding(DWORD dwPID1, DWORD dwPID2) + { + itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(dwPID1, dwPID2)); + if (it == m_mapRunningWedding.end()) + { + sys_err("try to end wedding %u %u", dwPID1, dwPID2); + return; + } + + TWedding& w = it->second; + + TPacketWeddingEnd p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p)); + m_mapRunningWedding.erase(it); + } + + void CManager::Update() + { + DWORD now = CClientManager::instance().GetCurrentTime(); + + if (!m_pqWeddingEnd.empty()) + { + while (!m_pqWeddingEnd.empty() && m_pqWeddingEnd.top().dwTime <= now) + { + TWeddingInfo wi = m_pqWeddingEnd.top(); + m_pqWeddingEnd.pop(); + + itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(wi.dwPID1, wi.dwPID2)); + if (it == m_mapRunningWedding.end()) + continue; + + TWedding& w = it->second; + + TPacketWeddingEnd p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p)); + m_mapRunningWedding.erase(it); + + itertype(m_MarriageByPID) it_marriage = m_MarriageByPID.find(w.dwPID1); + + if (it_marriage != m_MarriageByPID.end()) + { + TMarriage* pMarriage = it_marriage->second; + if (!pMarriage->is_married) + { + Remove(pMarriage->pid1, pMarriage->pid2); + } + } + } + } + if (!m_pqWeddingStart.empty()) + { + while (!m_pqWeddingStart.empty() && m_pqWeddingStart.top().dwTime <= now) + { + TWedding w = m_pqWeddingStart.top(); + m_pqWeddingStart.pop(); + + TPacketWeddingStart p; + p.dwPID1 = w.dwPID1; + p.dwPID2 = w.dwPID2; + CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_START, &p, sizeof(p)); + + w.dwTime += WEDDING_LENGTH; + m_pqWeddingEnd.push(TWeddingInfo(w.dwTime, w.dwPID1, w.dwPID2)); + m_mapRunningWedding.insert(make_pair(make_pair(w.dwPID1, w.dwPID2), w)); + } + } + } +} diff --git a/db/src/Marriage.h b/db/src/Marriage.h new file mode 100644 index 0000000..6758c8a --- /dev/null +++ b/db/src/Marriage.h @@ -0,0 +1,113 @@ +// vim: ts=4 sw=4 +#ifndef __MARRIAGE_H +#define __MARRIAGE_H + +#include +#include +#include + +#include "Peer.h" + +namespace marriage +{ + struct TWeddingInfo + { + DWORD dwTime; + DWORD dwPID1; + DWORD dwPID2; + TWeddingInfo(DWORD time, DWORD pid1, DWORD pid2) + : dwTime(time), + dwPID1(pid1), + dwPID2(pid2) + { + } + }; + + struct TWedding + { + DWORD dwTime; + DWORD dwMapIndex; + DWORD dwPID1; + DWORD dwPID2; + + TWedding(DWORD time, DWORD dwMapIndex, DWORD pid1, DWORD pid2) + : dwTime(time), + dwMapIndex(dwMapIndex), + dwPID1(pid1), + dwPID2(pid2) + { + } + }; + + extern bool operator < (const TWedding& lhs, const TWedding& rhs); + extern bool operator > (const TWedding& lhs, const TWedding& rhs); + extern bool operator > (const TWeddingInfo& lhs, const TWeddingInfo& rhs); + + struct TMarriage + { + DWORD pid1; + DWORD pid2; + int love_point; + DWORD time; + BYTE is_married; // false : ȥ , true : ȥ + std::string name1; + std::string name2; + + TMarriage(DWORD _pid1, DWORD _pid2, int _love_point, DWORD _time, BYTE _is_married, const char* name1, const char* name2) + : pid1(_pid1), pid2(_pid2), love_point(_love_point), time(_time), is_married(_is_married), name1(name1), name2(name2) + { + } + + DWORD GetOther(DWORD PID) + { + if (pid1 == PID) + return pid2; + + if (pid2 == PID) + return pid1; + + return 0; + } + }; + + class CManager : public singleton + { + public: + CManager(); + virtual ~CManager(); + + bool Initialize(); + + TMarriage* Get(DWORD dwPlayerID); + bool IsMarried(DWORD dwPlayerID) + { + return Get(dwPlayerID) != NULL; + } + + //void Reserve(DWORD dwPID1, DWORD dwPID2); + void Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2); + void Remove(DWORD dwPID1, DWORD dwPID2); + void Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried); + + void EngageToMarriage(DWORD dwPID1, DWORD dwPID2); + + void ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2); + void EndWedding(DWORD dwPID1, DWORD dwPID2); + + void OnSetup(CPeer* peer); + + void Update(); + + private: + std::set m_Marriages; + std::map m_MarriageByPID; + + std::priority_queue, std::greater > m_pqWeddingStart; + + std::priority_queue, std::greater > m_pqWeddingEnd; + + std::map, TWedding> m_mapRunningWedding; + }; +} + +#endif diff --git a/db/src/Monarch.cpp b/db/src/Monarch.cpp new file mode 100644 index 0000000..1eace36 --- /dev/null +++ b/db/src/Monarch.cpp @@ -0,0 +1,309 @@ +#include "Monarch.h" +#include "../../common/utils.h" +#include "Main.h" +#include "ClientManager.h" + +extern int g_test_server; + +CMonarch::CMonarch() +{ + memset(&m_MonarchInfo, 0, sizeof(MonarchInfo)); +} + +CMonarch::~CMonarch() +{ +} + +bool CMonarch::VoteMonarch(DWORD pid, DWORD selectdpid) +{ + MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid); + + if (it == m_map_MonarchElection.end()) + { + MonarchElectionInfo * p = new MonarchElectionInfo; + p->pid = pid; + p->selectedpid= selectdpid; + m_map_MonarchElection.insert(MAP_MONARCHELECTION::value_type(pid, p)); + + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO monarch_election(pid, selectedpid, electiondata) VALUES(%d, %d, now())", pid, selectdpid); + + CDBManager::instance().AsyncQuery(szQuery); + return 1; + } + + return 0; +} + +void CMonarch::ElectMonarch() +{ + int size = GetVecMonarchCandidacy().size(); + + int * s = new int[size]; + + itertype(m_map_MonarchElection) it = m_map_MonarchElection.begin(); + + int idx = 0; + + for (; it != m_map_MonarchElection.end(); ++it) + { + if ((idx = GetCandidacyIndex(it->second->pid)) < 0) + continue; + + ++s[idx]; + + if (g_test_server) + sys_log (0, "[MONARCH_VOTE] pid(%d) come to vote candidacy pid(%d)", it->second->pid, m_vec_MonarchCandidacy[idx].pid); + } + + delete [] s; +} + +bool CMonarch::IsCandidacy(DWORD pid) +{ + VEC_MONARCHCANDIDACY::iterator it = m_vec_MonarchCandidacy.begin(); + + for (; it != m_vec_MonarchCandidacy.end(); ++it) + { + if (it->pid == pid) + return false; + } + + return true; +} + +bool CMonarch::AddCandidacy(DWORD pid, const char * name) +{ + if (IsCandidacy(pid) == false) + return false; + + MonarchCandidacy info; + + info.pid = pid; + strlcpy(info.name, name, sizeof(info.name)); + m_vec_MonarchCandidacy.push_back(info); + + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "INSERT INTO monarch_candidacy(pid, date) VALUES(%d, now())", pid); + + CDBManager::instance().AsyncQuery(szQuery); + return true; +} + +bool CMonarch::DelCandidacy(const char * name) +{ + itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin(); + for (; it != m_vec_MonarchCandidacy.end(); ++it) + { + if (0 == strncmp(it->name, name, sizeof(it->name))) + { + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), + "DELETE FROM monarch_candidacy WHERE pid=%d ", it->pid); + CDBManager::instance().AsyncQuery(szQuery); + + m_vec_MonarchCandidacy.erase (it); + return true; + } + } + return false; + +} + +bool CMonarch::IsMonarch(int Empire, DWORD pid) +{ + if (m_MonarchInfo.pid[Empire] != pid) + return false; + return true; +} + +bool CMonarch::AddMoney(int Empire, int64_t Money) +{ + if (m_MonarchInfo.money[Empire] + Money > 2000000000) + return true; + + m_MonarchInfo.money[Empire] += Money; + + int64_t Money64 = m_MonarchInfo.money[Empire]; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%lld where empire=%d", Money64, Empire); + + CDBManager::instance().AsyncQuery(szQuery); + + return true; +} + +bool CMonarch::DecMoney(int Empire, int64_t Money) +{ + if (m_MonarchInfo.money[Empire] - Money < 0) + return false; + m_MonarchInfo.money[Empire] -= Money; + + int64_t Money64 = m_MonarchInfo.money[Empire]; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%lld where empire=%d", Money64, Empire); + + CDBManager::instance().AsyncQuery(szQuery); + return true; +} + +bool CMonarch::TakeMoney(int Empire, DWORD pid, int64_t Money) +{ + if (IsMonarch(Empire, pid) == false) + return false; + + if (m_MonarchInfo.money[Empire] < Money) + return false; + + m_MonarchInfo.money[Empire] -= Money; + + char szQuery[1024]; + snprintf(szQuery, sizeof(szQuery), + "UPDATE monarch set money=%lld; where empire=%d", m_MonarchInfo.money[Empire], Empire); + + CDBManager::instance().AsyncQuery(szQuery); + + if (g_test_server) + sys_log(0, "[MONARCH] Take money empire(%d) money(%lld)", Empire, m_MonarchInfo.money[Empire]); + return true; +} + +bool CMonarch::LoadMonarch() +{ + MonarchInfo * p = &m_MonarchInfo; + char szQuery[256]; + snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name, money, windate FROM monarch a, player%s b where a.pid=b.id", GetTablePostfix()); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows == 0) + { + delete pMsg; + return false; + } + + MYSQL_ROW row; + for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + { + int idx = 0; + int Empire = 0; str_to_number(Empire, row[idx++]); + + str_to_number(p->pid[Empire], row[idx++]); + strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); + + str_to_number(p->money[Empire], row[idx++]); + strlcpy(p->date[Empire], row[idx++], sizeof(p->date[Empire])); + + if (g_test_server) + sys_log(0, "[LOAD_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); + } + + delete pMsg; + return true; +} + +bool CMonarch::SetMonarch(const char * name) +{ + MonarchInfo * p = &m_MonarchInfo; + char szQuery[256]; + + snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name FROM player a where a.name = '%s'", name); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows == 0) + { + delete pMsg; + return false; + } + + MYSQL_ROW row; + int Empire = 0; + for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + { + int idx = 0; + str_to_number(Empire, row[idx++]); + + str_to_number(p->pid[Empire], row[idx++]); + strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); + p->money[Empire] = atoll(row[idx++]); + + if (g_test_server) + sys_log(0, "[Set_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); + } + delete pMsg; + + //db Է + snprintf(szQuery, sizeof(szQuery), + "REPLACE INTO monarch (empire, name, windate, money) VALUES(%d, %d, now(), %lld)", Empire, p->pid[Empire], p->money[Empire]); + + CDBManager::instance().AsyncQuery(szQuery, SQL_PLAYER); + return true; +} + +bool CMonarch::DelMonarch(int Empire) +{ + char szQuery[256]; + + snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where empire=%d", Empire); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows == 0) + { + delete pMsg; + return false; + } + + delete pMsg; + + memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire])); + m_MonarchInfo.money[Empire] = 0; + m_MonarchInfo.pid[Empire] = 0; + return true; +} + +bool CMonarch::DelMonarch(const char * name) +{ + for (int n = 1; n < 4; ++n) + { + if (0 == strncmp(m_MonarchInfo.name[n], name, sizeof(m_MonarchInfo.name[n]))) + { + char szQuery[256]; + + int Empire = n; + snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where name=%d", Empire); + SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); + + if (pMsg->Get()->uiNumRows == 0) + { + sys_err(" DirectQuery failed(%s)", szQuery); + delete pMsg; + return false; + } + + delete pMsg; + + memset(m_MonarchInfo.name[Empire], 0, 32); + m_MonarchInfo.money[Empire] = 0; + m_MonarchInfo.pid[Empire] = 0; + return true; + } + } + + return false; +} + +int CMonarch::GetCandidacyIndex(DWORD pid) +{ + itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin(); + + for (int n = 0; it != m_vec_MonarchCandidacy.end(); ++it, ++n) + { + if (it->pid == pid) + return n; + } + + return -1; +} diff --git a/db/src/Monarch.h b/db/src/Monarch.h new file mode 100644 index 0000000..81e7a6e --- /dev/null +++ b/db/src/Monarch.h @@ -0,0 +1,70 @@ +// vim: ts=4 sw=4 +#ifndef METIN2_MONARCH_H +#define METIN2_MONARCH_H + +#include "../../libthecore/include/stdafx.h" +#include +#include +#include "../../common/singleton.h" +#include "../../common/tables.h" + +class CMonarch : public singleton +{ + public: + typedef std::map MAP_MONARCHELECTION; + typedef std::vector VEC_MONARCHCANDIDACY; + + CMonarch(); + virtual ~CMonarch(); + + bool VoteMonarch(DWORD pid, DWORD selectedpid); + void ElectMonarch(); + + bool IsCandidacy(DWORD pid); + bool AddCandidacy(DWORD pid, const char * name); + bool DelCandidacy(const char * name); + + bool LoadMonarch(); + bool SetMonarch(const char * name); + bool DelMonarch(int Empire); + bool DelMonarch(const char * name); + + bool IsMonarch(int Empire, DWORD pid); + bool AddMoney(int Empire, int64_t Money); + bool DecMoney(int Empire, int64_t Money); + bool TakeMoney(int Empire, DWORD pid, int64_t Money); + + TMonarchInfo* GetMonarch() + { + return &m_MonarchInfo; + } + + VEC_MONARCHCANDIDACY& GetVecMonarchCandidacy() + { + return m_vec_MonarchCandidacy; + } + + size_t MonarchCandidacySize() + { + return m_vec_MonarchCandidacy.size(); + } + + private: + int GetCandidacyIndex(DWORD pid); + + MAP_MONARCHELECTION m_map_MonarchElection; + VEC_MONARCHCANDIDACY m_vec_MonarchCandidacy; + TMonarchInfo m_MonarchInfo; + + MonarchElectionInfo* GetMonarchElection(DWORD pid) + { + MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid); + + if (it != m_map_MonarchElection.end()) + return it->second; + + return NULL; + } +}; + +#endif diff --git a/db/src/MoneyLog.cpp b/db/src/MoneyLog.cpp new file mode 100644 index 0000000..8f5dd09 --- /dev/null +++ b/db/src/MoneyLog.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include "MoneyLog.h" +#include "ClientManager.h" +#include "Peer.h" + +CMoneyLog::CMoneyLog() +{ +} + +CMoneyLog::~CMoneyLog() +{ +} + +void CMoneyLog::Save() +{ + CPeer* peer = CClientManager::instance().GetAnyPeer(); + if (!peer) + return; + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType ++) + { + typeof(m_MoneyLogContainer[bType].begin()) it; + for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it) + { + //bType; + TPacketMoneyLog p; + p.type = bType; + p.vnum = it->first; + p.gold = it->second; + peer->EncodeHeader(HEADER_DG_MONEY_LOG, 0, sizeof(p)); + peer->Encode(&p, sizeof(p)); + } + m_MoneyLogContainer[bType].clear(); + } + /* + CPeer* peer = GetPeer(); + + peer-> + + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++) + { + //"INSERT INTO money_log%s VALUES('%s', %d, %d, %d)", CClientManager::instance().GetTablePostfix(), + typeof(m_MoneyLogContainer[bType].begin()) it; + for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it) + { + typeof(it->second.begin()) + } + } + + for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++) + m_MoneyLogContainer[bType].clear() + */ +} + +void CMoneyLog::AddLog(BYTE bType, DWORD dwVnum, int iGold) +{ + m_MoneyLogContainer[bType][dwVnum] += iGold; +} diff --git a/db/src/MoneyLog.h b/db/src/MoneyLog.h new file mode 100644 index 0000000..76e316c --- /dev/null +++ b/db/src/MoneyLog.h @@ -0,0 +1,20 @@ +// vim: ts=8 sw=4 +#ifndef __INC_MONEY_LOG +#define __INC_MONEY_LOG + +#include + +class CMoneyLog : public singleton +{ + public: + CMoneyLog(); + virtual ~CMoneyLog(); + + void Save(); + void AddLog(BYTE bType, DWORD dwVnum, int iGold); + + private: + std::map m_MoneyLogContainer[MONEY_LOG_TYPE_MAX_NUM]; +}; + +#endif diff --git a/db/src/NetBase.cpp b/db/src/NetBase.cpp new file mode 100644 index 0000000..34874d4 --- /dev/null +++ b/db/src/NetBase.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "NetBase.h" +#include "Config.h" +#include "ClientManager.h" + +LPFDWATCH CNetBase::m_fdWatcher = NULL; + +CNetBase::CNetBase() +{ +} + +CNetBase::~CNetBase() +{ +} + +CNetPoller::CNetPoller() +{ +} + +CNetPoller::~CNetPoller() +{ + Destroy(); +} + +bool CNetPoller::Create() +{ + sys_log(1, "NetPoller::Create()"); + + if (m_fdWatcher) + return true; + + m_fdWatcher = fdwatch_new(512); + + if (!m_fdWatcher) + { + Destroy(); + return false; + } + + return true; +} + +void CNetPoller::Destroy() +{ + sys_log(1, "NetPoller::Destroy()"); + + if (m_fdWatcher) + { + fdwatch_delete(m_fdWatcher); + m_fdWatcher = NULL; + } + + thecore_destroy(); +} diff --git a/db/src/NetBase.h b/db/src/NetBase.h new file mode 100644 index 0000000..00b1f84 --- /dev/null +++ b/db/src/NetBase.h @@ -0,0 +1,25 @@ +// vim: ts=8 sw=4 +#ifndef __INC_NETWORKBASE_H__ +#define __INC_NETWORKBASE_H__ + +class CNetBase +{ + public: + CNetBase(); + virtual ~CNetBase(); + + protected: + static LPFDWATCH m_fdWatcher; +}; + +class CNetPoller : public CNetBase, public singleton +{ + public: + CNetPoller(); + virtual ~CNetPoller(); + + bool Create(); + void Destroy(); +}; + +#endif diff --git a/db/src/Peer.cpp b/db/src/Peer.cpp new file mode 100644 index 0000000..f71b9ef --- /dev/null +++ b/db/src/Peer.cpp @@ -0,0 +1,197 @@ +#include "stdafx.h" +#include "Peer.h" +#include "ItemIDRangeManager.h" + +CPeer::CPeer() +{ + m_state = 0; + m_bChannel = 0; + m_dwHandle = 0; + m_dwUserCount = 0; + m_wListenPort = 0; + m_wP2PPort = 0; + + memset(m_alMaps, 0, sizeof(m_alMaps)); + + m_itemRange.dwMin = m_itemRange.dwMax = m_itemRange.dwUsableItemIDMin = 0; + m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0; +} + +CPeer::~CPeer() +{ + Close(); +} + +void CPeer::OnAccept() +{ + m_state = STATE_PLAYING; + + static DWORD current_handle = 0; + m_dwHandle = ++current_handle; + + sys_log(0, "Connection accepted. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd); +} + +void CPeer::OnConnect() +{ + sys_log(0, "Connection established. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd); + m_state = STATE_PLAYING; +} + +void CPeer::OnClose() +{ + m_state = STATE_CLOSE; + + sys_log(0, "Connection closed. (host: %s)", m_host); + sys_log(0, "ItemIDRange: returned. %u ~ %u", m_itemRange.dwMin, m_itemRange.dwMax); + + CItemIDRangeManager::instance().UpdateRange(m_itemRange.dwMin, m_itemRange.dwMax); + + m_itemRange.dwMin = 0; + m_itemRange.dwMax = 0; + m_itemRange.dwUsableItemIDMin = 0; +} + +DWORD CPeer::GetHandle() +{ + return m_dwHandle; +} + +DWORD CPeer::GetUserCount() +{ + return m_dwUserCount; +} + +void CPeer::SetUserCount(DWORD dwCount) +{ + m_dwUserCount = dwCount; +} + +bool CPeer::PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data) +{ + if (GetRecvLength() < iBytesProceed + 9) + return false; + + const char * buf = (const char *) GetRecvBuffer(); + buf += iBytesProceed; + + header = *(buf++); + + dwHandle = *((DWORD *) buf); + buf += sizeof(DWORD); + + dwLength = *((DWORD *) buf); + buf += sizeof(DWORD); + + //sys_log(0, "%d header %d handle %u length %u", GetRecvLength(), header, dwHandle, dwLength); + if (iBytesProceed + dwLength + 9 > (DWORD) GetRecvLength()) + { + sys_log(0, "PeekPacket: not enough buffer size: len %u, recv %d", + 9+dwLength, GetRecvLength()-iBytesProceed); + return false; + } + + *data = buf; + iBytesProceed += dwLength + 9; + return true; +} + +void CPeer::EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize) +{ + HEADER h; + + sys_log(1, "EncodeHeader %u handle %u size %u", header, dwHandle, dwSize); + + h.bHeader = header; + h.dwHandle = dwHandle; + h.dwSize = dwSize; + + Encode(&h, sizeof(HEADER)); +} + +void CPeer::EncodeReturn(BYTE header, DWORD dwHandle) +{ + EncodeHeader(header, dwHandle, 0); +} + +int CPeer::Send() +{ + if (m_state == STATE_CLOSE) + return -1; + + return (CPeerBase::Send()); +} + +void CPeer::SetP2PPort(WORD wPort) +{ + m_wP2PPort = wPort; +} + +void CPeer::SetMaps(long * pl) +{ + thecore_memcpy(m_alMaps, pl, sizeof(m_alMaps)); +} + +void CPeer::SendSpareItemIDRange() +{ + if (m_itemSpareRange.dwMin == 0 || m_itemSpareRange.dwMax == 0 || m_itemSpareRange.dwUsableItemIDMin == 0) + { + EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable)); + Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable)); + } + else + { + SetItemIDRange(m_itemSpareRange); + + if (SetSpareItemIDRange(CItemIDRangeManager::instance().GetRange()) == false) + { + sys_log(0, "ItemIDRange: spare range set error"); + m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0; + } + + EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable)); + Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable)); + } +} + +bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange) +{ + if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; + + m_itemRange = itemRange; + sys_log(0, "ItemIDRange: SET %s %u ~ %u start: %u", GetPublicIP(), m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin); + + return true; +} + +bool CPeer::SetSpareItemIDRange(TItemIDRangeTable itemRange) +{ + if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; + + m_itemSpareRange = itemRange; + sys_log(0, "ItemIDRange: SPARE SET %s %u ~ %u start: %u", GetPublicIP(), m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, + m_itemSpareRange.dwUsableItemIDMin); + + return true; +} + +bool CPeer::CheckItemIDRangeCollision(TItemIDRangeTable itemRange) +{ + if (m_itemRange.dwMin < itemRange.dwMax && m_itemRange.dwMax > itemRange.dwMin) + { + sys_err("ItemIDRange: Collision!! this %u ~ %u check %u ~ %u", + m_itemRange.dwMin, m_itemRange.dwMax, itemRange.dwMin, itemRange.dwMax); + return false; + } + + if (m_itemSpareRange.dwMin < itemRange.dwMax && m_itemSpareRange.dwMax > itemRange.dwMin) + { + sys_err("ItemIDRange: Collision with spare range this %u ~ %u check %u ~ %u", + m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, itemRange.dwMin, itemRange.dwMax); + return false; + } + + return true; +} + + diff --git a/db/src/Peer.h b/db/src/Peer.h new file mode 100644 index 0000000..bf80cf7 --- /dev/null +++ b/db/src/Peer.h @@ -0,0 +1,79 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PEER_H__ +#define __INC_PEER_H__ + +#include "PeerBase.h" + +class CPeer : public CPeerBase +{ + protected: + virtual void OnAccept(); + virtual void OnClose(); + virtual void OnConnect(); + + public: +#pragma pack(1) + typedef struct _header + { + BYTE bHeader; + DWORD dwHandle; + DWORD dwSize; + } HEADER; +#pragma pack() + enum EState + { + STATE_CLOSE = 0, + STATE_PLAYING = 1 + }; + + CPeer(); + virtual ~CPeer(); + + void EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize); + bool PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data); + void EncodeReturn(BYTE header, DWORD dwHandle); + + void ProcessInput(); + int Send(); + + DWORD GetHandle(); + DWORD GetUserCount(); + void SetUserCount(DWORD dwCount); + + void SetPublicIP(const char * ip) { m_stPublicIP = ip; } + const char * GetPublicIP() { return m_stPublicIP.c_str(); } + + void SetChannel(BYTE bChannel) { m_bChannel = bChannel; } + BYTE GetChannel() { return m_bChannel; } + + void SetListenPort(WORD wPort) { m_wListenPort = wPort; } + WORD GetListenPort() { return m_wListenPort; } + + void SetP2PPort(WORD wPort); + WORD GetP2PPort() { return m_wP2PPort; } + + void SetMaps(long* pl); + long * GetMaps() { return &m_alMaps[0]; } + + bool SetItemIDRange(TItemIDRangeTable itemRange); + bool SetSpareItemIDRange(TItemIDRangeTable itemRange); + bool CheckItemIDRangeCollision(TItemIDRangeTable itemRange); + void SendSpareItemIDRange(); + + private: + int m_state; + + BYTE m_bChannel; + DWORD m_dwHandle; + DWORD m_dwUserCount; + WORD m_wListenPort; // Ӽ Ŭ̾Ʈ listen ϴ Ʈ + WORD m_wP2PPort; // Ӽ Ӽ P2P listen ϴ Ʈ + long m_alMaps[32]; //  ϰ ִ°? + + TItemIDRangeTable m_itemRange; + TItemIDRangeTable m_itemSpareRange; + + std::string m_stPublicIP; +}; + +#endif diff --git a/db/src/PeerBase.cpp b/db/src/PeerBase.cpp new file mode 100644 index 0000000..151a1d3 --- /dev/null +++ b/db/src/PeerBase.cpp @@ -0,0 +1,214 @@ +#include "stdafx.h" +#include "PeerBase.h" + +CPeerBase::CPeerBase() : m_fd(INVALID_SOCKET), m_BytesRemain(0), m_outBuffer(NULL), m_inBuffer(NULL) +{ +} + +CPeerBase::~CPeerBase() +{ + Destroy(); +} + +void CPeerBase::Disconnect() +{ + if (m_fd != INVALID_SOCKET) + { + fdwatch_del_fd(m_fdWatcher, m_fd); + + socket_close(m_fd); + m_fd = INVALID_SOCKET; + } +} + +void CPeerBase::Destroy() +{ + Disconnect(); + + if (m_outBuffer) + { + buffer_delete(m_outBuffer); + m_outBuffer = NULL; + } + + if (m_inBuffer) + { + buffer_delete(m_inBuffer); + m_inBuffer = NULL; + } +} + +bool CPeerBase::Accept(socket_t fd_accept) +{ + struct sockaddr_in peer; + + if ((m_fd = socket_accept(fd_accept, &peer)) == INVALID_SOCKET) + { + Destroy(); + return false; + } + + //socket_block(m_fd); + socket_sndbuf(m_fd, 233016); + socket_rcvbuf(m_fd, 233016); + + strlcpy(m_host, inet_ntoa(peer.sin_addr), sizeof(m_host)); + m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE); + m_inBuffer = buffer_new(MAX_INPUT_LEN); + + if (!m_outBuffer || !m_inBuffer) + { + Destroy(); + return false; + } + + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false); + + OnAccept(); + sys_log(0, "ACCEPT FROM %s", inet_ntoa(peer.sin_addr)); + return true; +} + +bool CPeerBase::Connect(const char* host, WORD port) +{ + strlcpy(m_host, host, sizeof(m_host)); + + if ((m_fd = socket_connect(host, port)) == INVALID_SOCKET) + return false; + + m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE); + + if (!m_outBuffer) + { + Destroy(); + return false; + } + + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false); + + OnConnect(); + return true; +} + +void CPeerBase::Close() +{ + OnClose(); +} + +void CPeerBase::EncodeBYTE(BYTE b) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &b, 1); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::EncodeWORD(WORD w) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &w, 2); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::EncodeDWORD(DWORD dw) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, &dw, 4); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +void CPeerBase::Encode(const void* data, DWORD size) +{ + if (!m_outBuffer) + { + sys_err("Not ready to write"); + return; + } + + buffer_write(m_outBuffer, data, size); + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); +} + +int CPeerBase::Recv() +{ + if (!m_inBuffer) + { + sys_err("input buffer nil"); + return -1; + } + + buffer_adjust_size(m_inBuffer, MAX_INPUT_LEN >> 2); + int bytes_to_read = buffer_has_space(m_inBuffer); + ssize_t bytes_read = socket_read(m_fd, (char *) buffer_write_peek(m_inBuffer), bytes_to_read); + + if (bytes_read < 0) + { + sys_err("socket_read failed %s", strerror(errno)); + return -1; + } + else if (bytes_read == 0) + return 0; + + buffer_write_proceed(m_inBuffer, bytes_read); + m_BytesRemain = buffer_size(m_inBuffer); + return 1; +} + +void CPeerBase::RecvEnd(int proceed_bytes) +{ + buffer_read_proceed(m_inBuffer, proceed_bytes); + m_BytesRemain = buffer_size(m_inBuffer); +} + +int CPeerBase::GetRecvLength() +{ + return m_BytesRemain; +} + +const void * CPeerBase::GetRecvBuffer() +{ + return buffer_read_peek(m_inBuffer); +} + +int CPeerBase::GetSendLength() +{ + return buffer_size(m_outBuffer); +} + +int CPeerBase::Send() +{ + if (buffer_size(m_outBuffer) <= 0) + return 0; + + int iBufferLeft = fdwatch_get_buffer_size(m_fdWatcher, m_fd); + int iBytesToWrite = MIN(iBufferLeft, buffer_size(m_outBuffer)); + + if (iBytesToWrite == 0) + return 0; + + int result = socket_write(m_fd, (const char *) buffer_read_peek(m_outBuffer), iBytesToWrite); + + if (result == 0) + { + buffer_read_proceed(m_outBuffer, iBytesToWrite); + + if (buffer_size(m_outBuffer) != 0) + fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true); + } + + return (result); +} diff --git a/db/src/PeerBase.h b/db/src/PeerBase.h new file mode 100644 index 0000000..3d192c9 --- /dev/null +++ b/db/src/PeerBase.h @@ -0,0 +1,61 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PEERBASE_H__ +#define __INC_PEERBASE_H__ + +#include "NetBase.h" + +class CPeerBase : public CNetBase +{ + public: + enum + { + MAX_HOST_LENGTH = 30, + MAX_INPUT_LEN = 1024 * 1024 * 2, + DEFAULT_PACKET_BUFFER_SIZE = 1024 * 1024 * 2 + }; + + protected: + virtual void OnAccept() = 0; + virtual void OnConnect() = 0; + virtual void OnClose() = 0; + + public: + bool Accept(socket_t accept_fd); + bool Connect(const char* host, WORD port); + void Close(); + + public: + CPeerBase(); + virtual ~CPeerBase(); + + void Disconnect(); + void Destroy(); + + socket_t GetFd() { return m_fd; } + + void EncodeBYTE(BYTE b); + void EncodeWORD(WORD w); + void EncodeDWORD(DWORD dw); + void Encode(const void* data, DWORD size); + int Send(); + + int Recv(); + void RecvEnd(int proceed_bytes); + int GetRecvLength(); + const void * GetRecvBuffer(); + + int GetSendLength(); + + const char * GetHost() { return m_host; } + + protected: + char m_host[MAX_HOST_LENGTH + 1]; + socket_t m_fd; + + private: + int m_BytesRemain; + LPBUFFER m_outBuffer; + LPBUFFER m_inBuffer; +}; + +#endif diff --git a/db/src/PrivManager.cpp b/db/src/PrivManager.cpp new file mode 100644 index 0000000..c3a373f --- /dev/null +++ b/db/src/PrivManager.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "PrivManager.h" +#include "ClientManager.h" + +const int PRIV_DURATION = 60*60*12; +const int CHARACTER_GOOD_PRIV_DURATION = 2*60*60; +const int CHARACTER_BAD_PRIV_DURATION = 60*60; + +CPrivManager::CPrivManager() +{ + for (int type = 0; type < MAX_PRIV_NUM; ++type) + { + for (int empire = 0; empire < EMPIRE_MAX_NUM; ++empire) + m_aaPrivEmpire[type][empire] = 0; + } +} + +CPrivManager::~CPrivManager() +{ +} + +// +// @version 05/06/07 Bang2ni - ߺ ʽ 忡 ó +// +void CPrivManager::Update() +{ + time_t now = CClientManager::instance().GetCurrentTime(); + + while (!m_pqPrivGuild.empty() && m_pqPrivGuild.top().first <= now) + { + TPrivGuildData* p = m_pqPrivGuild.top().second; + m_pqPrivGuild.pop(); + + if (p->value != 0 && !p->bRemoved) + { + + typeof(m_aPrivGuild[p->type].begin()) it = m_aPrivGuild[p->type].find(p->guild_id); + + // ADD_GUILD_PRIV_TIME + // 忡 ߺ ʽ Ǿ map value () ǾǷ + // TPrivGuildData Ͱ ְ Ӽ鿡 cast ش. + if (it != m_aPrivGuild[p->type].end() && it->second == p) { + m_aPrivGuild[p->type].erase(it); + SendChangeGuildPriv(p->guild_id, p->type, 0, 0); + // END_OF_ADD_GUILD_PRIV_TIME + } + } + + delete p; + } + + while (!m_pqPrivEmpire.empty() && m_pqPrivEmpire.top().first <= now) + { + TPrivEmpireData* p = (m_pqPrivEmpire.top().second); + m_pqPrivEmpire.pop(); + + if (p->value != 0 && !p->bRemoved) + { + SendChangeEmpirePriv(p->empire, p->type, 0, 0); + m_aaPrivEmpire[p->type][p->empire] = 0; + } + + delete p; + } + + while (!m_pqPrivChar.empty() && m_pqPrivChar.top().first <= now) + { + TPrivCharData* p = (m_pqPrivChar.top().second); + m_pqPrivChar.pop(); + + if (!p->bRemoved) + { + // TODO send packet + SendChangeCharPriv(p->pid, p->type, 0); + typeof(m_aPrivChar[p->type].begin()) it = m_aPrivChar[p->type].find(p->pid); + if (it != m_aPrivChar[p->type].end()) + m_aPrivChar[p->type].erase(it); + } + delete p; + } +} + +void CPrivManager::AddCharPriv(DWORD pid, BYTE type, int value) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddCharPriv: wrong char priv type(%u) recved", type); + return; + } + + typeof(m_aPrivChar[type].begin()) it = m_aPrivChar[type].find(pid); + + if (it != m_aPrivChar[type].end()) + return; + + if (!value) + return; + + time_t now = CClientManager::instance().GetCurrentTime(); + TPrivCharData* p = new TPrivCharData(type, value, pid); + + int iDuration = CHARACTER_BAD_PRIV_DURATION; + + if (value > 0) + iDuration = CHARACTER_GOOD_PRIV_DURATION; + + m_pqPrivChar.push(std::make_pair(now+iDuration, p)); + m_aPrivChar[type].insert(std::make_pair(pid, p)); + + // TODO send packet + sys_log(0, "AddCharPriv %d %d %d", pid, type, value); + SendChangeCharPriv(pid, type, value); +} + +// +// @version 05/06/07 Bang2ni - ̹ ʽ 忡 ʽ +// +void CPrivManager::AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t duration_sec) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddGuildPriv: wrong guild priv type(%u) recved", type); + return; + } + + typeof(m_aPrivGuild[type].begin()) it = m_aPrivGuild[type].find(guild_id); + + time_t now = CClientManager::instance().GetCurrentTime(); + time_t end = now + duration_sec; + TPrivGuildData * p = new TPrivGuildData(type, value, guild_id, end); + m_pqPrivGuild.push(std::make_pair(end, p)); + + // ADD_GUILD_PRIV_TIME + // ̹ ʽ ִٸ map value ش. + // value ʹ priority queue ȴ. + if (it != m_aPrivGuild[type].end()) + it->second = p; + else + m_aPrivGuild[type].insert(std::make_pair(guild_id, p)); + + SendChangeGuildPriv(guild_id, type, value, end); + // END_OF_ADD_GUILD_PRIV_TIME + + sys_log(0, "Guild Priv guild(%d) type(%d) value(%d) duration_sec(%d)", guild_id, type, value, duration_sec); +} + +void CPrivManager::AddEmpirePriv(BYTE empire, BYTE type, int value, time_t duration_sec) +{ + if (MAX_PRIV_NUM <= type) + { + sys_err("PRIV_MANAGER: AddEmpirePriv: wrong empire priv type(%u) recved", type); + return; + } + + if (duration_sec < 0) + duration_sec = 0; + + time_t now = CClientManager::instance().GetCurrentTime(); + time_t end = now+duration_sec; + + // ȿȭ + // priority_queue ִ pointer == m_aaPrivEmpire[type][empire] + { + if (m_aaPrivEmpire[type][empire]) + m_aaPrivEmpire[type][empire]->bRemoved = true; + } + + TPrivEmpireData * p = new TPrivEmpireData(type, value, empire, end); + m_pqPrivEmpire.push(std::make_pair(end, p)); + m_aaPrivEmpire[type][empire] = p; + + // ADD_EMPIRE_PRIV_TIME + SendChangeEmpirePriv(empire, type, value, end); + // END_OF_ADD_EMPIRE_PRIV_TIME + + sys_log(0, "Empire Priv empire(%d) type(%d) value(%d) duration_sec(%d)", empire, type, value, duration_sec); +} + +/** + * @version 05/06/08 Bang2ni - ӽð ߰ + */ +struct FSendChangeGuildPriv +{ + FSendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec) + { + p.guild_id = guild_id; + p.type = type; + p.value = value; + p.bLog = 1; + // ADD_GUILD_PRIV_TIME + p.end_time_sec = end_time_sec; + // END_OF_ADD_GUILD_PRIV_TIME + } + + void operator() (CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_GUILD_PRIV, 0, sizeof(TPacketDGChangeGuildPriv)); + peer->Encode(&p, sizeof(TPacketDGChangeGuildPriv)); + p.bLog = 0; + } + + TPacketDGChangeGuildPriv p; +}; + +struct FSendChangeEmpirePriv +{ + FSendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec) + { + p.empire = empire; + p.type = type; + p.value = value; + p.bLog = 1; + // ADD_EMPIRE_PRIV_TIME + p.end_time_sec = end_time_sec; + // END_OF_ADD_EMPIRE_PRIV_TIME + } + + void operator ()(CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_EMPIRE_PRIV, 0, sizeof(TPacketDGChangeEmpirePriv)); + peer->Encode(&p, sizeof(TPacketDGChangeEmpirePriv)); + p.bLog = 0; + } + + TPacketDGChangeEmpirePriv p; +}; + +struct FSendChangeCharPriv +{ + FSendChangeCharPriv(DWORD pid, BYTE type, int value) + { + p.pid = pid; + p.type = type; + p.value = value; + p.bLog = 1; + } + void operator()(CPeer* peer) + { + peer->EncodeHeader(HEADER_DG_CHANGE_CHARACTER_PRIV, 0, sizeof(TPacketDGChangeCharacterPriv)); + peer->Encode(&p, sizeof(TPacketDGChangeCharacterPriv)); + p.bLog = 0; + } + TPacketDGChangeCharacterPriv p; +}; + +// ADD_GUILD_PRIV_TIME +void CPrivManager::SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec) +{ + CClientManager::instance().for_each_peer(FSendChangeGuildPriv(guild_id, type, value, end_time_sec)); +} +// END_OF_ADD_GUILD_PRIV_TIME + +// ADD_EMPIRE_PRIV_TIME +void CPrivManager::SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec) +{ + CClientManager::instance().for_each_peer(FSendChangeEmpirePriv(empire, type, value, end_time_sec)); +} +// END_OF_ADD_EMPIRE_PRIV_TIME + +void CPrivManager::SendChangeCharPriv(DWORD pid, BYTE type, int value) +{ + CClientManager::instance().for_each_peer(FSendChangeCharPriv(pid, type, value)); +} + +void CPrivManager::SendPrivOnSetup(CPeer* peer) +{ + for (int i = 1; i < MAX_PRIV_NUM; ++i) + { + for (int e = 0; e < EMPIRE_MAX_NUM; ++e) + { + // ADD_EMPIRE_PRIV_TIME + TPrivEmpireData* pPrivEmpireData = m_aaPrivEmpire[i][e]; + if (pPrivEmpireData) + { + FSendChangeEmpirePriv(e, i, pPrivEmpireData->value, pPrivEmpireData->end_time_sec)(peer); + } + // END_OF_ADD_EMPIRE_PRIV_TIME + } + + for (typeof(m_aPrivGuild[i].begin()) it = m_aPrivGuild[i].begin(); it != m_aPrivGuild[i].end();++it) + { + // ADD_GUILD_PRIV_TIME + FSendChangeGuildPriv(it->first, i, it->second->value, it->second->end_time_sec)(peer); + // END_OF_ADD_GUILD_PRIV_TIME + } + for (typeof(m_aPrivChar[i].begin()) it = m_aPrivChar[i].begin(); it != m_aPrivChar[i].end(); ++it) + { + FSendChangeCharPriv(it->first, i, it->second->value)(peer); + } + } +} diff --git a/db/src/PrivManager.h b/db/src/PrivManager.h new file mode 100644 index 0000000..9dd20df --- /dev/null +++ b/db/src/PrivManager.h @@ -0,0 +1,106 @@ +// vim: ts=8 sw=4 +#ifndef __INC_PRIV_MANAGER_H +#define __INC_PRIV_MANAGER_H + +#include "Peer.h" +#include +#include + +struct TPrivEmpireData +{ + BYTE type; + int value; + bool bRemoved; + BYTE empire; + + // ADD_EMPIRE_PRIV_TIME + time_t end_time_sec; + + TPrivEmpireData(BYTE type, int value, BYTE empire, time_t end_time_sec) + : type(type), value(value), bRemoved(false), empire(empire), end_time_sec(end_time_sec) + {} + // END_OF_ADD_EMPIRE_PRIV_TIME +}; + +/** + * @version 05/06/08 Bang2ni - ӽð ߰ + */ +struct TPrivGuildData +{ + BYTE type; + int value; + bool bRemoved; + DWORD guild_id; + + // ADD_GUILD_PRIV_TIME + time_t end_time_sec; ///< ӽð + + TPrivGuildData(BYTE type, int value, DWORD guild_id, time_t _end_time_sec) + : type(type), value(value), bRemoved(false), guild_id(guild_id), end_time_sec(_end_time_sec ) + {} + // END_OF_ADD_GUILD_PRIV_TIME +}; + +struct TPrivCharData +{ + BYTE type; + int value; + bool bRemoved; + DWORD pid; + TPrivCharData(BYTE type, int value, DWORD pid) + : type(type), value(value), bRemoved(false), pid(pid) + {} +}; + +/** + * @version 05/06/08 Bang2ni - Guild privilege Լ ð ߰ + */ +class CPrivManager : public singleton +{ + public: + CPrivManager(); + virtual ~CPrivManager(); + + // ADD_GUILD_PRIV_TIME + void AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t time_sec); + // END_OF_ADD_GUILD_PRIV_TIME + + // ADD_EMPIRE_PRIV_TIME + void AddEmpirePriv(BYTE empire, BYTE type, int value, time_t time_sec); + // END_OF_ADD_EMPIRE_PRIV_TIME + + void AddCharPriv(DWORD pid, BYTE type, int value); + + void Update(); + + void SendPrivOnSetup(CPeer* peer); + + private: + + // ADD_GUILD_PRIV_TIME + void SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec); + // END_OF_ADD_GUILD_PRIV_TIME + + // ADD_EMPIRE_PRIV_TIME + void SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec); + // END_OF_ADD_EMPIRE_PRIV_TIME + + void SendChangeCharPriv(DWORD pid, BYTE type, int value); + + typedef std::pair stPairChar; + typedef std::pair stPairGuild; + typedef std::pair stPairEmpire; + + std::priority_queue, std::greater > + m_pqPrivChar; + std::priority_queue, std::greater > + m_pqPrivGuild; + std::priority_queue, std::greater > + m_pqPrivEmpire; + + TPrivEmpireData* m_aaPrivEmpire[MAX_PRIV_NUM][EMPIRE_MAX_NUM]; + std::map m_aPrivGuild[MAX_PRIV_NUM]; + std::map m_aPrivChar[MAX_PRIV_NUM]; +}; + +#endif diff --git a/db/src/ProtoReader.cpp b/db/src/ProtoReader.cpp new file mode 100644 index 0000000..859215f --- /dev/null +++ b/db/src/ProtoReader.cpp @@ -0,0 +1,838 @@ +#include "stdafx.h" + +#include +#include "ProtoReader.h" + +#include "CsvReader.h" + +#include + +using namespace std; + +inline string trim_left(const string& str) +{ + string::size_type n = str.find_first_not_of(" \t\v\n\r"); + return n == string::npos ? str : str.substr(n, str.length()); +} + +inline string trim_right(const string& str) +{ + string::size_type n = str.find_last_not_of(" \t\v\n\r"); + return n == string::npos ? str : str.substr(0, n + 1); +} + +string trim(const string& str){return trim_left(trim_right(str));} + +static string* StringSplit(string strOrigin, string strTok) +{ + int cutAt; //ڸġ + int index = 0; //ڿε + string* strResult = new string[30]; //return Һ + + //strTokãݺ + while ((cutAt = strOrigin.find_first_of(strTok)) != strOrigin.npos) + { + if (cutAt > 0) //ڸġ0ũ() + { + strResult[index++] = strOrigin.substr(0, cutAt); //迭߰ + } + strOrigin = strOrigin.substr(cutAt+1); //ڸκѳ + } + + if(strOrigin.length() > 0) //̾ + { + strResult[index++] = strOrigin.substr(0, cutAt); //迭߰ + } + + for( int i=0;i "; + for (int j=0;j type_value && "Subtype rule: Out of range!!"); + + // assert .. + if (_countof(arSubType) <= type_value) + { + sys_err("SubType : Out of range!! (type_value: %d, count of registered subtype: %d", type_value, _countof(arSubType)); + return -1; + } + + // Ÿ Ÿ ̰ ϴ ˾ƺ, 0 + if (arSubType[type_value]==0) { + return 0; + } + // + + int retInt = -1; + //cout << "SubType : " << subTypeStr << " -> "; + for (int j=0;j " << retValue << endl; + + return retValue; +} + +int get_Item_Flag_Value(string inputString) +{ + + string arFlag[] = {"ITEM_TUNABLE", "ITEM_SAVE", "ITEM_STACKABLE", "COUNT_PER_1GOLD", "ITEM_SLOW_QUERY", "ITEM_UNIQUE", + "ITEM_MAKECOUNT", "ITEM_IRREMOVABLE", "CONFIRM_WHEN_USE", "QUEST_USE", "QUEST_USE_MULTIPLE", + "QUEST_GIVE", "ITEM_QUEST", "LOG", "STACKABLE", "SLOW_QUERY", "REFINEABLE", "IRREMOVABLE", "ITEM_APPLICABLE"}; + + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); // ܾ ɰ 迭. + for(int i =0;i " << retValue << endl; + + return retValue; +} + +int get_Item_WearFlag_Value(string inputString) +{ + + string arWearrFlag[] = {"WEAR_BODY", "WEAR_HEAD", "WEAR_FOOTS", "WEAR_WRIST", "WEAR_WEAPON", "WEAR_NECK", "WEAR_EAR", "WEAR_SHIELD", "WEAR_UNIQUE", + "WEAR_ARROW", "WEAR_HAIR", "WEAR_ABILITY"}; + + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); // ܾ ɰ 迭. + for(int i =0;i " << retValue << endl; + + return retValue; +} + +int get_Item_Immune_Value(string inputString) +{ + + string arImmune[] = {"PARA","CURSE","STUN","SLEEP","SLOW","POISON","TERROR"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, "|"); // ܾ ɰ 迭. + for(int i =0;i " << retValue << endl; + + return retValue; +} + + + + +int get_Item_LimitType_Value(string inputString) +{ + string arLimitType[] = {"LIMIT_NONE", "LEVEL", "STR", "DEX", "INT", "CON", "PC_BANG", "REAL_TIME", "REAL_TIME_FIRST_USE", "TIMER_BASED_ON_WEAR"}; + + int retInt = -1; + //cout << "LimitType : " << limitTypeStr << " -> "; + for (int j=0;j "; + for (int j=0;j "; + for (int j=0;j "; + for (int j=0;j "; + for (int j=0;j "; + for (int j=0;j " << retValue << endl; + + return retValue; +} +int get_Mob_RaceFlag_Value(string inputString) +{ + string arRaceFlag[] = {"ANIMAL","UNDEAD","DEVIL","HUMAN","ORC","MILGYO","INSECT","FIRE","ICE","DESERT","TREE", + "ATT_ELEC","ATT_FIRE","ATT_ICE","ATT_WIND","ATT_EARTH","ATT_DARK"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, ","); // ܾ ɰ 迭. + for(int i =0;i " << retValue << endl; + + return retValue; +} +int get_Mob_ImmuneFlag_Value(string inputString) +{ + string arImmuneFlag[] = {"STUN","SLOW","FALL","CURSE","POISON","TERROR", "REFLECT"}; + + int retValue = 0; + string* arInputString = StringSplit(inputString, ","); // ܾ ɰ 迭. + for(int i =0;i " << retValue << endl; + + + return retValue; +} + + +#ifndef __DUMP_PROTO__ + +// ̺ ش. +bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable,std::map &nameMap) +{ + int col = 0; + str_to_number(mobTable->dwVnum, csvTable.AsStringByIndex(col++)); + strlcpy(mobTable->szName, csvTable.AsStringByIndex(col++), sizeof(mobTable->szName)); + + //3. ̸ ־ֱ. + map::iterator it; + it = nameMap.find(mobTable->dwVnum); + if (it != nameMap.end()) { + const char * localeName = it->second; + strlcpy(mobTable->szLocaleName, localeName, sizeof (mobTable->szLocaleName)); + } else { + strlcpy(mobTable->szLocaleName, mobTable->szName, sizeof (mobTable->szLocaleName)); + } + + //RANK + int rankValue = get_Mob_Rank_Value(csvTable.AsStringByIndex(col++)); + mobTable->bRank = rankValue; + //TYPE + int typeValue = get_Mob_Type_Value(csvTable.AsStringByIndex(col++)); + mobTable->bType = typeValue; + //BATTLE_TYPE + int battleTypeValue = get_Mob_BattleType_Value(csvTable.AsStringByIndex(col++)); + mobTable->bBattleType = battleTypeValue; + + str_to_number(mobTable->bLevel, csvTable.AsStringByIndex(col++)); + //SIZE + int sizeValue = get_Mob_Size_Value(csvTable.AsStringByIndex(col++)); + mobTable->bSize = sizeValue; + //AI_FLAG + int aiFlagValue = get_Mob_AIFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwAIFlag = aiFlagValue; + //mount_capacity; + col++; + //RACE_FLAG + int raceFlagValue = get_Mob_RaceFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwRaceFlag = raceFlagValue; + //IMMUNE_FLAG + int immuneFlagValue = get_Mob_ImmuneFlag_Value(csvTable.AsStringByIndex(col++)); + mobTable->dwImmuneFlag = immuneFlagValue; + + str_to_number(mobTable->bEmpire, csvTable.AsStringByIndex(col++)); //col = 11 + + strlcpy(mobTable->szFolder, csvTable.AsStringByIndex(col++), sizeof(mobTable->szFolder)); + + str_to_number(mobTable->bOnClickType, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->bStr, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bDex, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bCon, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bInt, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDamageRange[0], csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDamageRange[1], csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwMaxHP, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRegenCycle, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRegenPercent, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwGoldMin, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwGoldMax, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwExp, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wDef, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->sAttackSpeed, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->sMovingSpeed, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bAggresiveHPPct, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wAggressiveSight, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->wAttackRange, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->dwDropItemVnum, csvTable.AsStringByIndex(col++)); //32 + str_to_number(mobTable->dwResurrectionVnum, csvTable.AsStringByIndex(col++)); + for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i) + str_to_number(mobTable->cEnchants[i], csvTable.AsStringByIndex(col++)); + + for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i) + str_to_number(mobTable->cResists[i], csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->fDamMultiply, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwSummonVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->dwDrainSP, csvTable.AsStringByIndex(col++)); + + //Mob_Color + ++col; + + str_to_number(mobTable->dwPolymorphItemVnum, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->Skills[0].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[0].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[1].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[1].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[2].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[2].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[3].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[3].dwVnum, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[4].bLevel, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->Skills[4].dwVnum, csvTable.AsStringByIndex(col++)); + + str_to_number(mobTable->bBerserkPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bStoneSkinPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bGodSpeedPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bDeathBlowPoint, csvTable.AsStringByIndex(col++)); + str_to_number(mobTable->bRevivePoint, csvTable.AsStringByIndex(col++)); + + sys_log(0, "MOB #%-5d %-24s level: %-3u rank: %u empire: %d", mobTable->dwVnum, mobTable->szLocaleName, mobTable->bLevel, mobTable->bRank, mobTable->bEmpire); + + return true; +} + +bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map &nameMap) +{ + int col = 0; + + int dataArray[33]; + for (int i=0; idwVnum = dataArray[0]; + itemTable->dwVnumRange = 0; + } + else + { + std::string s_start_vnum (s.substr(0, pos)); + std::string s_end_vnum (s.substr(pos +1 )); + + int start_vnum = atoi(s_start_vnum.c_str()); + int end_vnum = atoi(s_end_vnum.c_str()); + if (0 == start_vnum || (0 != end_vnum && end_vnum < start_vnum)) + { + sys_err ("INVALID VNUM %s", s.c_str()); + return false; + } + itemTable->dwVnum = start_vnum; + itemTable->dwVnumRange = end_vnum - start_vnum; + } + } + + strlcpy(itemTable->szName, csvTable.AsStringByIndex(1), sizeof(itemTable->szName)); + // ̸ ־ֱ. + map::iterator it; + it = nameMap.find(itemTable->dwVnum); + if (it != nameMap.end()) { + const char * localeName = it->second; + strlcpy(itemTable->szLocaleName, localeName, sizeof (itemTable->szLocaleName)); + } else { + strlcpy(itemTable->szLocaleName, itemTable->szName, sizeof (itemTable->szLocaleName)); + } + itemTable->bType = dataArray[2]; + itemTable->bSubType = dataArray[3]; + itemTable->bSize = dataArray[4]; + itemTable->dwAntiFlags = dataArray[5]; + itemTable->dwFlags = dataArray[6]; + itemTable->dwWearFlags = dataArray[7]; + itemTable->dwImmuneFlag = dataArray[8]; + itemTable->dwGold = dataArray[9]; + itemTable->dwShopBuyPrice = dataArray[10]; + itemTable->dwRefinedVnum = dataArray[11]; + itemTable->wRefineSet = dataArray[12]; + itemTable->bAlterToMagicItemPct = dataArray[13]; + itemTable->cLimitRealTimeFirstUseIndex = -1; + itemTable->cLimitTimerBasedOnWearIndex = -1; + + int i; + + for (i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + itemTable->aLimits[i].bType = dataArray[14+i*2]; + itemTable->aLimits[i].lValue = dataArray[15+i*2]; + + if (LIMIT_REAL_TIME_START_FIRST_USE == itemTable->aLimits[i].bType) + itemTable->cLimitRealTimeFirstUseIndex = (char)i; + + if (LIMIT_TIMER_BASED_ON_WEAR == itemTable->aLimits[i].bType) + itemTable->cLimitTimerBasedOnWearIndex = (char)i; + + } + + for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i) + { + itemTable->aApplies[i].bType = dataArray[18+i*2]; + itemTable->aApplies[i].lValue = dataArray[19+i*2]; + } + + for (i = 0; i < ITEM_VALUES_MAX_NUM; ++i) + itemTable->alValues[i] = dataArray[24+i]; + + //column for 'Specular' + itemTable->bGainSocketPct = dataArray[31]; + itemTable->sAddonType = dataArray[32]; + + //test + str_to_number(itemTable->bWeight, "0"); + + return true; +} + +#endif diff --git a/db/src/ProtoReader.h b/db/src/ProtoReader.h new file mode 100644 index 0000000..5505f73 --- /dev/null +++ b/db/src/ProtoReader.h @@ -0,0 +1,36 @@ +#ifndef __Item_CSV_READER_H__ +#define __Item_CSV_READER_H__ + +#include +#include + +#include "CsvReader.h" + +//csv оͼ ̺ ־ش. +void putItemIntoTable(); //(̺, ׽Ʈ) + +int get_Item_Type_Value(std::string inputString); +int get_Item_SubType_Value(int type_value, std::string inputString); +int get_Item_AntiFlag_Value(std::string inputString); +int get_Item_Flag_Value(std::string inputString); +int get_Item_WearFlag_Value(std::string inputString); +int get_Item_Immune_Value(std::string inputString); +int get_Item_LimitType_Value(std::string inputString); +int get_Item_ApplyType_Value(std::string inputString); + + +// 䵵 ִ. +int get_Mob_Rank_Value(std::string inputString); +int get_Mob_Type_Value(std::string inputString); +int get_Mob_BattleType_Value(std::string inputString); + +int get_Mob_Size_Value(std::string inputString); +int get_Mob_AIFlag_Value(std::string inputString); +int get_Mob_RaceFlag_Value(std::string inputString); +int get_Mob_ImmuneFlag_Value(std::string inputString); + +// +bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable, std::map &nameMap); +bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map &nameMap); + +#endif \ No newline at end of file diff --git a/db/src/QID.h b/db/src/QID.h new file mode 100644 index 0000000..230d2ef --- /dev/null +++ b/db/src/QID.h @@ -0,0 +1,39 @@ +#ifndef __INC_METIN_II_DB_QID_H__ +#define __INC_METIN_II_DB_QID_H__ + +/** + * @version 05/06/10 Bang2ni - ߰(QID_ITEMPRICE_XXX) + */ +enum QID +{ + QID_PLAYER, // 0 + QID_ITEM, // 1 + QID_QUEST, // 2 + QID_AFFECT, // 3 + QID_LOGIN, // 4 + QID_SAFEBOX_LOAD, // 5 + QID_SAFEBOX_CHANGE_SIZE, // 6 + QID_SAFEBOX_CHANGE_PASSWORD, // 7 + QID_SAFEBOX_CHANGE_PASSWORD_SECOND, // 8 + QID_SAFEBOX_SAVE, // 9 + QID_ITEM_SAVE, // 10 + QID_ITEM_DESTROY, // 11 + QID_QUEST_SAVE, // 12 + QID_PLAYER_SAVE, // 13 + QID_HIGHSCORE_REGISTER, // 14 + QID_PLAYER_DELETE, // 15 + QID_LOGIN_BY_KEY, // 16 + QID_PLAYER_INDEX_CREATE, // 17 + QID_ITEM_AWARD_LOAD, // 18 + QID_ITEM_AWARD_TAKEN, // 19 + QID_GUILD_RANKING, // 20 + + // MYSHOP_PRICE_LIST + QID_ITEMPRICE_SAVE, ///< 21, + QID_ITEMPRICE_DESTROY, ///< 22, + QID_ITEMPRICE_LOAD_FOR_UPDATE, ///< 23, Ʈ ε + QID_ITEMPRICE_LOAD, ///< 24, ε + // END_OF_MYSHOP_PRICE_LIST +}; + +#endif diff --git a/db/src/grid.cpp b/db/src/grid.cpp new file mode 100644 index 0000000..bb94831 --- /dev/null +++ b/db/src/grid.cpp @@ -0,0 +1,130 @@ +#include +#include +#include "../../libthecore/include/memcpy.h" +#include "../../common/stl.h" +#include "grid.h" + +CGrid::CGrid(int w, int h) : m_iWidth(w), m_iHeight(h) +{ + m_pGrid = new char[m_iWidth * m_iHeight]; + memset(m_pGrid, 0, sizeof(char) * m_iWidth * m_iHeight); +} + +CGrid::CGrid(CGrid * pkGrid, int w, int h) : m_iWidth(w), m_iHeight(h) +{ + m_pGrid = new char[m_iWidth * m_iHeight]; + int iSize = std::MIN(w * h, pkGrid->m_iWidth * pkGrid->m_iHeight); + thecore_memcpy(m_pGrid, pkGrid->m_pGrid, sizeof(char) * iSize); +} + +CGrid::~CGrid() +{ + delete [] m_pGrid; +} + +void CGrid::Clear() +{ + memset(m_pGrid, 0, sizeof(char) * m_iWidth * m_iHeight); +} + +int CGrid::FindBlank(int w, int h) +{ + // ũⰡ ũٸ Ȯ ʿ ׳ + if (w > m_iWidth || h > m_iHeight) + return -1; + + int iRow; + + for (iRow = 0; iRow < m_iHeight; ++iRow) + { + for (int iCol = 0; iCol < m_iWidth; ++iCol) + { + int iIndex = iRow * m_iWidth + iCol; + + if (IsEmpty(iIndex, w, h)) + return iIndex; + } + } + + return -1; +} + +bool CGrid::Put(int iPos, int w, int h) +{ + if (!IsEmpty(iPos, w, h)) + return false; + + for (int y = 0; y < h; ++y) + { + int iStart = iPos + (y * m_iWidth); + m_pGrid[iStart] = true; + + int x = 1; + while (x < w) + m_pGrid[iStart + x++] = true; + } + + return true; +} + +void CGrid::Get(int iPos, int w, int h) +{ + if (iPos >= m_iWidth * m_iHeight) + return; + + for (int y = 0; y < h; ++y) + { + int iStart = iPos + (y * m_iWidth); + m_pGrid[iStart] = false; + + int x = 1; + while (x < w) + m_pGrid[iStart + x++] = false; + } +} + +bool CGrid::IsEmpty(int iPos, int w, int h) +{ + int iRow = iPos / m_iWidth; + + // Grid ΰ ˻ + if (iRow + h > m_iHeight) + return false; + + if (iPos + w > iRow * m_iWidth + m_iWidth) + return false; + + for (int y = 0; y < h; ++y) + { + int iStart = iPos + (y * m_iWidth); + + if (m_pGrid[iStart]) + return false; + + int x = 1; + while (x < w) + if (m_pGrid[iStart + x++]) + return false; + } + + return true; +} + +void CGrid::Print() +{ + printf("Grid %p\n", this); + + for (int y = 0; y < m_iHeight; ++y) + { + for (int x = 0; x < m_iWidth; ++x) + printf("%d", m_pGrid[y * m_iWidth + x]); + + printf("\n"); + } +} + +unsigned int CGrid::GetSize() +{ + return m_iWidth * m_iHeight; +} + diff --git a/db/src/grid.h b/db/src/grid.h new file mode 100644 index 0000000..9ae21ed --- /dev/null +++ b/db/src/grid.h @@ -0,0 +1,27 @@ +// vim: ts=8 sw=4 +#ifndef __INC_METIN_II_GRID_H__ +#define __INC_METIN_II_GRID_H__ + +class CGrid +{ + public: + CGrid(int w, int h); + CGrid(CGrid * pkGrid, int w, int h); + ~CGrid(); + + void Clear(); + int FindBlank(int w, int h); + bool IsEmpty(int iPos, int w, int h); + bool Put(int iPos, int w, int h); + void Get(int iPos, int w, int h); + void Print(); + unsigned int GetSize(); + + protected: + int m_iWidth; + int m_iHeight; + + char * m_pGrid; +}; + +#endif diff --git a/db/src/stdafx.h b/db/src/stdafx.h new file mode 100644 index 0000000..c36c862 --- /dev/null +++ b/db/src/stdafx.h @@ -0,0 +1,20 @@ +#ifndef __INC_METiN_II_DBSERV_STDAFX_H__ +#define __INC_METiN_II_DBSERV_STDAFX_H__ + +#include "../../libthecore/include/stdafx.h" + +#ifndef __WIN32__ +#include +#else +#define isdigit iswdigit +#define isspace iswspace +#endif + +#include "../../common/length.h" +#include "../../common/tables.h" +#include "../../common/singleton.h" +#include "../../common/utils.h" +#include "../../common/stl.h" +#include "../../common/service.h" + +#endif diff --git a/db/src/version.cpp b/db/src/version.cpp new file mode 100644 index 0000000..f4bb8b9 --- /dev/null +++ b/db/src/version.cpp @@ -0,0 +1,22 @@ +#include +#include + +void WriteVersion() +{ +#ifndef __WIN32__ + FILE* fp(fopen("VERSION.txt", "w")); + + if (NULL != fp) + { + fprintf(fp, "game perforce revision: %s\n", __P4_VERSION__); + fprintf(fp, "%s@%s:%s\n", __USER__, __HOSTNAME__, __PWD__); + fclose(fp); + } + else + { + fprintf(stderr, "cannot open VERSION.txt\n"); + exit(0); + } +#endif +} + diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt new file mode 100644 index 0000000..54bcb27 --- /dev/null +++ b/game/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.8) + +project(game CXX) + +file(GLOB_RECURSE sources + src/*.cpp src/*.h +) + +include_directories(${PROJECT_BINARY_DIR}/src/system/) + +include_directories(src/) + +# Find dependencies +find_package(libmysql REQUIRED) +find_package(Boost COMPONENTS system REQUIRED) +find_package(DevIL REQUIRED) +find_package(LZO REQUIRED) + +add_executable(${PROJECT_NAME} ${sources}) + +# Link dependencies if found +if (libmysql_FOUND) + target_link_libraries (${PROJECT_NAME} ${MYSQL_LIBRARIES}) +endif (libmysql_FOUND) + +if (Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES} ${Boost_SYSTEM_LIBRARY}) +endif (Boost_FOUND) + +if (IL_FOUND) + include_directories(${IL_INCLUDE_DIR}) + target_link_libraries (${PROJECT_NAME} ${IL_LIBRARIES}) +endif (IL_FOUND) + +if (LZO_FOUND) + include_directories(${LZO_INCLUDE_DIR}) + target_link_libraries (${PROJECT_NAME} ${LZO_LIBRARIES}) +endif (LZO_FOUND) + + +target_link_libraries(${PROJECT_NAME} md) + +# Pthreads +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package (Threads REQUIRED) +target_link_libraries (${PROJECT_NAME} Threads::Threads) + +find_package(GTest REQUIRED) +if (GTEST_FOUND) + include_directories(${GTEST_INCLUDE_DIRS}) + target_link_libraries (${PROJECT_NAME} ${GTEST_BOTH_LIBRARIES}) +endif (GTEST_FOUND) + +target_link_libraries(${PROJECT_NAME} libgame libpoly libsql libthecore liblua) + diff --git a/game/src/BattleArena.cpp b/game/src/BattleArena.cpp new file mode 100644 index 0000000..4ec76b1 --- /dev/null +++ b/game/src/BattleArena.cpp @@ -0,0 +1,335 @@ +#include "stdafx.h" +#include "constants.h" +#include "BattleArena.h" +#include "start_position.h" +#include "char_manager.h" +#include "char.h" +#include "sectree_manager.h" +#include "regen.h" +#include "questmanager.h" + +extern int passes_per_sec; +extern int test_server; + +CBattleArena::CBattleArena() + : m_pEvent(NULL), + m_status(STATUS_CLOSE), + m_nMapIndex(0), + m_nEmpire(0), + m_bForceEnd(false) +{ +} + +bool CBattleArena::IsRunning() +{ + return m_status == STATUS_CLOSE ? false : true; +} + +bool CBattleArena::IsBattleArenaMap(int nMapIndex) +{ + if ( nMapIndex == nBATTLE_ARENA_MAP[1] || + nMapIndex == nBATTLE_ARENA_MAP[2] || + nMapIndex == nBATTLE_ARENA_MAP[3] ) + { + return true; + } + + return false; +} + +struct FWarpToHome +{ + void operator() (LPENTITY ent) + { + if ( ent->IsType(ENTITY_CHARACTER) == true ) + { + LPCHARACTER lpChar = (LPCHARACTER)ent; + + if ( lpChar->IsPC() == true ) + { + if ( !test_server ) + { + if ( lpChar->GetGMLevel() != GM_PLAYER ) return; + } + + int nEmpire, nMapIndex, x, y; + + nEmpire = lpChar->GetEmpire(); + nMapIndex = EMPIRE_START_MAP(nEmpire); + x = EMPIRE_START_X(nEmpire); + y = EMPIRE_START_Y(nEmpire); + + lpChar->WarpSet(x, y, nMapIndex); + } + } + } +}; + +void CBattleArena::SetStatus(BATTLEARENA_STATUS status) +{ + m_status = status; +} + +EVENTINFO(SBattleArenaInfo) +{ + int nEmpire; + int nMapIndex; + int state; + int wait_count; + + SBattleArenaInfo() + : nEmpire( 0 ) + , nMapIndex( 0 ) + , state( 0 ) + , wait_count( 0 ) + { + } +}; + +EVENTFUNC(battle_arena_event) +{ + SBattleArenaInfo * pInfo = dynamic_cast(event->info ); + + if ( pInfo == NULL ) + { + return 0; // cancel immediately + } + + if (pInfo != NULL) + { + switch (pInfo->state) + { + case 0: + { + ++pInfo->state; + BroadcastNotice(LC_TEXT("͵ ݱ 5 ҽϴ!!!")); + } + return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*4); + + case 1: + { + ++pInfo->state; + BroadcastNotice(LC_TEXT("͵ ݱ 1 ҽϴ!!!")); + } + return test_server ? PASSES_PER_SEC(10) : PASSES_PER_SEC(60); + + case 2: + { + ++pInfo->state; + pInfo->wait_count = 0; + + quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0); + BroadcastNotice(LC_TEXT("͵ ϱ ߽ϴ.")); + + LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex); + + if ( sectree != NULL ) + { + std::string strMap = LocaleService_GetMapPath(); + if ( pInfo->nEmpire > 0 ) + { + strMap += strRegen[pInfo->nEmpire]; + + regen_do(strMap.c_str(), pInfo->nMapIndex, sectree->m_setting.iBaseX, sectree->m_setting.iBaseY, NULL, false); + } + } + } + return test_server ? PASSES_PER_SEC(60*1) : PASSES_PER_SEC(60*5); + + case 3 : + { + if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 ) + { + pInfo->state = 6; + SendNoticeMap(LC_TEXT("߾ ܿ 𿩵ϴ."), pInfo->nMapIndex, false); + } + else + { + pInfo->wait_count++; + + if ( pInfo->wait_count >= 5 ) + { + pInfo->state++; + SendNoticeMap(LC_TEXT("͵ Դϴ."), pInfo->nMapIndex, false); + } + else + { + CBattleArena::instance().SpawnRandomStone(); + } + } + } + return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5); + + case 4 : + { + pInfo->state++; + SendNoticeMap(LC_TEXT("͵ ߽ϴ."), pInfo->nMapIndex, false); + SendNoticeMap(LC_TEXT(" ưϴ."), pInfo->nMapIndex, false); + + SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex); + } + return PASSES_PER_SEC(30); + + case 5 : + { + LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex); + + if ( sectree != NULL ) + { + struct FWarpToHome f; + sectree->for_each( f ); + } + + CBattleArena::instance().End(); + } + return 0; + + case 6 : + { + pInfo->state++; + pInfo->wait_count = 0; + + SendNoticeMap(LC_TEXT("͵ Ÿϴ."), pInfo->nMapIndex, false); + SendNoticeMap(LC_TEXT("30 ͸ָ ּ."), pInfo->nMapIndex, false); + + CBattleArena::instance().SpawnLastBoss(); + } + return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5); + + case 7 : + { + if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 ) + { + SendNoticeMap(LC_TEXT("͸ֿ ϵ ƽϴ."), pInfo->nMapIndex, false); + SendNoticeMap(LC_TEXT(" ưϴ."), pInfo->nMapIndex, false); + + pInfo->state = 5; + + return PASSES_PER_SEC(60); + } + + pInfo->wait_count++; + + if ( pInfo->wait_count >= 6 ) + { + SendNoticeMap(LC_TEXT("͸ְ Ͽϴ."), pInfo->nMapIndex, false); + SendNoticeMap(LC_TEXT(" ưϴ."), pInfo->nMapIndex, false); + + SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex); + SECTREE_MANAGER::instance().PurgeStonesInMap(pInfo->nMapIndex); + + pInfo->state = 5; + + return PASSES_PER_SEC(60); + } + else + { + CBattleArena::instance().SpawnRandomStone(); + } + } + return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5); + } + } + + return 0; +} + +bool CBattleArena::Start(int nEmpire) +{ + if ( m_status != STATUS_CLOSE ) return false; + if ( nEmpire < 1 || nEmpire > 3 ) return false; + + m_nMapIndex = nBATTLE_ARENA_MAP[nEmpire]; + m_nEmpire = nEmpire; + + char szBuf[1024]; + snprintf(szBuf, sizeof(szBuf), LC_TEXT("%s ͵ ϰ ֽϴ."), EMPIRE_NAME(m_nEmpire)); + BroadcastNotice(szBuf); + BroadcastNotice(LC_TEXT("10 Դϴ.")); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + SBattleArenaInfo* info = AllocEventInfo(); + + info->nMapIndex = m_nMapIndex; + info->nEmpire = m_nEmpire; + info->state = 0; + info->wait_count = 0; + + m_pEvent = event_create(battle_arena_event, info, test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(5*60)); + + SetStatus(STATUS_BATTLE); + + quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", m_nMapIndex); + + return true; +} + +void CBattleArena::End() +{ + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + m_bForceEnd = false; + m_nMapIndex = 0; + m_nEmpire = 0; + m_status = STATUS_CLOSE; + + quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0); +} + +void CBattleArena::ForceEnd() +{ + if ( m_bForceEnd == true ) return; + + m_bForceEnd = true; + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + SBattleArenaInfo* info = AllocEventInfo(); + + info->nMapIndex = m_nMapIndex; + info->nEmpire = m_nEmpire; + info->state = 3; + + event_create(battle_arena_event, info, PASSES_PER_SEC(5)); +} + +void CBattleArena::SpawnRandomStone() +{ + static const DWORD vnum[7] = { 8012, 8013, 8014, 8024, 8025, 8026, 8027 }; + + static const int region_info[3][4] = { + {688900, 247600, 692700, 250000}, + {740200, 251000, 744200, 247700}, + {791400, 250900, 795900, 250400} }; + + int idx = m_nMapIndex - 190; + if ( idx < 0 || idx >= 3 ) return; + + CHARACTER_MANAGER::instance().SpawnMobRange( + vnum[number(0, 6)], + m_nMapIndex, + region_info[idx][0], region_info[idx][1], region_info[idx][2], region_info[idx][3], + false, true); +} + +void CBattleArena::SpawnLastBoss() +{ + static const DWORD vnum = 8616; + + static const int position[3][2] = { + { 691000, 248900 }, + { 742300, 249000 }, + { 793600, 249300 } }; + + int idx = m_nMapIndex - 190; + if ( idx < 0 || idx >= 3 ) return; + + CHARACTER_MANAGER::instance().SpawnMob(vnum, m_nMapIndex, position[idx][0], position[idx][1], 0); +} + diff --git a/game/src/BattleArena.h b/game/src/BattleArena.h new file mode 100644 index 0000000..086271e --- /dev/null +++ b/game/src/BattleArena.h @@ -0,0 +1,42 @@ + +const static int nBATTLE_ARENA_MAP[] = { 0, 190, 191, 192 }; +const static std::string strRegen[] = +{ + "", + "/metin2_map_battlearena01/regen00.txt", + "/metin2_map_battlearena02/regen00.txt", + "/metin2_map_battlearena03/regen00.txt", +}; + +enum BATTLEARENA_STATUS +{ + STATUS_CLOSE = 0, + STATUS_BATTLE, + STATUS_END, +}; + +class CBattleArena : public singleton +{ + private : + LPEVENT m_pEvent; + BATTLEARENA_STATUS m_status; + int m_nMapIndex; + int m_nEmpire; + bool m_bForceEnd; + + public : + CBattleArena(); + + static bool IsBattleArenaMap(int nMapIndex); + + bool IsRunning(); + void SetStatus(BATTLEARENA_STATUS status); + + bool Start(int nEmpire); + void ForceEnd(); + void End(); + + void SpawnLastBoss(); + void SpawnRandomStone(); +}; + diff --git a/game/src/BlueDragon.cpp b/game/src/BlueDragon.cpp new file mode 100644 index 0000000..acec908 --- /dev/null +++ b/game/src/BlueDragon.cpp @@ -0,0 +1,197 @@ + +#include "stdafx.h" + +#include "BlueDragon.h" + +extern int test_server; +extern int passes_per_sec; + +#include "vector.h" +#include "utils.h" +#include "char.h" +#include "mob_manager.h" +#include "sectree_manager.h" +#include "battle.h" +#include "affect.h" +#include "BlueDragon_Binder.h" +#include "BlueDragon_Skill.h" +#include "packet.h" +#include "motion.h" + +time_t UseBlueDragonSkill(LPCHARACTER pChar, unsigned int idx) +{ + LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap( pChar->GetMapIndex() ); + + if (NULL == pSecMap) + return 0; + + int nextUsingTime = 0; + + switch (idx) + { + case 0: + { + sys_log(0, "BlueDragon: Using Skill Breath"); + + FSkillBreath f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill0", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "period", "max")); + } + break; + + case 1: + { + sys_log(0, "BlueDragon: Using Skill Weak Breath"); + + FSkillWeakBreath f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill1", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "period", "max")); + } + break; + + case 2: + { + sys_log(0, "BlueDragon: Using Skill EarthQuake"); + + FSkillEarthQuake f(pChar); + + pSecMap->for_each( f ); + + nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill2", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "period", "max")); + + if (NULL != f.pFarthestChar) + { + pChar->BeginFight( f.pFarthestChar ); + } + } + break; + + default: + sys_err("BlueDragon: Wrong Skill Index: %d", idx); + return 0; + } + + int addPct = BlueDragon_GetRangeFactor("hp_period", pChar->GetHPPct()); + + nextUsingTime += (nextUsingTime * addPct) / 100; + + return nextUsingTime; +} + +int BlueDragon_StateBattle(LPCHARACTER pChar) +{ + if (pChar->GetHPPct() > 98) + return PASSES_PER_SEC(1); + + const int SkillCount = 3; + int SkillPriority[SkillCount]; + static time_t timeSkillCanUseTime[SkillCount]; + + if (pChar->GetHPPct() > 76) + { + SkillPriority[0] = 1; + SkillPriority[1] = 0; + SkillPriority[2] = 2; + } + else if (pChar->GetHPPct() > 31) + { + SkillPriority[0] = 0; + SkillPriority[1] = 1; + SkillPriority[2] = 2; + } + else + { + SkillPriority[0] = 0; + SkillPriority[1] = 2; + SkillPriority[2] = 1; + } + + time_t timeNow = static_cast(get_dword_time()); + + for (int i=0 ; i < SkillCount ; ++i) + { + const int SkillIndex = SkillPriority[i]; + + if (timeSkillCanUseTime[SkillIndex] < timeNow) + { + int SkillUsingDuration = + static_cast(CMotionManager::instance().GetMotionDuration( pChar->GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + SkillIndex) )); + + timeSkillCanUseTime[SkillIndex] = timeNow + (UseBlueDragonSkill( pChar, SkillIndex ) * 1000) + SkillUsingDuration + 3000; + + pChar->SendMovePacket(FUNC_MOB_SKILL, SkillIndex, pChar->GetX(), pChar->GetY(), 0, timeNow); + + return 0 == SkillUsingDuration ? PASSES_PER_SEC(1) : PASSES_PER_SEC(SkillUsingDuration); + } + } + + return PASSES_PER_SEC(1); +} + +int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam) +{ + if (NULL == me || NULL == pAttacker) + return dam; + + if (true == pAttacker->IsMonster() && 2493 == pAttacker->GetMobTable().dwVnum) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (ATK_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( pAttacker->GetMapIndex(), dwDragonStoneID ); + + dam += (dam * (val*cnt))/100; + + break; + } + } + } + + if (true == me->IsMonster() && 2493 == me->GetMobTable().dwVnum) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (DEF_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type")) + { + DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum"); + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val"); + size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( me->GetMapIndex(), dwDragonStoneID ); + + dam -= (dam * (val*cnt))/100; + + if (dam <= 0) + dam = 1; + + break; + } + } + } + + if (true == me->IsStone() && 0 != pAttacker->GetMountVnum()) + { + for (int i=1 ; i <= 4 ; ++i) + { + if (me->GetMobTable().dwVnum == BlueDragon_GetIndexFactor("DragonStone", i, "vnum")) + { + if (pAttacker->GetMountVnum() == BlueDragon_GetIndexFactor("DragonStone", i, "enemy")) + { + size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "enemy_val"); + + dam *= val; + + break; + } + } + } + } + + return dam; +} + diff --git a/game/src/BlueDragon.h b/game/src/BlueDragon.h new file mode 100644 index 0000000..25b7d5b --- /dev/null +++ b/game/src/BlueDragon.h @@ -0,0 +1,5 @@ + +extern int BlueDragon_StateBattle (LPCHARACTER); +extern time_t UseBlueDragonSkill (LPCHARACTER, unsigned int); +extern int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER attacker, int dam); + diff --git a/game/src/BlueDragon_Binder.cpp b/game/src/BlueDragon_Binder.cpp new file mode 100644 index 0000000..b6bbfe7 --- /dev/null +++ b/game/src/BlueDragon_Binder.cpp @@ -0,0 +1,218 @@ + +#include "stdafx.h" + +#include "BlueDragon_Binder.h" + +#include "questmanager.h" + +unsigned int BlueDragon_GetSkillFactor(const size_t cnt, ...) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + va_list vl; + + va_start(vl, cnt); + + for( size_t i=0 ; i < cnt ; ++i ) + { + const char* key = va_arg(vl, const char*); + + if (NULL == key) + { + va_end(vl); + lua_settop( L, stack_top ); + sys_err("BlueDragon: wrong key list"); + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1) && i != cnt-1) + { + va_end(vl); + lua_settop( L, stack_top ); + sys_err("BlueDragon: wrong key table %s", key); + return 0; + } + } + + va_end(vl); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + sys_err("BlueDragon: Last key is not a number"); + return 0; + } + + int val = static_cast(lua_tonumber( L, -1 )); + + lua_settop( L, stack_top ); + + return val; +} + +unsigned int BlueDragon_GetRangeFactor(const char* key, const int val) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no required table %s", key); + return 0; + } + + const size_t cnt = static_cast(luaL_getn(L, -1)); + + for( size_t i=1 ; i <= cnt ; ++i ) + { + lua_rawgeti( L, -1, i ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: wrong table index %s %d", key, i); + return 0; + } + + lua_pushstring( L, "min" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no min value set %s", key); + return 0; + } + + const int min = static_cast(lua_tonumber(L, -1)); + + lua_pop(L, 1); + + lua_pushstring( L, "max" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no max value set %s", key); + return 0; + } + + const int max = static_cast(lua_tonumber(L, -1)); + + lua_pop(L, 1); + + if (min <= val && val <= max) + { + lua_pushstring( L, "pct" ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no pct value set %s", key); + return 0; + } + + const int pct = static_cast(lua_tonumber(L, -1)); + + lua_settop( L, stack_top ); + + return pct; + } + + lua_pop(L, 1); + } + + lua_settop( L, stack_top ); + + return 0; +} + +unsigned int BlueDragon_GetIndexFactor(const char* container, const size_t idx, const char* key) +{ + lua_State* L = quest::CQuestManager::instance().GetLuaState(); + + const int stack_top = lua_gettop(L); + + lua_getglobal( L, "BlueDragonSetting" ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + return 0; + } + + lua_pushstring( L, container ); + lua_gettable( L, -2 ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no required table %s", key); + return 0; + } + + lua_rawgeti( L, -1, idx ); + + if (false == lua_istable(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: wrong table index %s %d", key, idx); + return 0; + } + + lua_pushstring( L, key ); + lua_gettable( L, -2 ); + + if (false == lua_isnumber(L, -1)) + { + lua_settop( L, stack_top ); + + sys_err("BlueDragon: no min value set %s", key); + return 0; + } + + const unsigned int ret = static_cast(lua_tonumber(L, -1)); + + lua_settop( L, stack_top ); + + return ret; +} + diff --git a/game/src/BlueDragon_Binder.h b/game/src/BlueDragon_Binder.h new file mode 100644 index 0000000..b87ceec --- /dev/null +++ b/game/src/BlueDragon_Binder.h @@ -0,0 +1,13 @@ + +enum BLUEDRAGON_STONE_EFFECT +{ + DEF_BONUS = 1, + ATK_BONUS = 2, + REGEN_TIME_BONUS = 3, + REGEN_PECT_BONUS = 4, +}; + +extern unsigned int BlueDragon_GetRangeFactor (const char* key, const int val); +extern unsigned int BlueDragon_GetSkillFactor (const size_t cnt, ...); +extern unsigned int BlueDragon_GetIndexFactor (const char* container, const size_t idx, const char* key); + diff --git a/game/src/BlueDragon_Skill.h b/game/src/BlueDragon_Skill.h new file mode 100644 index 0000000..12c3690 --- /dev/null +++ b/game/src/BlueDragon_Skill.h @@ -0,0 +1,342 @@ + +struct FSkillBreath +{ + EJobs Set1; + EJobs Set2; + ESex gender; + LPCHARACTER pAttacker; + + FSkillBreath(LPCHARACTER p) + { + pAttacker = p; + + Set1 = static_cast(number(0,3)); + Set2 = static_cast(number(0,3)); + gender = static_cast(number(0,2)); + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill0", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int overlapDamageCount = 0; + + int pct = 0; + if (ch->GetJob() == Set1) + { + const char* ptr = NULL; + + switch ( Set1 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; + + default: + case JOB_MAX_NUM: return; + } + + + int firstDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max")); + pct += firstDamagePercent; + + if (firstDamagePercent > 0) + overlapDamageCount++; + } + + if (ch->GetJob() == Set2) + { + const char* ptr = NULL; + + switch ( Set2 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; + + default: + case JOB_MAX_NUM: return; + } + + int secondDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max")); + pct += secondDamagePercent; + + if (secondDamagePercent > 0) + overlapDamageCount++; + } + + if (GET_SEX(ch) == gender) + { + const char* ptr = NULL; + + switch (gender) + { + case SEX_MALE: ptr = "male"; break; + case SEX_FEMALE: ptr = "female"; break; + default: return; + } + + int thirdDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "max")); + pct += thirdDamagePercent; + + if (thirdDamagePercent > 0) + overlapDamageCount++; + } + + switch (overlapDamageCount) + { + case 1: + ch->EffectPacket(SE_PERCENT_DAMAGE1); + break; + case 2: + ch->EffectPacket(SE_PERCENT_DAMAGE2); + break; + case 3: + ch->EffectPacket(SE_PERCENT_DAMAGE3); + break; + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + pct += addPct; + + int dam = number(BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "max")); + + dam += (dam * addPct) / 100; + dam += (ch->GetMaxHP() * pct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE ); + + sys_log(0, "BlueDragon: Breath to %s pct(%d) dam(%d) overlap(%d)", ch->GetName(), pct, dam, overlapDamageCount); + } + } + } + } +}; + +struct FSkillWeakBreath +{ + LPCHARACTER pAttacker; + + FSkillWeakBreath(LPCHARACTER p) + { + pAttacker = p; + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill1", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + int dam = number( BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "max") ); + dam += (dam * addPct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE ); + + sys_log(0, "BlueDragon: WeakBreath to %s addPct(%d) dam(%d)", ch->GetName(), addPct, dam); + } + } + } + } +}; + +struct FSkillEarthQuake +{ + EJobs Set1; + EJobs Set2; + ESex gender; + long MaxDistance; + LPCHARACTER pAttacker; + LPCHARACTER pFarthestChar; + + FSkillEarthQuake(LPCHARACTER p) + { + pAttacker = p; + + MaxDistance = 0; + pFarthestChar = NULL; + + Set1 = static_cast(number(0,3)); + Set2 = static_cast(number(0,3)); + gender = static_cast(number(0,2)); + } + + void operator()(LPENTITY ent) + { + if (NULL != ent) + { + if (true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = static_cast(ent); + + if (true == ch->IsPC() && false == ch->IsDead()) + { + if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE)) + return; + + if ((signed)BlueDragon_GetSkillFactor(2, "Skill2", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY())) + { + sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) ); + return; + } + + int sec = number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "max")); + + if (ch->GetJob() == Set1) + { + const char* ptr = NULL; + + switch ( Set1 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; + + default: + case JOB_MAX_NUM: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max")); + } + + if (ch->GetJob() == Set2) + { + const char* ptr = NULL; + + switch ( Set2 ) + { + case JOB_WARRIOR: ptr = "musa"; break; + case JOB_ASSASSIN: ptr = "assa"; break; + case JOB_SURA: ptr = "sura"; break; + case JOB_SHAMAN: ptr = "muda"; break; + + default: + case JOB_MAX_NUM: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max")); + } + + if (GET_SEX(ch) == gender) + { + const char* ptr = NULL; + + switch (gender) + { + case SEX_MALE: ptr = "male"; break; + case SEX_FEMALE: ptr = "female"; break; + default: return; + } + + sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "max")); + } + + int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct()); + + int dam = number( BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "max") ); + dam += (dam * addPct) / 100; + + ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE); + + SkillAttackAffect( ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" ); + + sys_log(0, "BlueDragon: EarthQuake to %s addPct(%d) dam(%d) sec(%d)", ch->GetName(), addPct, dam, sec); + + + VECTOR vec; + vec.x = static_cast(pAttacker->GetX() - ch->GetX()); + vec.y = static_cast(pAttacker->GetY() - ch->GetY()); + vec.z = 0.0f; + + Normalize( &vec, &vec ); + + const int nFlyDistance = 1000; + + long tx = ch->GetX() + vec.x * nFlyDistance; + long ty = ch->GetY() + vec.y * nFlyDistance; + + for (int i=0 ; i < 5 ; ++i) + { + if (true == SECTREE_MANAGER::instance().IsMovablePosition( ch->GetMapIndex(), tx, ty )) + { + break; + } + + switch( i ) + { + case 0: + tx = ch->GetX() + vec.x * nFlyDistance * -1; + ty = ch->GetY() + vec.y * nFlyDistance * -1; + break; + case 1: + tx = ch->GetX() + vec.x * nFlyDistance * -1; + ty = ch->GetY() + vec.y * nFlyDistance; + break; + case 2: + tx = ch->GetX() + vec.x * nFlyDistance; + ty = ch->GetY() + vec.y * nFlyDistance * -1; + break; + case 3: + tx = ch->GetX() + vec.x * number(1,100); + ty = ch->GetY() + vec.y * number(1,100); + break; + case 4: + tx = ch->GetX() + vec.x * number(1,10); + ty = ch->GetY() + vec.y * number(1,10); + break; + } + } + + ch->Sync( tx , ty ); + ch->Goto( tx , ty ); + ch->CalculateMoveDuration(); + + ch->SyncPacket(); + + long dist = DISTANCE_APPROX( pAttacker->GetX() - ch->GetX(), pAttacker->GetY() - ch->GetY() ); + + if (dist > MaxDistance) + { + MaxDistance = dist; + pFarthestChar = ch; + } + } + } + } + } +}; + diff --git a/game/src/ClientPackageCryptInfo.cpp b/game/src/ClientPackageCryptInfo.cpp new file mode 100644 index 0000000..eb7ea0b --- /dev/null +++ b/game/src/ClientPackageCryptInfo.cpp @@ -0,0 +1,225 @@ +#include "stdafx.h" +#include "ClientPackageCryptInfo.h" +#include "../../common/stl.h" + +#ifndef __FreeBSD__ +#include "../../libthecore/include/xdirent.h" +#endif + +CClientPackageCryptInfo::CClientPackageCryptInfo() : m_pSerializedCryptKeyStream(NULL), m_nCryptKeyPackageCnt(0) +{ +} + +CClientPackageCryptInfo::~CClientPackageCryptInfo() +{ + m_vecPackageCryptKeys.clear(); + m_mapPackageSDB.clear(); + if( m_pSerializedCryptKeyStream ) + { + delete[] m_pSerializedCryptKeyStream; + m_pSerializedCryptKeyStream = NULL; + } +} + +bool CClientPackageCryptInfo::LoadPackageCryptFile( const char* pCryptFile ) +{ + FILE * fp = fopen(pCryptFile, "rb"); + + if (!fp) + return false; + + int iSDBDataOffset; + fread(&iSDBDataOffset, sizeof(int), 1, fp); + + int iPackageCnt; + fread( &iPackageCnt, sizeof(int), 1, fp ); + m_nCryptKeyPackageCnt += iPackageCnt; + + int iCryptKeySize = iSDBDataOffset - 2*sizeof(int); + + { + + if (0 == iCryptKeySize) + { + sys_log(0, "[PackageCryptInfo] failed to load crypt key. (file: %s, key size: %d)", pCryptFile, iCryptKeySize); + m_nCryptKeyPackageCnt -= iPackageCnt; + } + else + { + int nCurKeySize = (int)m_vecPackageCryptKeys.size(); + m_vecPackageCryptKeys.resize( nCurKeySize + sizeof(int) + iCryptKeySize); + + memcpy( &m_vecPackageCryptKeys[nCurKeySize], &iCryptKeySize, sizeof(int)); + fread( &m_vecPackageCryptKeys[nCurKeySize + sizeof(int)], sizeof(BYTE), iCryptKeySize, fp ); + sys_log(0, "[PackageCryptInfo] %s loaded. (key size: %d, count: %d, total: %d)", pCryptFile, iCryptKeySize, iPackageCnt, m_nCryptKeyPackageCnt); + } + } + + //about SDB data + //total packagecnt (4byte) + // for packagecnt + // db name hash 4byte( stl.h stringhash ) +child node size(4byte) + + //stream to client + // sdb file cnt( 4byte ) + // for sdb file cnt + // filename hash ( stl.h stringhash ) + // related map name size(4), relate map name + // sdb block size( 1byte ) + // sdb blocks + + int iSDBPackageCnt; + fread(&iSDBPackageCnt, sizeof(int), 1, fp); + + DWORD dwPackageNameHash, dwPackageStreamSize, dwSDBFileCnt, dwFileNameHash, dwMapNameSize; + + std::string strRelatedMapName; + + if (0 == iCryptKeySize && 0 == iSDBPackageCnt) + return false; + + for( int i = 0; i < iSDBPackageCnt; ++i ) + { + fread(&dwPackageNameHash, sizeof(DWORD), 1, fp); + fread(&dwPackageStreamSize, sizeof(DWORD), 1, fp); + + fread(&dwSDBFileCnt, sizeof(DWORD), 1, fp); + + sys_log(0, "[PackageCryptInfo] SDB Loaded. (Name Hash : %d, Stream Size: %d, File Count: %d)", dwPackageNameHash,dwPackageStreamSize, dwSDBFileCnt); + + for( int j = 0; j < (int)dwSDBFileCnt; ++j ) + { + fread(&dwFileNameHash, sizeof(DWORD), 1, fp); + fread(&dwMapNameSize, sizeof(DWORD), 1, fp); + + strRelatedMapName.resize( dwMapNameSize ); + fread(&strRelatedMapName[0], sizeof(BYTE), dwMapNameSize, fp); + + sys_log(0, "[PackageCryptInfo] \t SDB each file info loaded.(MapName: %s, NameHash: %X)", strRelatedMapName.c_str(), dwFileNameHash); + + BYTE bSDBStreamSize; + std::vector vecSDBStream; + fread(&bSDBStreamSize, sizeof(BYTE), 1, fp); + + vecSDBStream.resize(bSDBStreamSize); + fread(&vecSDBStream[0], sizeof(BYTE), bSDBStreamSize, fp); + + //reconstruct it + TPackageSDBMap::iterator it = m_mapPackageSDB.find( strRelatedMapName ); + if( it == m_mapPackageSDB.end() ) + { + TPerFileSDBInfo fileSDBInfo; + m_mapPackageSDB[strRelatedMapName] = fileSDBInfo; + } + + TSupplementaryDataBlockInfo SDBInfo; + std::vector& rSDBInfos = m_mapPackageSDB[strRelatedMapName].vecSDBInfos; + { + SDBInfo.dwPackageIdentifier = dwPackageNameHash; + SDBInfo.dwFileIdentifier = dwFileNameHash; + SDBInfo.vecSDBStream.resize( bSDBStreamSize ); + + memcpy(&SDBInfo.vecSDBStream[0], &vecSDBStream[0], bSDBStreamSize ); + + rSDBInfos.push_back( SDBInfo ); + } + } + } + + fclose(fp); + return true; +} + + +bool CClientPackageCryptInfo::LoadPackageCryptInfo( const char* pCryptInfoDir ) +{ + DIR * pDir = opendir(pCryptInfoDir); + + if (!pDir) + return false; + + m_nCryptKeyPackageCnt = 0; + if( m_pSerializedCryptKeyStream ) + { + delete[] m_pSerializedCryptKeyStream; + m_pSerializedCryptKeyStream = NULL; + } + + m_mapPackageSDB.clear(); + m_vecPackageCryptKeys.clear(); + + const char szPrefixCryptInfoFile[] = "cshybridcrypt"; + + dirent * pDirEnt; + while ((pDirEnt = readdir(pDir))) + { + //if (strncmp( &(pDirEnt->d_name[0]), szPrefixCryptInfoFile, strlen(szPrefixCryptInfoFile)) ) + if (std::string::npos == std::string(pDirEnt->d_name).find(szPrefixCryptInfoFile)) + { + sys_log(0, "[PackageCryptInfo] %s is not crypt file. pass!", pDirEnt->d_name); + continue; + } + + std::string strFullPathName = std::string(pCryptInfoDir) + std::string(pDirEnt->d_name); + + sys_log(0, "[PackageCryptInfo] Try to load crypt file: %s", strFullPathName.c_str()); + + if (false == LoadPackageCryptFile( strFullPathName.c_str() )) + sys_err("[PackageCryptInfo] Failed to load %s", strFullPathName.c_str()); + } + + closedir(pDir); + return true; +} + +void CClientPackageCryptInfo::GetPackageCryptKeys( BYTE** ppData, int& iDataSize ) +{ + int nCryptKeySize = m_vecPackageCryptKeys.size(); + int iStreamSize = sizeof(int)+nCryptKeySize; + + //NOTE : Crypt Key Info isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated ) + //it`s not safe but due to performance benefit we don`t do re-serialize. + if( m_pSerializedCryptKeyStream ) + { + *ppData = m_pSerializedCryptKeyStream; + iDataSize = iStreamSize; + return; + } + + if( nCryptKeySize > 0 ) + { + m_pSerializedCryptKeyStream = new BYTE[iStreamSize]; + memcpy(&m_pSerializedCryptKeyStream[0], &m_nCryptKeyPackageCnt, sizeof(int) ); + memcpy(&m_pSerializedCryptKeyStream[sizeof(int)], &m_vecPackageCryptKeys[0], nCryptKeySize ); + + *ppData = m_pSerializedCryptKeyStream; + iDataSize = iStreamSize; + } + else + { + *ppData = NULL; + iDataSize = 0; + } +} + + +bool CClientPackageCryptInfo::GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize ) +{ + std::string strLowerMapName = pMapName; + stl_lowers(strLowerMapName); + + TPackageSDBMap::iterator it = m_mapPackageSDB.find( strLowerMapName.c_str() ); + if( it == m_mapPackageSDB.end() || it->second.vecSDBInfos.size() == 0 ) + { + //sys_err("GetRelatedMapSDBStreams Failed(%s)", strLowerMapName.c_str()); + return false; + } + + *ppData = it->second.GetSerializedStream(); + iDataSize = it->second.GetSize(); + + //sys_log(0, "GetRelatedMapSDBStreams Size(%d)", iDataSize); + + return true; +} + diff --git a/game/src/ClientPackageCryptInfo.h b/game/src/ClientPackageCryptInfo.h new file mode 100644 index 0000000..662a325 --- /dev/null +++ b/game/src/ClientPackageCryptInfo.h @@ -0,0 +1,117 @@ +#ifndef __INC_CLIENTPACKAGE_CRYPTINFO_H +#define __INC_CLIENTPACKAGE_CRYPTINFO_H + +#include + +#pragma pack(1) + +typedef struct SSupplementaryDataBlockInfo +{ + DWORD dwPackageIdentifier; + DWORD dwFileIdentifier; + std::vector vecSDBStream; + + void Serialize( BYTE* pStream ) + { + memcpy(pStream, &dwPackageIdentifier, sizeof(DWORD)); + memcpy(pStream+4, &dwFileIdentifier, sizeof(DWORD)); + + BYTE bSize = vecSDBStream.size(); + memcpy(pStream+8, &bSize, sizeof(BYTE)); + memcpy(pStream+9, &vecSDBStream[0], bSize); + } + + DWORD GetSerializedSize() const + { + return sizeof(DWORD)*2 + sizeof(BYTE) + vecSDBStream.size(); + } + +} TSupplementaryDataBlockInfo; + +#pragma pack() + +class CClientPackageCryptInfo +{ +public: + CClientPackageCryptInfo(); + ~CClientPackageCryptInfo(); + + bool LoadPackageCryptInfo( const char* pCryptInfoDir ); + void GetPackageCryptKeys( BYTE** ppData, int& iDataSize ); + + bool GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize ); + +private: + bool LoadPackageCryptFile( const char* pCryptFile ); + +private: + int m_nCryptKeyPackageCnt; + std::vector m_vecPackageCryptKeys; + BYTE* m_pSerializedCryptKeyStream; + + typedef struct SPerFileSDBInfo + { + SPerFileSDBInfo() : m_pSerializedStream(NULL) {} + ~SPerFileSDBInfo() + { + if(m_pSerializedStream) + { + delete[]m_pSerializedStream; + } + } + + DWORD GetSize() const + { + DWORD dwSize = 4; //initial vecSDBInfo count + + for(int i = 0; i < (int)vecSDBInfos.size(); ++i) + { + dwSize += vecSDBInfos[i].GetSerializedSize(); + } + + return dwSize; + } + + BYTE* GetSerializedStream() + { + //NOTE : SDB Data isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated ) + //it`s not safe but due to performance benefit we don`t do re-serialize. + if(m_pSerializedStream) + return m_pSerializedStream; + + m_pSerializedStream = new BYTE[GetSize()]; + + int iWrittenOffset = 0; + int iSDBInfoSize = vecSDBInfos.size(); + + //write size + memcpy( m_pSerializedStream, &iSDBInfoSize, sizeof(int) ); + iWrittenOffset += sizeof(int); + for(int i = 0; i < iSDBInfoSize; ++i) + { + vecSDBInfos[i].Serialize( m_pSerializedStream + iWrittenOffset ); + iWrittenOffset += vecSDBInfos[i].GetSerializedSize(); + } + + return m_pSerializedStream; + } + + std::vector vecSDBInfos; + + private: + BYTE* m_pSerializedStream; + + } TPerFileSDBInfo; + + typedef boost::unordered_map TPackageSDBMap; //key: related map name + TPackageSDBMap m_mapPackageSDB; + + +}; + + + + + + +#endif //__INC_CLIENTPACKAGE_CRYPTINFO_H \ No newline at end of file diff --git a/game/src/DragonLair.cpp b/game/src/DragonLair.cpp new file mode 100644 index 0000000..9535a83 --- /dev/null +++ b/game/src/DragonLair.cpp @@ -0,0 +1,255 @@ + +#include "stdafx.h" + +#include "DragonLair.h" + +#include "entity.h" +#include "sectree_manager.h" +#include "char.h" +#include "guild.h" +#include "locale_service.h" +#include "regen.h" +#include "log.h" +#include "utils.h" + +extern int passes_per_sec; + +struct FWarpToDragronLairWithGuildMembers +{ + DWORD dwGuildID; + long mapIndex; + long x, y; + + FWarpToDragronLairWithGuildMembers( DWORD guildID, long map, long X, long Y ) + : dwGuildID(guildID), mapIndex(map), x(X), y(Y) + { + } + + void operator()(LPENTITY ent) + { + if (NULL != ent && true == ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER pChar = static_cast(ent); + + if (true == pChar->IsPC()) + { + if (NULL != pChar->GetGuild()) + { + if (dwGuildID == pChar->GetGuild()->GetID()) + { + pChar->WarpSet(x, y, mapIndex); + } + } + } + } + } +}; + +struct FWarpToVillage +{ + void operator() (LPENTITY ent) + { + if (NULL != ent) + { + LPCHARACTER pChar = static_cast(ent); + + if (NULL != pChar) + { + if (true == pChar->IsPC()) + { + pChar->GoHome(); + } + } + } + } +}; + +EVENTINFO(tag_DragonLair_Collapse_EventInfo) +{ + int step; + CDragonLair* pLair; + long InstanceMapIndex; + + tag_DragonLair_Collapse_EventInfo() + : step( 0 ) + , pLair( 0 ) + , InstanceMapIndex( 0 ) + { + } +}; + +EVENTFUNC( DragonLair_Collapse_Event ) +{ + tag_DragonLair_Collapse_EventInfo* pInfo = dynamic_cast(event->info); + + if ( pInfo == NULL ) + { + sys_err( "DragonLair_Collapse_Event> Null pointer" ); + return 0; + } + + if (0 == pInfo->step) + { + char buf[512]; + snprintf(buf, 512, LC_TEXT("밡 %d ʸ ׾ȿФ"), pInfo->pLair->GetEstimatedTime()); + SendNoticeMap(buf, pInfo->InstanceMapIndex, true); + + pInfo->step++; + + return PASSES_PER_SEC( 30 ); + } + else if (1 == pInfo->step) + { + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pInfo->InstanceMapIndex ); + + if (NULL != pMap) + { + FWarpToVillage f; + pMap->for_each( f ); + } + + pInfo->step++; + + return PASSES_PER_SEC( 30 ); + } + else + { + SECTREE_MANAGER::instance().DestroyPrivateMap( pInfo->InstanceMapIndex ); + M2_DELETE(pInfo->pLair); + } + + return 0; +} + + + + + + + + + +CDragonLair::CDragonLair(DWORD guildID, long BaseMapID, long PrivateMapID) + : GuildID_(guildID), BaseMapIndex_(BaseMapID), PrivateMapIndex_(PrivateMapID) +{ + StartTime_ = get_global_time(); +} + +CDragonLair::~CDragonLair() +{ +} + +DWORD CDragonLair::GetEstimatedTime() const +{ + return get_global_time() - StartTime_; +} + +void CDragonLair::OnDragonDead(LPCHARACTER pDragon) +{ + sys_log(0, "DragonLair: ׾ȿ"); + + LogManager::instance().DragonSlayLog( GuildID_, pDragon->GetMobTable().dwVnum, StartTime_, get_global_time() ); +} + + + + + + + + + + + + +CDragonLairManager::CDragonLairManager() +{ +} + +CDragonLairManager::~CDragonLairManager() +{ +} + +bool CDragonLairManager::Start(long MapIndexFrom, long BaseMapIndex, DWORD GuildID) +{ + long instanceMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(BaseMapIndex); + if (instanceMapIndex == 0) { + sys_err("CDragonLairManager::Start() : no private map index available"); + return false; + } + + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(MapIndexFrom); + + if (NULL != pMap) + { + LPSECTREE_MAP pTargetMap = SECTREE_MANAGER::instance().GetMap(BaseMapIndex); + + if (NULL == pTargetMap) + { + return false; + } + + const TMapRegion* pRegionInfo = SECTREE_MANAGER::instance().GetMapRegion( pTargetMap->m_setting.iIndex ); + + if (NULL != pRegionInfo) + { + FWarpToDragronLairWithGuildMembers f(GuildID, instanceMapIndex, 844000, 1066900); + + pMap->for_each( f ); + + LairMap_.insert( std::make_pair(GuildID, M2_NEW CDragonLair(GuildID, BaseMapIndex, instanceMapIndex)) ); + + std::string strMapBasePath( LocaleService_GetMapPath() ); + + strMapBasePath += "/" + pRegionInfo->strMapName + "/instance_regen.txt"; + + sys_log(0, "%s", strMapBasePath.c_str()); + + regen_do(strMapBasePath.c_str(), instanceMapIndex, pTargetMap->m_setting.iBaseX, pTargetMap->m_setting.iBaseY, NULL, true); + + return true; + } + } + + return false; +} + +void CDragonLairManager::OnDragonDead(LPCHARACTER pDragon, DWORD KillerGuildID) +{ + if (NULL == pDragon) + return; + + if (false == pDragon->IsMonster()) + return; + + boost::unordered_map::iterator iter = LairMap_.find( KillerGuildID ); + + if (LairMap_.end() == iter) + { + return; + } + + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pDragon->GetMapIndex() ); + + if (NULL == iter->second || NULL == pMap) + { + LairMap_.erase( iter ); + return; + } + + iter->second->OnDragonDead( pDragon ); + + // ֵ ֱ + + tag_DragonLair_Collapse_EventInfo* info; + info = AllocEventInfo(); + + info->step = 0; + info->pLair = iter->second; + info->InstanceMapIndex = pDragon->GetMapIndex(); + + event_create(DragonLair_Collapse_Event, info, PASSES_PER_SEC(10)); + + LairMap_.erase( iter ); +} + diff --git a/game/src/DragonLair.h b/game/src/DragonLair.h new file mode 100644 index 0000000..081b1a5 --- /dev/null +++ b/game/src/DragonLair.h @@ -0,0 +1,37 @@ + +#include + +#include "../../common/stl.h" + +class CDragonLair +{ + public: + CDragonLair (DWORD dwGuildID, long BaseMapID, long PrivateMapID); + virtual ~CDragonLair (); + + DWORD GetEstimatedTime () const; + + void OnDragonDead (LPCHARACTER pDragon); + + private: + DWORD StartTime_; + DWORD GuildID_; + long BaseMapIndex_; + long PrivateMapIndex_; +}; + +class CDragonLairManager : public singleton +{ + public: + CDragonLairManager (); + virtual ~CDragonLairManager (); + + bool Start (long MapIndexFrom, long BaseMapIndex, DWORD GuildID); + void OnDragonDead (LPCHARACTER pDragon, DWORD KillerGuildID); + + size_t GetLairCount () const { return LairMap_.size(); } + + private: + boost::unordered_map LairMap_; +}; + diff --git a/game/src/DragonSoul.cpp b/game/src/DragonSoul.cpp new file mode 100644 index 0000000..6e526b7 --- /dev/null +++ b/game/src/DragonSoul.cpp @@ -0,0 +1,1151 @@ +#include "stdafx.h" +#include "constants.h" +#include "item.h" +#include "item_manager.h" +#include "unique_item.h" +#include "packet.h" +#include "desc.h" +#include "char.h" +#include "dragon_soul_table.h" +#include "log.h" +#include "DragonSoul.h" +#include + +typedef std::vector TTokenVector; + +int Gamble(std::vector& vec_probs) +{ + float range = 0.f; + for (int i = 0; i < vec_probs.size(); i++) + { + range += vec_probs[i]; + } + float fProb = fnumber(0.f, range); + float sum = 0.f; + for (int idx = 0; idx < vec_probs.size(); idx++) + { + sum += vec_probs[idx]; + if (sum >= fProb) + return idx; + } + return -1; +} + +// ġ ̺(prob_lst) ޾ random_set.size() index Ͽ random_set return +bool MakeDistinctRandomNumberSet(std::list prob_lst, OUT std::vector& random_set) +{ + int size = prob_lst.size(); + int n = random_set.size(); + if (size < n) + return false; + + std::vector select_bit(size, 0); + for (int i = 0; i < n; i++) + { + float range = 0.f; + for (std::list ::iterator it = prob_lst.begin(); it != prob_lst.end(); it++) + { + range += *it; + } + float r = fnumber (0.f, range); + float sum = 0.f; + int idx = 0; + for (std::list ::iterator it = prob_lst.begin(); it != prob_lst.end(); it++) + { + while (select_bit[idx++]); + + sum += *it; + if (sum >= r) + { + select_bit[idx - 1] = 1; + random_set[i] = idx - 1; + prob_lst.erase(it); + break; + } + } + } + return true; +} + +/* ȥ Vnum comment + * ITEM VNUM 10 ڸ, FEDCBA Ѵٸ + * FE : ȥ . D : + * C : ܰ B : ȭ + * A : ȣ... + */ + +BYTE GetType(DWORD dwVnum) +{ + return (dwVnum / 10000); +} + +BYTE GetGradeIdx(DWORD dwVnum) +{ + return (dwVnum / 1000) % 10; +} + +BYTE GetStepIdx(DWORD dwVnum) +{ + return (dwVnum / 100) % 10; +} + +BYTE GetStrengthIdx(DWORD dwVnum) +{ + return (dwVnum / 10) % 10; +} + +bool DSManager::ReadDragonSoulTableFile(const char * c_pszFileName) +{ + m_pTable = new DragonSoulTable(); + return m_pTable->ReadDragonSoulTableFile(c_pszFileName); +} + +void DSManager::GetDragonSoulInfo(DWORD dwVnum, BYTE& bType, BYTE& bGrade, BYTE& bStep, BYTE& bStrength) const +{ + bType = GetType(dwVnum); + bGrade = GetGradeIdx(dwVnum); + bStep = GetStepIdx(dwVnum); + bStrength = GetStrengthIdx(dwVnum); +} + +bool DSManager::IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const +{ + if (NULL == pItem) + return false; + + WORD wBaseCell = GetBasePosition(pItem); + if (WORD_MAX == wBaseCell) + return false; + + if (Cell.window_type != DRAGON_SOUL_INVENTORY + || (Cell.cell < wBaseCell || Cell.cell >= wBaseCell + DRAGON_SOUL_BOX_SIZE)) + { + return false; + } + else + return true; + +} + +WORD DSManager::GetBasePosition(const LPITEM pItem) const +{ + if (NULL == pItem) + return WORD_MAX; + + BYTE type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pItem->GetVnum(), type, grade_idx, step_idx, strength_idx); + + BYTE col_type = pItem->GetSubType(); + BYTE row_type = grade_idx; + if (row_type > DRAGON_SOUL_GRADE_MAX) + return WORD_MAX; + + return col_type * DRAGON_SOUL_STEP_MAX * DRAGON_SOUL_BOX_SIZE + row_type * DRAGON_SOUL_BOX_SIZE; +} + +bool DSManager::RefreshItemAttributes(LPITEM pDS) +{ + if (!pDS->IsDragonSoul()) + { + sys_err ("This item(ID : %d) is not DragonSoul.", pDS->GetID()); + return false; + } + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pDS->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + DragonSoulTable::TVecApplys vec_basic_applys; + DragonSoulTable::TVecApplys vec_addtional_applys; + + if (!m_pTable->GetBasicApplys(ds_type, vec_basic_applys)) + { + sys_err ("There is no BasicApply about %d type dragon soul.", ds_type); + return false; + } + + if (!m_pTable->GetAdditionalApplys(ds_type, vec_addtional_applys)) + { + sys_err ("There is no AdditionalApply about %d type dragon soul.", ds_type); + return false; + } + + // add_min add_max ̷ . + int basic_apply_num, add_min, add_max; + if (!m_pTable->GetApplyNumSettings(ds_type, grade_idx, basic_apply_num, add_min, add_max)) + { + sys_err ("In ApplyNumSettings, INVALID VALUES Group type(%d), GRADE idx(%d)", ds_type, grade_idx); + return false; + } + + float fWeight = 0.f; + if (!m_pTable->GetWeight(ds_type, grade_idx, step_idx, strength_idx, fWeight)) + { + return false; + } + fWeight /= 100.f; + + int n = MIN(basic_apply_num, vec_basic_applys.size()); + for (int i = 0; i < n; i++) + { + const SApply& basic_apply = vec_basic_applys[i]; + BYTE bType = basic_apply.apply_type; + short sValue = (short)(ceil((float)basic_apply.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(i, bType, sValue); + } + + for (int i = DRAGON_SOUL_ADDITIONAL_ATTR_START_IDX; i < ITEM_ATTRIBUTE_MAX_NUM; i++) + { + BYTE bType = pDS->GetAttributeType(i); + short sValue = 0; + if (APPLY_NONE == bType) + continue; + for (int j = 0; j < vec_addtional_applys.size(); j++) + { + if (vec_addtional_applys[j].apply_type == bType) + { + sValue = vec_addtional_applys[j].apply_value; + break; + } + } + pDS->SetForceAttribute(i, bType, (short)(ceil((float)sValue * fWeight - 0.01f))); + } + return true; +} + +bool DSManager::PutAttributes(LPITEM pDS) +{ + if (!pDS->IsDragonSoul()) + { + sys_err ("This item(ID : %d) is not DragonSoul.", pDS->GetID()); + return false; + } + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pDS->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + DragonSoulTable::TVecApplys vec_basic_applys; + DragonSoulTable::TVecApplys vec_addtional_applys; + + if (!m_pTable->GetBasicApplys(ds_type, vec_basic_applys)) + { + sys_err ("There is no BasicApply about %d type dragon soul.", ds_type); + return false; + } + if (!m_pTable->GetAdditionalApplys(ds_type, vec_addtional_applys)) + { + sys_err ("There is no AdditionalApply about %d type dragon soul.", ds_type); + return false; + } + + + int basic_apply_num, add_min, add_max; + if (!m_pTable->GetApplyNumSettings(ds_type, grade_idx, basic_apply_num, add_min, add_max)) + { + sys_err ("In ApplyNumSettings, INVALID VALUES Group type(%d), GRADE idx(%d)", ds_type, grade_idx); + return false; + } + + float fWeight = 0.f; + if (!m_pTable->GetWeight(ds_type, grade_idx, step_idx, strength_idx, fWeight)) + { + return false; + } + fWeight /= 100.f; + + int n = MIN(basic_apply_num, vec_basic_applys.size()); + for (int i = 0; i < n; i++) + { + const SApply& basic_apply = vec_basic_applys[i]; + BYTE bType = basic_apply.apply_type; + short sValue = (short)(ceil((float)basic_apply.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(i, bType, sValue); + } + + BYTE additional_attr_num = MIN(number (add_min, add_max), 3); + + std::vector random_set; + if (additional_attr_num > 0) + { + random_set.resize(additional_attr_num); + std::list list_probs; + for (int i = 0; i < vec_addtional_applys.size(); i++) + { + list_probs.push_back(vec_addtional_applys[i].prob); + } + if (!MakeDistinctRandomNumberSet(list_probs, random_set)) + { + sys_err ("MakeDistinctRandomNumberSet error."); + return false; + } + + for (int i = 0; i < additional_attr_num; i++) + { + int r = random_set[i]; + const SApply& additional_attr = vec_addtional_applys[r]; + BYTE bType = additional_attr.apply_type; + short sValue = (short)(ceil((float)additional_attr.apply_value * fWeight - 0.01f)); + + pDS->SetForceAttribute(DRAGON_SOUL_ADDITIONAL_ATTR_START_IDX + i, bType, sValue); + } + } + + return true; +} + +bool DSManager::DragonSoulItemInitialize(LPITEM pItem) +{ + if (NULL == pItem || !pItem->IsDragonSoul()) + return false; + PutAttributes(pItem); + int time = DSManager::instance().GetDuration(pItem); + if (time > 0) + pItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, time); + return true; +} + +DWORD DSManager::MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine) +{ + return bType * 10000 + grade * 1000 + step * 100 + refine * 10; +} + +int DSManager::GetDuration(const LPITEM pItem) const +{ + return pItem->GetDuration(); +} + +// ȥ ޾Ƽ ϴ Լ +bool DSManager::ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor) +{ + if (NULL == ch || NULL == pItem) + return false; + if (pItem->IsEquipped()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȥ ϴ.")); + return false; + } + + DWORD dwVnum = pItem->GetVnum(); + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(dwVnum, ds_type, grade_idx, step_idx, strength_idx); + + int iBonus = 0; + + if (NULL != pExtractor) + { + iBonus = pExtractor->GetValue(0); + } + + std::vector vec_chargings; + std::vector vec_probs; + + if (!m_pTable->GetDragonHeartExtValues(ds_type, grade_idx, vec_chargings, vec_probs)) + { + return false; + } + + int idx = Gamble(vec_probs); + + float sum = 0.f; + if (-1 == idx) + { + sys_err ("Gamble is failed. ds_type(%d), grade_idx(%d)", ds_type, grade_idx); + return false; + } + + float fCharge = vec_chargings[idx] * (100 + iBonus) / 100.f; + fCharge = std::MINMAX (0.f, fCharge, 100.f); + + if (fCharge < FLT_EPSILON) + { + pItem->SetCount(pItem->GetCount() - 1); + if (NULL != pExtractor) + { + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_FAIL", ""); + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ⿡ Ͽϴ.")); + return false; + } + else + { + LPITEM pDH = ITEM_MANAGER::instance().CreateItem(DRAGON_HEART_VNUM); + + if (NULL == pDH) + { + sys_err ("Cannot create DRAGON_HEART(%d).", DRAGON_HEART_VNUM); + return NULL; + } + + pItem->SetCount(pItem->GetCount() - 1); + if (NULL != pExtractor) + { + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + + int iCharge = (int)(fCharge + 0.5f); + pDH->SetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX, iCharge); + ch->AutoGiveItem(pDH, true); + + std::string s = boost::lexical_cast (iCharge); + s += "%s"; + LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_SUCCESS", s.c_str()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ⿡ Ͽϴ.")); + return true; + } +} + +// Ư ȥ â θ ϰ, н λ깰 ִ Լ. +bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM pExtractor) +{ + if (NULL == ch || NULL == pItem) + { + sys_err ("NULL POINTER. ch(%p) or pItem(%p)", ch, pItem); + return false; + } + + // ǥ ġ valid ˻ , valid ʴٸ ã´. + if (!IsValidCellForThisItem(pItem, DestCell)) + { + int iEmptyCell = ch->GetEmptyDragonSoulInventory(pItem); + if (iEmptyCell < 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + return false; + } + else + { + DestCell.window_type = DRAGON_SOUL_INVENTORY; + DestCell.cell = iEmptyCell; + } + } + + if (!pItem->IsEquipped() || !pItem->RemoveFromCharacter()) + return false; + + bool bSuccess; + DWORD dwByProduct = 0; + int iBonus = 0; + float fProb; + float fDice; + // ȥ . + { + DWORD dwVnum = pItem->GetVnum(); + + BYTE ds_type, grade_idx, step_idx, strength_idx; + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + // ٸ ϴ ϴ ̶ . + if (!m_pTable->GetDragonSoulExtValues(ds_type, grade_idx, fProb, dwByProduct)) + { + pItem->AddToCharacter(ch, DestCell); + return true; + } + + + if (NULL != pExtractor) + { + iBonus = pExtractor->GetValue(ITEM_VALUE_DRAGON_SOUL_POLL_OUT_BONUS_IDX); + pExtractor->SetCount(pExtractor->GetCount() - 1); + } + fDice = fnumber(0.f, 100.f); + bSuccess = fDice <= (fProb * (100 + iBonus) / 100.f); + } + + // ij ȥ ߰ Ȥ . λ깰 . + { + char buf[128]; + + if (bSuccess) + { + if (pExtractor) + { + sprintf(buf, "dice(%d) prob(%d + %d) EXTR(VN:%d)", (int)fDice, (int)fProb, iBonus, pExtractor->GetVnum()); + } + else + { + sprintf(buf, "dice(%d) prob(%d)", fDice, fProb); + } + LogManager::instance().ItemLog(ch, pItem, "DS_PULL_OUT_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + pItem->AddToCharacter(ch, DestCell); + return true; + } + else + { + if (pExtractor) + { + sprintf(buf, "dice(%d) prob(%d + %d) EXTR(VN:%d) ByProd(VN:%d)", (int)fDice, (int)fProb, iBonus, pExtractor->GetVnum(), dwByProduct); + } + else + { + sprintf(buf, "dice(%d) prob(%d) ByProd(VNUM:%d)", (int)fDice, (int)fProb, dwByProduct); + } + LogManager::instance().ItemLog(ch, pItem, "DS_PULL_OUT_FAILED", buf); + M2_DESTROY_ITEM(pItem); + pItem = NULL; + if (dwByProduct) + { + LPITEM pByProduct = ch->AutoGiveItem(dwByProduct, true); + if (pByProduct) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽ %s ϴ."), pByProduct->GetName()); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ⿡ Ͽϴ.")); + } + } + + return bSuccess; +} + +bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot upgrade dragon soul without refine window."); + return false; + } + + // Ȥó ߺǴ item pointer ֱ ؼ set + // ̻ Ŷ , ߺ TItemPos ְ, ߸ TItemPos ִ. + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + if (aItemPoses[i].IsEquipPosition()) + return false; + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (NULL != pItem) + { + // ȥ ƴ â . + if (!pItem->IsDragonSoul()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + + set_items.insert(pItem); + } + } + + if (set_items.size() == 0) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + return false; + } + + int count = set_items.size(); + int need_count = 0; + int fee = 0; + std::vector vec_probs; + float prob_sum; + + BYTE ds_type, grade_idx, step_idx, strength_idx; + int result_grade; + + // ó ȭ ´. + std::set ::iterator it = set_items.begin(); + { + LPITEM pItem = *it; + + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + if (!m_pTable->GetRefineGradeValues(ds_type, grade_idx, need_count, fee, vec_probs)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + } + while (++it != set_items.end()) + { + LPITEM pItem = *it; + + // Ŭ ui â ø ұ , + // ˸ ó . + if (pItem->IsEquipped()) + { + return false; + } + + if (ds_type != GetType(pItem->GetVnum()) || grade_idx != GetGradeIdx(pItem->GetVnum())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + + return false; + } + } + + // Ŭ󿡼 ѹ üũ ϱ count != need_count invalid Ŭ ɼ ũ. + if (count != need_count) + { + sys_err ("Possiblity of invalid client. Name %s", ch->GetName()); + BYTE bSubHeader = count < need_count? DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL : DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL; + SendRefineResultPacket(ch, bSubHeader, NPOS); + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + if (-1 == (result_grade = Gamble(vec_probs))) + { + sys_err ("Gamble failed. See RefineGardeTables' probabilities"); + return false; + } + + LPITEM pResultItem = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(ds_type, (BYTE)result_grade, 0, 0)); + + if (NULL == pResultItem) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(ds_type, (BYTE)result_grade, 0, 0)); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + int left_count = need_count; + + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + int n = pItem->GetCount(); + if (left_count > n) + { + pItem->RemoveFromCharacter(); + M2_DESTROY_ITEM(pItem); + left_count -= n; + } + else + { + pItem->SetCount(n - left_count); + } + } + + ch->AutoGiveItem(pResultItem, true); + + if (result_grade > grade_idx) + { + char buf[128]; + sprintf(buf, "GRADE : %d -> %d", grade_idx, result_grade); + LogManager::instance().ItemLog(ch, pResultItem, "DS_GRADE_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return true; + } + else + { + char buf[128]; + sprintf(buf, "GRADE : %d -> %d", grade_idx, result_grade); + LogManager::instance().ItemLog(ch, pResultItem, "DS_GRADE_REFINE_FAIL", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return false; + } +} + +bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot use dragon soul refine window."); + return false; + } + + // Ȥó ߺǴ item pointer ֱ ؼ set + // ̻ Ŷ , ߺ TItemPos ְ, ߸ TItemPos ִ. + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (NULL != pItem) + { + // ȥ ƴ â . + if (!pItem->IsDragonSoul()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + set_items.insert(pItem); + } + } + + if (set_items.size() == 0) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + return false; + } + + std::string stGroupName; + int count = set_items.size(); + int need_count = 0; + int fee = 0; + std::vector vec_probs; + + BYTE ds_type, grade_idx, step_idx, strength_idx; + int result_step; + + // ó ȭ ´. + std::set ::iterator it = set_items.begin(); + { + LPITEM pItem = *it; + GetDragonSoulInfo(pItem->GetVnum(), ds_type, grade_idx, step_idx, strength_idx); + + if (!m_pTable->GetRefineStepValues(ds_type, step_idx, need_count, fee, vec_probs)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + while(++it != set_items.end()) + { + LPITEM pItem = *it; + // Ŭ ui â ø ұ , + // ˸ ó . + if (pItem->IsEquipped()) + { + return false; + } + if (ds_type != GetType(pItem->GetVnum()) || grade_idx != GetGradeIdx(pItem->GetVnum()) || step_idx != GetStepIdx(pItem->GetVnum())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + // Ŭ󿡼 ѹ üũ ϱ count != need_count invalid Ŭ ɼ ũ. + if (count != need_count) + { + sys_err ("Possiblity of invalid client. Name %s", ch->GetName()); + BYTE bSubHeader = count < need_count? DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL : DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL; + SendRefineResultPacket(ch, bSubHeader, NPOS); + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + float sum = 0.f; + + if (-1 == (result_step = Gamble(vec_probs))) + { + sys_err ("Gamble failed. See RefineStepTables' probabilities"); + return false; + } + + LPITEM pResultItem = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(ds_type, grade_idx, (BYTE)result_step, 0)); + + if (NULL == pResultItem) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(ds_type, grade_idx, (BYTE)result_step, 0)); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + int left_count = need_count; + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + int n = pItem->GetCount(); + if (left_count > n) + { + pItem->RemoveFromCharacter(); + M2_DESTROY_ITEM(pItem); + left_count -= n; + } + else + { + pItem->SetCount(n - left_count); + } + } + + ch->AutoGiveItem(pResultItem, true); + if (result_step > step_idx) + { + char buf[128]; + sprintf(buf, "STEP : %d -> %d", step_idx, result_step); + LogManager::instance().ItemLog(ch, pResultItem, "DS_STEP_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return true; + } + else + { + char buf[128]; + sprintf(buf, "STEP : %d -> %d", step_idx, result_step); + LogManager::instance().ItemLog(ch, pResultItem, "DS_STEP_REFINE_FAIL", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ܰ ߽ϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell())); + return false; + } +} + +bool IsDragonSoulRefineMaterial(LPITEM pItem) +{ + if (pItem->GetType() != ITEM_MATERIAL) + return false; + return (pItem->GetSubType() == MATERIAL_DS_REFINE_NORMAL || + pItem->GetSubType() == MATERIAL_DS_REFINE_BLESSED || + pItem->GetSubType() == MATERIAL_DS_REFINE_HOLLY); +} + +bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]) +{ + if (NULL == ch) + return false; + if (NULL == aItemPoses) + { + return false; + } + + if (!ch->DragonSoul_RefineWindow_CanRefine()) + { + sys_err ("%s do not activate DragonSoulRefineWindow. But how can he come here?", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_INFO, "[SYSTEM ERROR]You cannot use dragon soul refine window."); + return false; + } + + // Ȥó ߺǴ item pointer ֱ ؼ set + // ̻ Ŷ , ߺ TItemPos ְ, ߸ TItemPos ִ. + std::set set_items; + for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++) + { + LPITEM pItem = ch->GetItem(aItemPoses[i]); + if (pItem) + { + set_items.insert(pItem); + } + } + if (set_items.size() == 0) + { + return false; + } + + int fee; + + LPITEM pRefineStone = NULL; + LPITEM pDragonSoul = NULL; + for (std::set ::iterator it = set_items.begin(); it != set_items.end(); it++) + { + LPITEM pItem = *it; + // Ŭ ui â ø ұ , + // ˸ ó . + if (pItem->IsEquipped()) + { + return false; + } + + // ȥ ȭ â ִ. + // ׸ ϳ ־Ѵ. + if (pItem->IsDragonSoul()) + { + if (pDragonSoul != NULL) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + pDragonSoul = pItem; + } + else if(IsDragonSoulRefineMaterial(pItem)) + { + if (pRefineStone != NULL) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_TOO_MUCH_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + pRefineStone = pItem; + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ʿ ᰡ ƴմϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell())); + return false; + } + } + + BYTE bType, bGrade, bStep, bStrength; + + if (!pDragonSoul || !pRefineStone) + { + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MATERIAL, NPOS); + + return false; + } + + if (NULL != pDragonSoul) + { + GetDragonSoulInfo(pDragonSoul->GetVnum(), bType, bGrade, bStep, bStrength); + + float fWeight = 0.f; + // ġ ٸ ȭ ȥ + if (!m_pTable->GetWeight(bType, bGrade, bStep, bStrength + 1, fWeight)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + return false; + } + // ȭ ġ 0̶ ̻ ȭǼ ȵȴ. + if (fWeight < FLT_EPSILON) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + return false; + } + } + + float fProb; + if (!m_pTable->GetRefineStrengthValues(bType, pRefineStone->GetSubType(), bStrength, fee, fProb)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ȥԴϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell())); + + return false; + } + + if (ch->GetGold() < fee) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS); + return false; + } + + ch->PointChange(POINT_GOLD, -fee); + LPITEM pResult = NULL; + BYTE bSubHeader; + + if (fnumber(0.f, 100.f) <= fProb) + { + pResult = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(bType, bGrade, bStep, bStrength + 1)); + if (NULL == pResult) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(bType, bGrade, bStep, bStrength + 1)); + return false; + } + pDragonSoul->RemoveFromCharacter(); + + pDragonSoul->CopyAttributeTo(pResult); + RefreshItemAttributes(pResult); + + pDragonSoul->SetCount(pDragonSoul->GetCount() - 1); + pRefineStone->SetCount(pRefineStone->GetCount() - 1); + + char buf[128]; + sprintf(buf, "STRENGTH : %d -> %d", bStrength, bStrength + 1); + LogManager::instance().ItemLog(ch, pDragonSoul, "DS_STRENGTH_REFINE_SUCCESS", buf); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ߽ϴ.")); + ch->AutoGiveItem(pResult, true); + bSubHeader = DS_SUB_HEADER_REFINE_SUCCEED; + } + else + { + if (bStrength != 0) + { + pResult = ITEM_MANAGER::instance().CreateItem(MakeDragonSoulVnum(bType, bGrade, bStep, bStrength - 1)); + if (NULL == pResult) + { + sys_err ("INVALID DRAGON SOUL(%d)", MakeDragonSoulVnum(bType, bGrade, bStep, bStrength - 1)); + return false; + } + pDragonSoul->CopyAttributeTo(pResult); + RefreshItemAttributes(pResult); + } + bSubHeader = DS_SUB_HEADER_REFINE_FAIL; + + char buf[128]; + sprintf(buf, "STRENGTH : %d -> %d", bStrength, bStrength - 1); + // strengthȭ н ־, α׸ . + LogManager::instance().ItemLog(ch, pDragonSoul, "DS_STRENGTH_REFINE_FAIL", buf); + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ ߽ϴ.")); + pDragonSoul->SetCount(pDragonSoul->GetCount() - 1); + pRefineStone->SetCount(pRefineStone->GetCount() - 1); + if (NULL != pResult) + ch->AutoGiveItem(pResult, true); + + } + + SendRefineResultPacket(ch, bSubHeader, NULL == pResult? NPOS : TItemPos (pResult->GetWindow(), pResult->GetCell())); + + return true; +} + +void DSManager::SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos) +{ + TPacketGCDragonSoulRefine pack; + pack.bSubType = bSubHeader; + + if (pos.IsValidItemPosition()) + { + pack.Pos = pos; + } + LPDESC d = ch->GetDesc(); + if (NULL == d) + { + return ; + } + else + { + d->Packet(&pack, sizeof(pack)); + } +} + +int DSManager::LeftTime(LPITEM pItem) const +{ + if (pItem == NULL) + return false; + + // ϴ timer based on wear ȥ ð Ǿ . + if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0) + { + return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC); + } + // ٸ limit type ȥ ð Ǹ ϴ ð Ҵٰ Ǵ. + else + { + return INT_MAX; + } +} + +bool DSManager::IsTimeLeftDragonSoul(LPITEM pItem) const +{ + if (pItem == NULL) + return false; + + // ϴ timer based on wear ȥ ð Ǿ . + if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0) + { + return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC) > 0; + } + // ٸ limit type ȥ ð Ǹ ϴ ð Ҵٰ Ǵ. + else + { + return true; + } +} + +bool DSManager::IsActiveDragonSoul(LPITEM pItem) const +{ + return pItem->GetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX); +} + +bool DSManager::ActivateDragonSoul(LPITEM pItem) +{ + if (NULL == pItem) + return false; + LPCHARACTER pOwner = pItem->GetOwner(); + if (NULL == pOwner) + return false; + + int deck_idx = pOwner->DragonSoul_GetActiveDeck(); + + if (deck_idx < 0) + return false; + + if (INVENTORY_MAX_NUM + WEAR_MAX_NUM + DS_SLOT_MAX * deck_idx <= pItem->GetCell() && + pItem->GetCell() < INVENTORY_MAX_NUM + WEAR_MAX_NUM + DS_SLOT_MAX * (deck_idx + 1)) + { + if (IsTimeLeftDragonSoul(pItem) && !IsActiveDragonSoul(pItem)) + { + char buf[128]; + sprintf (buf, "LEFT TIME(%d)", LeftTime(pItem)); + LogManager::instance().ItemLog(pOwner, pItem, "DS_ACTIVATE", buf); + pItem->ModifyPoints(true); + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 1); + + pItem->StartTimerBasedOnWearExpireEvent(); + } + return true; + } + else + return false; +} + +bool DSManager::DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState) +{ + if (NULL == pItem) + return false; + + LPCHARACTER pOwner = pItem->GetOwner(); + if (NULL == pOwner) + return false; + + if (!IsActiveDragonSoul(pItem)) + return false; + + char buf[128]; + pItem->StopTimerBasedOnWearExpireEvent(); + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 0); + pItem->ModifyPoints(false); + + sprintf (buf, "LEFT TIME(%d)", LeftTime(pItem)); + LogManager::instance().ItemLog(pOwner, pItem, "DS_DEACTIVATE", buf); + + if (false == bSkipRefreshOwnerActiveState) + RefreshDragonSoulState(pOwner); + + return true; +} + +void DSManager::RefreshDragonSoulState(LPCHARACTER ch) +{ + if (NULL == ch) + return ; + for (int i = WEAR_MAX_NUM; i < WEAR_MAX_NUM + DS_SLOT_MAX * DRAGON_SOUL_DECK_MAX_NUM; i++) + { + LPITEM pItem = ch->GetWear(i); + if (pItem != NULL) + { + if(IsActiveDragonSoul(pItem)) + { + return; + } + } + } + ch->DragonSoul_DeactivateAll(); +} + +DSManager::DSManager() +{ + m_pTable = NULL; +} + +DSManager::~DSManager() +{ + if (m_pTable) + delete m_pTable; +} diff --git a/game/src/DragonSoul.h b/game/src/DragonSoul.h new file mode 100644 index 0000000..4d8b9d9 --- /dev/null +++ b/game/src/DragonSoul.h @@ -0,0 +1,60 @@ +#ifndef __INC_METIN_II_GAME_DRAGON_SOUL_H__ +#define __INC_METIN_II_GAME_DRAGON_SOUL_H__ + +#include "../../common/length.h" + +class CHARACTER; +class CItem; + +class DragonSoulTable; + +class DSManager : public singleton +{ +public: + DSManager(); + ~DSManager(); + bool ReadDragonSoulTableFile(const char * c_pszFileName); + + void GetDragonSoulInfo(DWORD dwVnum, OUT BYTE& bType, OUT BYTE& bGrade, OUT BYTE& bStep, OUT BYTE& bRefine) const; + // fixme : titempos + WORD GetBasePosition(const LPITEM pItem) const; + bool IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const; + int GetDuration(const LPITEM pItem) const; + + // ȥ ޾Ƽ Ư ϴ Լ + bool ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor = NULL); + + // Ư ȥ(pItem) â θ ϰ, + // н λ깰 ִ Լ.(λ깰 dragon_soul_table.txt ) + // DestCell invalid , ȥ ڵ ߰. + // , ȥ(pItem) delete. + // ִٸ Ȯ pExtractor->GetValue(0)%ŭ . + // λ깰 ڵ ߰. + bool PullOut(LPCHARACTER ch, TItemPos DestCell, IN OUT LPITEM& pItem, LPITEM pExtractor = NULL); + + // ȥ ׷̵ Լ + bool DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + bool DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + bool DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]); + + bool DragonSoulItemInitialize(LPITEM pItem); + + bool IsTimeLeftDragonSoul(LPITEM pItem) const; + int LeftTime(LPITEM pItem) const; + bool ActivateDragonSoul(LPITEM pItem); + bool DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState = false); + bool IsActiveDragonSoul(LPITEM pItem) const; +private: + void SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos); + + // ij ȥ 캸, Ȱȭ ȥ ٸ, ij ȥ Ȱ ¸ off Ű Լ. + void RefreshDragonSoulState(LPCHARACTER ch); + + DWORD MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine); + bool PutAttributes(LPITEM pDS); + bool RefreshItemAttributes(LPITEM pItem); + + DragonSoulTable* m_pTable; +}; + +#endif diff --git a/game/src/FSM.cpp b/game/src/FSM.cpp new file mode 100644 index 0000000..d2b0c09 --- /dev/null +++ b/game/src/FSM.cpp @@ -0,0 +1,64 @@ +// Local Includes + +#include +#include + +#include "FSM.h" + +// Constructor +CFSM::CFSM() +{ + // Initialize States + m_stateInitial.Set(this, &CFSM::BeginStateInitial, &CFSM::StateInitial, &CFSM::EndStateInitial); + + // Initialize State Machine + m_pCurrentState = static_cast(&m_stateInitial); + m_pNewState = NULL; +} + +//====================================================================================================== +// Global Functions + +// Update +void CFSM::Update() +{ + // Check New State + if (m_pNewState) + { + if (NULL != m_pCurrentState) + { + m_pCurrentState->ExecuteEndState(); + } + + // Set New State + m_pCurrentState = m_pNewState; + m_pNewState = 0; + + // Execute Begin State + m_pCurrentState->ExecuteBeginState(); + } + + // Execute State + m_pCurrentState->ExecuteState(); +} + +//====================================================================================================== +// State Functions + +// Is State +bool CFSM::IsState(CState & State) const +{ + return (m_pCurrentState == &State); +} + +// Goto State +bool CFSM::GotoState(CState & NewState) +{ + if (IsState(NewState) && m_pNewState == &NewState) + return true; + + // Set New State + m_pNewState = &NewState; + return true; +} + diff --git a/game/src/FSM.h b/game/src/FSM.h new file mode 100644 index 0000000..fae91f9 --- /dev/null +++ b/game/src/FSM.h @@ -0,0 +1,34 @@ +#ifndef _fsm_fsm_h +#define _fsm_fsm_h + +// Local Includes +#include "state.h" + +// FSM Class +class CFSM +{ + protected: + CState * m_pCurrentState; // Current State + CState * m_pNewState; // New State + CStateTemplate m_stateInitial; // Initial State + + public: + // Constructor + CFSM(); + + // Destructor + virtual ~CFSM() {} + + // Global Functions + virtual void Update(); + + // State Functions + bool IsState(CState &State) const; + bool GotoState(CState &NewState); + + virtual void BeginStateInitial() {} + virtual void StateInitial() {} + virtual void EndStateInitial() {} +}; + +#endif diff --git a/game/src/FileMonitor_FreeBSD.cpp b/game/src/FileMonitor_FreeBSD.cpp new file mode 100644 index 0000000..e44a65e --- /dev/null +++ b/game/src/FileMonitor_FreeBSD.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "FileMonitor_FreeBSD.h" +#include "../../libthecore/include/log.h" + + +#define INVALID_KERNEL_EVENT -1 + +FileMonitorFreeBSD::FileMonitorFreeBSD() +{ + m_KernelEventQueue = INVALID_KERNEL_EVENT; +} + +FileMonitorFreeBSD::~FileMonitorFreeBSD() +{ + if( m_KernelEventQueue != INVALID_KERNEL_EVENT ) + { + close ( m_KernelEventQueue ); + m_KernelEventQueue = INVALID_KERNEL_EVENT; + } + + TMonitorFileHashMap::iterator it; + for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it ) + { + close(it->second.fhMonitor); + } + + m_FileLists.clear(); + + m_MonitoredEventLists.clear(); + m_TriggeredEventLists.clear(); +} + + +void FileMonitorFreeBSD::Update(DWORD dwPulses) +{ + if( m_KernelEventQueue == INVALID_KERNEL_EVENT || m_FileLists.size() == 0 ) + return; + + int nEvent = kevent(m_KernelEventQueue, &m_TriggeredEventLists[0], (int)m_TriggeredEventLists.size(), &m_MonitoredEventLists[0], (int)m_MonitoredEventLists.size(), NULL ); + if( nEvent == INVALID_KERNEL_EVENT ) + { + return; + } + else if( nEvent > 0 ) + { + for( int i = 0; i < nEvent; ++i ) + { + int nEventFlags = m_MonitoredEventLists[i].flags; + eFileUpdatedOptions eUpdateOption = e_FileUpdate_None; + + if (nEventFlags & EV_ERROR) + eUpdateOption = e_FileUpdate_Error; + + else if (nEventFlags & NOTE_DELETE) + eUpdateOption = e_FileUpdate_Deleted; + + else if (nEventFlags & NOTE_EXTEND || nEventFlags & NOTE_WRITE) + eUpdateOption = e_FileUpdate_Modified; + + else if (nEventFlags & NOTE_ATTRIB) + eUpdateOption = e_FileUpdate_AttrModified; + + else if (nEventFlags & NOTE_LINK) + eUpdateOption = e_FileUpdate_Linked; + + else if (nEventFlags & NOTE_RENAME) + eUpdateOption = e_FileUpdate_Renamed; + + else if (nEventFlags & NOTE_REVOKE) + eUpdateOption = e_FileUpdate_Revoked; + + if( eUpdateOption != e_FileUpdate_None ) + { + TMonitorFileHashMap::iterator it; + for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it ) + { + FileIOContext_FreeBSD& context = it->second; + if( context.idxToEventList == i ) + { + std::string strModifedFileName = it->first; + context.pListenFunc( strModifedFileName, eUpdateOption ); + break; + } + } + } + } + } +} + +void FileMonitorFreeBSD::AddWatch(const std::string& strFileName, PFN_FileChangeListener pListenerFunc) +{ + int iFileHandle = -1; + if( (iFileHandle = open(strFileName.c_str(), O_RDONLY)) == -1) + { + sys_err("FileMonitorFreeBSD:AddWatch : can`t open file(%s).\n", strFileName.c_str()); + return; + } + + //create kqueue if not exists + if( m_KernelEventQueue == INVALID_KERNEL_EVENT ) + m_KernelEventQueue = kqueue(); + + if( m_KernelEventQueue == INVALID_KERNEL_EVENT ) + { + sys_err("FileMonitorFreeBSD:AddWatch : failed to create kqueue.\n"); + return; + } + + TMonitorFileHashMap::iterator it = m_FileLists.find( strFileName ); + if( it != m_FileLists.end() ) + { + sys_log(0, "FileMonitorFreeBSD:AddWatch : trying to add duplicated watch on file(%s).\n", strFileName.c_str() ); + return; + } + + //set file context + FileIOContext_FreeBSD context; + { + context.fhMonitor = iFileHandle; + context.idxToEventList = (int)m_MonitoredEventLists.size(); + context.pListenFunc = pListenerFunc; + } + + m_FileLists[strFileName] = context; + + //set events + struct kevent kTriggerEvent, kMonitorEvent; + + EV_SET(&kTriggerEvent, iFileHandle, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, + 0, 0); + + m_TriggeredEventLists.push_back( kTriggerEvent ); + m_MonitoredEventLists.push_back( kMonitorEvent ); +} \ No newline at end of file diff --git a/game/src/FileMonitor_FreeBSD.h b/game/src/FileMonitor_FreeBSD.h new file mode 100644 index 0000000..23e2170 --- /dev/null +++ b/game/src/FileMonitor_FreeBSD.h @@ -0,0 +1,47 @@ +#ifndef FILEMONITOR_FREEBSD_INCLUDED +#define FILEMONITOR_FREEBSD_INCLUDED + +#include "IFileMonitor.h" +#include +#include +#include +#include +#include + +struct FileIOContext_FreeBSD +{ + int fhMonitor; + int idxToEventList; // evtTrigger & evtMonitor index should be same + PFN_FileChangeListener pListenFunc; +}; + +class FileMonitorFreeBSD : public IFileMonitor +{ +private: + FileMonitorFreeBSD(); //hidden + +public: + virtual ~FileMonitorFreeBSD(); + + void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc); + void Update (DWORD dwPulses); + + static FileMonitorFreeBSD& Instance() + { + static FileMonitorFreeBSD theMonitor; + return theMonitor; + } + +private: + typedef boost::unordered_map TMonitorFileHashMap; + typedef std::vector TEventList; + + TMonitorFileHashMap m_FileLists; + TEventList m_MonitoredEventLists; + TEventList m_TriggeredEventLists; + + int m_KernelEventQueue; +}; + + +#endif //FILEMONITOR_FREEBSD_INCLUDED diff --git a/game/src/HackShield.cpp b/game/src/HackShield.cpp new file mode 100644 index 0000000..9b7ddfa --- /dev/null +++ b/game/src/HackShield.cpp @@ -0,0 +1,54 @@ + +#include "stdafx.h" + +#include "HackShield.h" + +#include "HackShield_Impl.h" +#include "config.h" + +bool CHackShieldManager::Initialize() +{ + impl_ = M2_NEW CHackShieldImpl; + + if (NULL == impl_) + { + return false; + } + + return impl_->Initialize(); +} + +void CHackShieldManager::Release() +{ + if (NULL != impl_) + { + impl_->Release(); + + M2_DELETE(impl_); + + impl_ = NULL; + } +} + +bool CHackShieldManager::CreateClientHandle(DWORD dwPlayerID) +{ + return impl_->CreateClientHandle(dwPlayerID); +} + +void CHackShieldManager::DeleteClientHandle(DWORD dwPlayerID) +{ + impl_->DeleteClientHandle(dwPlayerID); +} + +bool CHackShieldManager::SendCheckPacket(LPCHARACTER ch) +{ + return impl_->SendCheckPacket(ch); +} + +bool CHackShieldManager::VerifyAck(LPCHARACTER ch, const void* buf) +{ + TPacketGCHSCheck* p = reinterpret_cast(const_cast(buf)); + + return impl_->VerifyAck(ch, p); +} + diff --git a/game/src/HackShield.h b/game/src/HackShield.h new file mode 100644 index 0000000..f9954cb --- /dev/null +++ b/game/src/HackShield.h @@ -0,0 +1,24 @@ + +#ifndef HACK_SHIELD_MANAGER_H_ +#define HACK_SHIELD_MANAGER_H_ + +class CHackShieldImpl; + +class CHackShieldManager : public singleton +{ + public: + bool Initialize (); + void Release (); + + bool CreateClientHandle (DWORD dwPlayerID); + void DeleteClientHandle (DWORD dwPlayerID); + + bool SendCheckPacket (LPCHARACTER ch); + bool VerifyAck (LPCHARACTER ch, const void* buf); + + private: + CHackShieldImpl* impl_; +}; + +#endif /* HACK_SHIELD_MANAGER_H_ */ + diff --git a/game/src/HackShield_Impl.cpp b/game/src/HackShield_Impl.cpp new file mode 100644 index 0000000..1cdd0f1 --- /dev/null +++ b/game/src/HackShield_Impl.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" + +#include "HackShield_Impl.h" + +#ifdef __FreeBSD__ + +#include "char.h" +#include "packet.h" +#include "desc.h" +#include "log.h" + +bool CHackShieldImpl::Initialize() +{ + handle_ = _AhnHS_CreateServerObject("metin2client.bin.hsb"); + + if (ANTICPX_INVALID_HANDLE_VALUE == handle_) + { + return false; + } + + sys_log(0, "HShield: Success to CreateServerObject"); + + return true; +} + +void CHackShieldImpl::Release() +{ + _AhnHS_CloseServerHandle(handle_); + + sys_log(0, "HShield: Server Handle Closed"); +} + +bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID) +{ + ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( dwPlayerID ); + + if (iter != CliehtHandleMap_.end()) + { + sys_log(0, "HShield: Client Handle is already created for Player(%u)", dwPlayerID); + + return false; + } + + AHNHS_CLIENT_HANDLE handle = _AhnHS_CreateClientObject(handle_); + + if (ANTICPX_INVALID_HANDLE_VALUE == handle) + { + sys_log(0, "HShield: Failed to create client handle for Player(%u)", dwPlayerID); + + return false; + } + + CliehtHandleMap_.insert( std::make_pair(dwPlayerID, handle) ); + + sys_log(0, "HShield: Success to create client handle for Player(%u)", dwPlayerID); + + return true; +} + +void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID) +{ + ClientHandleContainer::iterator iter = CliehtHandleMap_.find( dwPlayerID ); + + if (iter == CliehtHandleMap_.end()) + { + sys_log(0, "HShield: there is no client handle for Player(%u)", dwPlayerID); + + return; + } + + _AhnHS_CloseClientHandle(iter->second); + + CliehtHandleMap_.erase(iter); + + sys_log(0, "HShield: client handle deleted for Player(%u)", dwPlayerID); +} + +bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch) +{ + if (NULL == ch) + { + return false; + } + + ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() ); + + if (iter == CliehtHandleMap_.end()) + { + sys_log(0, "HShield: Client Handle not create for Player(%u)", ch->GetPlayerID()); + return false; + } + + TPacketGCHSCheck pack; + pack.bHeader = HEADER_GC_HS_REQUEST; + + memset( &pack.Req, 0, sizeof(pack.Req)); + unsigned long ret = _AhnHS_MakeRequest( iter->second, &(pack.Req) ); + + if (0 != ret) + { + sys_log(0, "HShield: _AhnHS_MakeRequest return error(%u) for Player(%u)", ret, ch->GetPlayerID()); + return false; + } + else + { + sys_log(0, "HShield: _AhnHS_MakeRequest success ret(%d)", ret); + } + + if (NULL != ch->GetDesc()) + { + ch->GetDesc()->Packet( &pack, sizeof(pack) ); + sys_log(0, "HShield: Send Check Request for Player(%u)", ch->GetPlayerID()); + + return true; + } + + sys_log(0, "HShield: Failed to get DESC for Player(%u)", ch->GetPlayerID()); + + return false; +} + +bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf) +{ + if (NULL == ch) + { + return false; + } + + bool NeedDisconnect = false; + + ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() ); + + if (iter == CliehtHandleMap_.end()) + { + sys_log(0, "HShield: Cannot Find ClientHandle For Verify"); + + NeedDisconnect = true; + } + + unsigned long dwError = 0; + + unsigned long ret = _AhnHS_VerifyResponseEx( iter->second, buf->Req.byBuffer, buf->Req.nLength, &dwError ); + + if (ANTICPX_RECOMMAND_CLOSE_SESSION == ret) + { + sys_log(0, "HShield: not a valid ack ret(%u) error(%u) from Player(%u)", ret, dwError, ch->GetPlayerID()); + NeedDisconnect = true; + + ch->StopHackShieldCheckCycle(); + } + + if (NULL != ch->GetDesc()) + { + if (true == NeedDisconnect) + { + ch->GetDesc()->SetPhase(PHASE_CLOSE); + LogManager::instance().HackShieldLog(dwError, ch); + return false; + } + else + { + ch->SetHackShieldCheckMode(false); + } + } + + sys_log(0, "HShield: Valid Ack from Player(%u)", ch->GetPlayerID()); + + return true; +} + +#else + +bool CHackShieldImpl::Initialize() +{ + return true; +} + +void CHackShieldImpl::Release() +{ +} + +bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID) +{ + return true; +} + +void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID) +{ +} + +bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch) +{ + return true; +} + +bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf) +{ + return true; +} + +#endif + diff --git a/game/src/HackShield_Impl.h b/game/src/HackShield_Impl.h new file mode 100644 index 0000000..af32445 --- /dev/null +++ b/game/src/HackShield_Impl.h @@ -0,0 +1,51 @@ + +#ifndef HACK_SHIELD_IMPL_H_ +#define HACK_SHIELD_IMPL_H_ + +#include + +#ifdef __FreeBSD__ +// Live build only +#define UNIX +#include +#undef UNIX +#endif + +#pragma pack(1) + +typedef struct SPacketGCHSCheck +{ + BYTE bHeader; +#ifdef __FreeBSD__ + AHNHS_TRANS_BUFFER Req; +#endif +} TPacketGCHSCheck; + +#pragma pack() + +class CHackShieldImpl +{ + public: + bool Initialize (); + void Release (); + + bool CreateClientHandle (DWORD dwPlayerID); + void DeleteClientHandle (DWORD dwPlayerID); + + bool SendCheckPacket (LPCHARACTER ch); + bool VerifyAck (LPCHARACTER ch, TPacketGCHSCheck* buf); + + private: +#ifdef __FreeBSD__ + AHNHS_SERVER_HANDLE handle_; + + typedef boost::unordered_map ClientHandleContainer; + ClientHandleContainer CliehtHandleMap_; + + typedef boost::unordered_map ClientCheckContainer; + ClientCheckContainer ClientCheckMap_; +#endif +}; + +#endif /* HACK_SHIELD_IMPL_H_ */ + diff --git a/game/src/IFileMonitor.h b/game/src/IFileMonitor.h new file mode 100644 index 0000000..8ed621a --- /dev/null +++ b/game/src/IFileMonitor.h @@ -0,0 +1,30 @@ +#ifndef IFILEMONITOR_INCLUDED +#define IFILEMONITOR_INCLUDED + +//#include +#include + +enum eFileUpdatedOptions +{ + e_FileUpdate_None = -1, + e_FileUpdate_Error, + e_FileUpdate_Deleted, + e_FileUpdate_Modified, + e_FileUpdate_AttrModified, + e_FileUpdate_Linked, + e_FileUpdate_Renamed, + e_FileUpdate_Revoked, +}; + +// TODO : in FreeBSD boost function doesn`t work with boost bind +// so currently we only support for static function ptr only +//typedef boost::function< void ( const std::string&, eFileUpdatedOptions ) > PFN_FileChangeListener; +typedef void (* PFN_FileChangeListener )(const std::string&, eFileUpdatedOptions); + +struct IFileMonitor +{ + virtual void Update (DWORD dwPulses) = 0; + virtual void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc) = 0; +}; + +#endif // IFILEMONITOR_INCLUDED diff --git a/game/src/MarkConvert.cpp b/game/src/MarkConvert.cpp new file mode 100644 index 0000000..01eff54 --- /dev/null +++ b/game/src/MarkConvert.cpp @@ -0,0 +1,135 @@ +#include "stdafx.h" +#include "MarkManager.h" + +#ifdef __WIN32__ +#include +#endif + +#define OLD_MARK_INDEX_FILENAME "guild_mark.idx" +#define OLD_MARK_DATA_FILENAME "guild_mark.tga" + +static Pixel * LoadOldGuildMarkImageFile() +{ + FILE * fp = fopen(OLD_MARK_DATA_FILENAME, "rb"); + + if (!fp) + { + sys_err("cannot open %s", OLD_MARK_INDEX_FILENAME); + return NULL; + } + + int dataSize = 512 * 512 * sizeof(Pixel); + Pixel * dataPtr = (Pixel *) malloc(dataSize); + + fread(dataPtr, dataSize, 1, fp); + + fclose(fp); + + return dataPtr; +} + +bool GuildMarkConvert(const std::vector & vecGuildID) +{ + // +#ifndef __WIN32__ + mkdir("mark", S_IRWXU); +#else + _mkdir("mark"); +#endif + + // ε ֳ? +#ifndef __WIN32__ + if (0 != access(OLD_MARK_INDEX_FILENAME, F_OK)) +#else + if (0 != _access(OLD_MARK_INDEX_FILENAME, 0)) +#endif + return true; + + // ε + FILE* fp = fopen(OLD_MARK_INDEX_FILENAME, "r"); + + if (NULL == fp) + return false; + + // ̹ + Pixel * oldImagePtr = LoadOldGuildMarkImageFile(); + + if (NULL == oldImagePtr) + { + fclose(fp); + return false; + } + + /* + // guild_mark.tga targa ƴϰ, 512 * 512 * 4 ũ raw ̴. + // Ȯϱ targa Ϸ . + CGuildMarkImage * pkImage = new CGuildMarkImage; + pkImage->Build("guild_mark_real.tga"); + pkImage->Load("guild_mark_real.tga"); + pkImage->PutData(0, 0, 512, 512, oldImagePtr); + pkImage->Save("guild_mark_real.tga"); + */ + sys_log(0, "Guild Mark Converting Start."); + + char line[256]; + DWORD guild_id; + DWORD mark_id; + Pixel mark[SGuildMark::SIZE]; + + while (fgets(line, sizeof(line)-1, fp)) + { + sscanf(line, "%u %u", &guild_id, &mark_id); + + if (find(vecGuildID.begin(), vecGuildID.end(), guild_id) == vecGuildID.end()) + { + sys_log(0, " skipping guild ID %u", guild_id); + continue; + } + + // mark id -> ̹ ġ ã + uint row = mark_id / 32; + uint col = mark_id % 32; + + if (row >= 42) + { + sys_err("invalid mark_id %u", mark_id); + continue; + } + + uint sx = col * 16; + uint sy = row * 12; + + Pixel * src = oldImagePtr + sy * 512 + sx; + Pixel * dst = mark; + + // ̹ ũ Ѱ + for (int y = 0; y != SGuildMark::HEIGHT; ++y) + { + for (int x = 0; x != SGuildMark::WIDTH; ++x) + *(dst++) = *(src+x); + + src += 512; + } + + // ũ ýۿ ִ´. + CGuildMarkManager::instance().SaveMark(guild_id, (BYTE *) mark); + line[0] = '\0'; + } + + free(oldImagePtr); + fclose(fp); + + // Ʈ ѹ ϸǹǷ Űش. +#ifndef __WIN32__ + system("mv -f guild_mark.idx guild_mark.idx.removable"); + system("mv -f guild_mark.tga guild_mark.tga.removable"); +#else + system("move /Y guild_mark.idx guild_mark.idx.removable"); + system("move /Y guild_mark.tga guild_mark.tga.removable"); +#endif + + sys_log(0, "Guild Mark Converting Complete."); + + return true; +} + diff --git a/game/src/MarkImage.cpp b/game/src/MarkImage.cpp new file mode 100644 index 0000000..9f716fc --- /dev/null +++ b/game/src/MarkImage.cpp @@ -0,0 +1,299 @@ +#include "stdafx.h" +#include "MarkImage.h" + +#include "crc32.h" +#include "lzo_manager.h" +#define CLZO LZOManager + +CGuildMarkImage * NewMarkImage() +{ + return M2_NEW CGuildMarkImage; +} + +void DeleteMarkImage(CGuildMarkImage * pkImage) +{ + M2_DELETE(pkImage); +} + +CGuildMarkImage::CGuildMarkImage() + : m_uImg(INVALID_HANDLE) +{ + memset( &m_apxImage, 0, sizeof(m_apxImage) ); +} + +CGuildMarkImage::~CGuildMarkImage() +{ + Destroy(); +} + +void CGuildMarkImage::Destroy() +{ + if (INVALID_HANDLE == m_uImg) + return; + + ilDeleteImages(1, &m_uImg); + m_uImg = INVALID_HANDLE; +} + +void CGuildMarkImage::Create() +{ + if (INVALID_HANDLE != m_uImg) + return; + + ilGenImages(1, &m_uImg); +} + +bool CGuildMarkImage::Save(const char* c_szFileName) +{ + ilEnable(IL_FILE_OVERWRITE); + ilBindImage(m_uImg); + + if (!ilSave(IL_TGA, (const ILstring)c_szFileName)) + return false; + + return true; +} + +bool CGuildMarkImage::Build(const char * c_szFileName) +{ + sys_log(0, "GuildMarkImage: creating new file %s", c_szFileName); + + Destroy(); + Create(); + + ilBindImage(m_uImg); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + + BYTE * data = (BYTE *) malloc(sizeof(Pixel) * WIDTH * HEIGHT); + memset(data, 0, sizeof(Pixel) * WIDTH * HEIGHT); + + if (!ilTexImage(WIDTH, HEIGHT, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data)) + { + sys_err("GuildMarkImage: cannot initialize image"); + return false; + } + + free(data); + + ilEnable(IL_FILE_OVERWRITE); + + if (!ilSave(IL_TGA, (const ILstring)c_szFileName)) + return false; + + return true; +} + +bool CGuildMarkImage::Load(const char * c_szFileName) +{ + Destroy(); + Create(); + + ilBindImage(m_uImg); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + + if (!ilLoad(IL_TYPE_UNKNOWN, (const ILstring) c_szFileName)) + { + sys_err("GuildMarkImage: %s cannot open file.", c_szFileName); + return false; + } + + if (ilGetInteger(IL_IMAGE_WIDTH) != WIDTH) + { + sys_err("GuildMarkImage: %s width must be %u", c_szFileName, WIDTH); + return false; + } + + if (ilGetInteger(IL_IMAGE_HEIGHT) != HEIGHT) + { + sys_err("GuildMarkImage: %s height must be %u", c_szFileName, HEIGHT); + return false; + } + + ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE); + + BuildAllBlocks(); + return true; +} + +void CGuildMarkImage::PutData(UINT x, UINT y, UINT width, UINT height, void * data) +{ + ilBindImage(m_uImg); + ilSetPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data); +} + +void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * data) +{ + ilBindImage(m_uImg); + ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data); +} + +// ̹ = 512x512 +// = ũ 4 x 4 +// ũ = 16 x 12 +// ̹ = 8 x 10 + +// SERVER +bool CGuildMarkImage::SaveMark(DWORD posMark, BYTE * pbImage) +{ + if (posMark >= MARK_TOTAL_COUNT) + { + sys_err("GuildMarkImage::CopyMarkFromData: Invalid mark position %u", posMark); + return false; + } + + // ũ ü ̹ ׸. + DWORD colMark = posMark % MARK_COL_COUNT; + DWORD rowMark = posMark / MARK_COL_COUNT; + + printf("PutMark pos %u %ux%u\n", posMark, colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT); + PutData(colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, pbImage); + + // ׷ Ʈ + DWORD rowBlock = rowMark / SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT; + DWORD colBlock = colMark / SGuildMarkBlock::MARK_PER_BLOCK_WIDTH; + + Pixel apxBuf[SGuildMarkBlock::SIZE]; + GetData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + m_aakBlock[rowBlock][colBlock].Compress(apxBuf); + return true; +} + +bool CGuildMarkImage::DeleteMark(DWORD posMark) +{ + Pixel image[SGuildMark::SIZE]; + memset(&image, 0, sizeof(image)); + return SaveMark(posMark, (BYTE *) &image); +} + +// CLIENT +bool CGuildMarkImage::SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize) +{ + if (posBlock >= BLOCK_TOTAL_COUNT) + return false; + + Pixel apxBuf[SGuildMarkBlock::SIZE]; + lzo_uint sizeBuf = sizeof(apxBuf); + + if (LZO_E_OK != lzo1x_decompress_safe(pbComp, dwCompSize, (BYTE *) apxBuf, &sizeBuf, CLZO::Instance().GetWorkMemory())) + { + sys_err("GuildMarkImage::CopyBlockFromCompressedData: cannot decompress, compressed size = %u", dwCompSize); + return false; + } + + if (sizeBuf != sizeof(apxBuf)) + { + sys_err("GuildMarkImage::CopyBlockFromCompressedData: image corrupted, decompressed size = %u", sizeBuf); + return false; + } + + DWORD rowBlock = posBlock / BLOCK_COL_COUNT; + DWORD colBlock = posBlock % BLOCK_COL_COUNT; + + PutData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + + m_aakBlock[rowBlock][colBlock].CopyFrom(pbComp, dwCompSize, GetCRC32((const char *) apxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE)); + return true; +} + +void CGuildMarkImage::BuildAllBlocks() // ̹ ü ȭ +{ + Pixel apxBuf[SGuildMarkBlock::SIZE]; + sys_log(0, "GuildMarkImage::BuildAllBlocks"); + + for (UINT row = 0; row < BLOCK_ROW_COUNT; ++row) + for (UINT col = 0; col < BLOCK_COL_COUNT; ++col) + { + GetData(col * SGuildMarkBlock::WIDTH, row * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf); + m_aakBlock[row][col].Compress(apxBuf); + } +} + +DWORD CGuildMarkImage::GetEmptyPosition() +{ + SGuildMark kMark; + + for (DWORD row = 0; row < MARK_ROW_COUNT; ++row) + { + for (DWORD col = 0; col < MARK_COL_COUNT; ++col) + { + GetData(col * SGuildMark::WIDTH, row * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, kMark.m_apxBuf); + + if (kMark.IsEmpty()) + return (row * MARK_COL_COUNT + col); + } + } + + return INVALID_MARK_POSITION; +} + +void CGuildMarkImage::GetDiffBlocks(const DWORD * crcList, std::map & mapDiffBlocks) +{ + BYTE posBlock = 0; + + for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row) + for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col) + { + if (m_aakBlock[row][col].m_crc != *crcList) + { + mapDiffBlocks.insert(std::map::value_type(posBlock, &m_aakBlock[row][col])); + } + ++crcList; + ++posBlock; + } +} + +void CGuildMarkImage::GetBlockCRCList(DWORD * crcList) +{ + for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row) + for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col) + *(crcList++) = m_aakBlock[row][col].GetCRC(); +} + +//////////////////////////////////////////////////////////////////////////////// +void SGuildMark::Clear() +{ + for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel) + m_apxBuf[iPixel] = 0xff000000; +} + +bool SGuildMark::IsEmpty() +{ + for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel) + if (m_apxBuf[iPixel] != 0x00000000) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +DWORD SGuildMarkBlock::GetCRC() const +{ + return m_crc; +} + +void SGuildMarkBlock::CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc) +{ + if (dwCompSize > MAX_COMP_SIZE) + return; + + m_sizeCompBuf = dwCompSize; + thecore_memcpy(m_abCompBuf, pbCompBuf, dwCompSize); + m_crc = crc; + //printf("SGuildMarkBlock::CopyFrom: %u > %u crc %u\n", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf, m_crc); +} + +void SGuildMarkBlock::Compress(const Pixel * pxBuf) +{ + m_sizeCompBuf = MAX_COMP_SIZE; + + if (LZO_E_OK != lzo1x_1_compress((const BYTE *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE, m_abCompBuf, &m_sizeCompBuf, CLZO::Instance().GetWorkMemory())) + { + sys_err("SGuildMarkBlock::Compress: Error! %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf); + return; + } + + //sys_log(0, "SGuildMarkBlock::Compress %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf); + m_crc = GetCRC32((const char *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE); +} diff --git a/game/src/MarkImage.h b/game/src/MarkImage.h new file mode 100644 index 0000000..ef55c8b --- /dev/null +++ b/game/src/MarkImage.h @@ -0,0 +1,111 @@ +#ifndef __INC_METIN_II_MARKIMAGE_H__ +#define __INC_METIN_II_MARKIMAGE_H__ + +#include +#include "minilzo.h" + +typedef unsigned long Pixel; + +struct SGuildMark +{ + enum + { + WIDTH = 16, + HEIGHT = 12, + SIZE = WIDTH * HEIGHT, + }; + + /////////////////////////////////////////////////////////////////////////////// + Pixel m_apxBuf[SIZE]; // ̹ + + /////////////////////////////////////////////////////////////////////////////// + void Clear(); + bool IsEmpty(); +}; + +struct SGuildMarkBlock +{ + enum + { + MARK_PER_BLOCK_WIDTH = 4, + MARK_PER_BLOCK_HEIGHT = 4, + + WIDTH = SGuildMark::WIDTH * MARK_PER_BLOCK_WIDTH, + HEIGHT = SGuildMark::HEIGHT * MARK_PER_BLOCK_HEIGHT, + + SIZE = WIDTH * HEIGHT, + MAX_COMP_SIZE = (SIZE * sizeof(Pixel)) + ((SIZE * sizeof(Pixel)) >> 4) + 64 + 3 + }; + + /////////////////////////////////////////////////////////////////////////////// + Pixel m_apxBuf[SIZE]; // ̹ + + BYTE m_abCompBuf[MAX_COMP_SIZE]; // + lzo_uint m_sizeCompBuf; // ũ + DWORD m_crc; // CRC + + /////////////////////////////////////////////////////////////////////////////// + DWORD GetCRC() const; + + void CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc); + void Compress(const Pixel * pxBuf); +}; + +class CGuildMarkImage +{ + public: + enum + { + WIDTH = 512, + HEIGHT = 512, + + BLOCK_ROW_COUNT = HEIGHT / SGuildMarkBlock::HEIGHT, // 10 + BLOCK_COL_COUNT = WIDTH / SGuildMarkBlock::WIDTH, // 8 + + BLOCK_TOTAL_COUNT = BLOCK_ROW_COUNT * BLOCK_COL_COUNT, // 80 + + MARK_ROW_COUNT = BLOCK_ROW_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT, // 40 + MARK_COL_COUNT = BLOCK_COL_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_WIDTH, // 32 + + MARK_TOTAL_COUNT = MARK_ROW_COUNT * MARK_COL_COUNT, // 1280 + + INVALID_MARK_POSITION = 0xffffffff, + }; + + CGuildMarkImage(); + virtual ~CGuildMarkImage(); + + void Create(); + void Destroy(); + + bool Build(const char * c_szFileName); + bool Save(const char* c_szFileName); + bool Load(const char* c_szFileName); + + void PutData(UINT x, UINT y, UINT width, UINT height, void* data); + void GetData(UINT x, UINT y, UINT width, UINT height, void* data); + + bool SaveMark(DWORD posMark, BYTE * pbMarkImage); + bool DeleteMark(DWORD posMark); + bool SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize); // -> Ŭ̾Ʈ + + DWORD GetEmptyPosition(); // ũ ġ ´. + + void GetBlockCRCList(DWORD * crcList); + void GetDiffBlocks(const DWORD * crcList, std::map & mapDiffBlocks); + + private: + enum + { + INVALID_HANDLE = 0xffffffff, + }; + + void BuildAllBlocks(); + + SGuildMarkBlock m_aakBlock[BLOCK_ROW_COUNT][BLOCK_COL_COUNT]; + Pixel m_apxImage[WIDTH * HEIGHT * sizeof(Pixel)]; + + ILuint m_uImg; +}; + +#endif diff --git a/game/src/MarkManager.cpp b/game/src/MarkManager.cpp new file mode 100644 index 0000000..8979216 --- /dev/null +++ b/game/src/MarkManager.cpp @@ -0,0 +1,473 @@ +#include "stdafx.h" +#include "MarkManager.h" + +#include "crc32.h" + +CGuildMarkImage * CGuildMarkManager::__NewImage() +{ + return M2_NEW CGuildMarkImage; +} + +void CGuildMarkManager::__DeleteImage(CGuildMarkImage * pkImgDel) +{ + M2_DELETE(pkImgDel); +} + +CGuildMarkManager::CGuildMarkManager() +{ + // mark id . () + for (DWORD i = 0; i < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT; ++i) + m_setFreeMarkID.insert(i); +} + +CGuildMarkManager::~CGuildMarkManager() +{ + for (std::map::iterator it = m_mapIdx_Image.begin(); it != m_mapIdx_Image.end(); ++it) + __DeleteImage(it->second); + + m_mapIdx_Image.clear(); +} + +bool CGuildMarkManager::GetMarkImageFilename(DWORD imgIdx, std::string & path) const +{ + if (imgIdx >= MAX_IMAGE_COUNT) + return false; + + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_%u.tga", m_pathPrefix.c_str(), imgIdx); + path = buf; + return true; +} + +void CGuildMarkManager::SetMarkPathPrefix(const char * prefix) +{ + m_pathPrefix = prefix; +} + +// ũ ε ҷ ( ) +bool CGuildMarkManager::LoadMarkIndex() +{ + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str()); + FILE * fp = fopen(buf, "r"); + + if (!fp) + return false; + + DWORD guildID; + DWORD markID; + + char line[256]; + + while (fgets(line, sizeof(line)-1, fp)) + { + sscanf(line, "%u %u", &guildID, &markID); + line[0] = '\0'; + AddMarkIDByGuildID(guildID, markID); + } + + LoadMarkImages(); + + fclose(fp); + return true; +} + +bool CGuildMarkManager::SaveMarkIndex() +{ + char buf[64]; + snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str()); + FILE * fp = fopen(buf, "w"); + + if (!fp) + { + sys_err("MarkManager::SaveMarkIndex: cannot open index file."); + return false; + } + + for (std::map::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + fprintf(fp, "%u %u\n", it->first, it->second); + + fclose(fp); + sys_log(0, "MarkManager::SaveMarkIndex: index count %d", m_mapGID_MarkID.size()); + return true; +} + +void CGuildMarkManager::LoadMarkImages() +{ + bool isMarkExists[MAX_IMAGE_COUNT]; + memset(isMarkExists, 0, sizeof(isMarkExists)); + + for (std::map::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + { + DWORD markID = it->second; + + if (markID < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT) + isMarkExists[markID / CGuildMarkImage::MARK_TOTAL_COUNT] = true; + } + + for (DWORD i = 0; i < MAX_IMAGE_COUNT; ++i) + if (isMarkExists[i]) + __GetImage(i); +} + +void CGuildMarkManager::SaveMarkImage(DWORD imgIdx) +{ + std::string path; + + if (GetMarkImageFilename(imgIdx, path)) + if (!__GetImage(imgIdx)->Save(path.c_str())) + sys_err("%s Save failed\n", path.c_str()); +} + +CGuildMarkImage * CGuildMarkManager::__GetImage(DWORD imgIdx) +{ + std::map::iterator it = m_mapIdx_Image.find(imgIdx); + + if (it == m_mapIdx_Image.end()) + { + std::string imagePath; + + if (GetMarkImageFilename(imgIdx, imagePath)) + { + CGuildMarkImage * pkImage = __NewImage(); + m_mapIdx_Image.insert(std::map::value_type(imgIdx, pkImage)); + + if (!pkImage->Load(imagePath.c_str())) + { + pkImage->Build(imagePath.c_str()); + pkImage->Load(imagePath.c_str()); + } + + return pkImage; + } + else + return NULL; + } + else + return it->second; +} + +bool CGuildMarkManager::AddMarkIDByGuildID(DWORD guildID, DWORD markID) +{ + if (markID >= MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT) + return false; + + //sys_log(0, "MarkManager: guild_id=%d mark_id=%d", guildID, markID); + m_mapGID_MarkID.insert(std::map::value_type(guildID, markID)); + m_setFreeMarkID.erase(markID); + return true; +} + +DWORD CGuildMarkManager::GetMarkID(DWORD guildID) +{ + std::map::iterator it = m_mapGID_MarkID.find(guildID); + + if (it == m_mapGID_MarkID.end()) + return INVALID_MARK_ID; + + return it->second; +} + +DWORD CGuildMarkManager::__AllocMarkID(DWORD guildID) +{ + std::set::iterator it = m_setFreeMarkID.lower_bound(0); + + if (it == m_setFreeMarkID.end()) + return INVALID_MARK_ID; + + DWORD markID = *it; + + DWORD imgIdx = markID / CGuildMarkImage::MARK_TOTAL_COUNT; + CGuildMarkImage * pkImage = __GetImage(imgIdx); // ̹ ٸ + + if (pkImage && AddMarkIDByGuildID(guildID, markID)) + return markID; + + return INVALID_MARK_ID; +} + +DWORD CGuildMarkManager::GetMarkImageCount() const +{ + return m_mapIdx_Image.size(); +} + +DWORD CGuildMarkManager::GetMarkCount() const +{ + return m_mapGID_MarkID.size(); +} + +// SERVER +void CGuildMarkManager::CopyMarkIdx(char * pcBuf) const +{ + WORD * pwBuf = (WORD *) pcBuf; + + for (std::map::const_iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it) + { + *(pwBuf++) = it->first; // guild id + *(pwBuf++) = it->second; // mark id + } +} + +// SERVER +DWORD CGuildMarkManager::SaveMark(DWORD guildID, BYTE * pbMarkImage) +{ + DWORD idMark; + + if ((idMark = GetMarkID(guildID)) == INVALID_MARK_ID) + { + if ((idMark = __AllocMarkID(guildID)) == INVALID_MARK_ID) + { + sys_err("CGuildMarkManager: cannot alloc mark id %u", guildID); + return false; + } + else + sys_log(0, "SaveMark: mark id alloc %u", idMark); + } + else + sys_log(0, "SaveMark: mark id found %u", idMark); + + DWORD imgIdx = (idMark / CGuildMarkImage::MARK_TOTAL_COUNT); + CGuildMarkImage * pkImage = __GetImage(imgIdx); + + if (pkImage) + { + pkImage->SaveMark(idMark % CGuildMarkImage::MARK_TOTAL_COUNT, pbMarkImage); + + SaveMarkImage(imgIdx); + SaveMarkIndex(); + } + + return idMark; +} + +// SERVER +void CGuildMarkManager::DeleteMark(DWORD guildID) +{ + std::map::iterator it = m_mapGID_MarkID.find(guildID); + + if (it == m_mapGID_MarkID.end()) + return; + + CGuildMarkImage * pkImage; + + if ((pkImage = __GetImage(it->second / CGuildMarkImage::MARK_TOTAL_COUNT)) != NULL) + pkImage->DeleteMark(it->second % CGuildMarkImage::MARK_TOTAL_COUNT); + + m_setFreeMarkID.insert(it->second); + m_mapGID_MarkID.erase(it); + + SaveMarkIndex(); +} + +// SERVER +void CGuildMarkManager::GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map & mapDiffBlocks) +{ + mapDiffBlocks.clear(); + + // Ŭ̾Ʈ ̹ û . + if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx)) + { + sys_err("invalid idx %u", imgIdx); + return; + } + + CGuildMarkImage * p = __GetImage(imgIdx); + + if (p) + p->GetDiffBlocks(crcList, mapDiffBlocks); +} + +// CLIENT +bool CGuildMarkManager::SaveBlockFromCompressedData(DWORD imgIdx, DWORD posBlock, const BYTE * pbBlock, DWORD dwSize) +{ + CGuildMarkImage * pkImage = __GetImage(imgIdx); + + if (pkImage) + pkImage->SaveBlockFromCompressedData(posBlock, pbBlock, dwSize); + + return false; +} + +// CLIENT +bool CGuildMarkManager::GetBlockCRCList(DWORD imgIdx, DWORD * crcList) +{ + // Ŭ̾Ʈ ̹ û . + if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx)) + { + sys_err("invalid idx %u", imgIdx); + return false; + } + + CGuildMarkImage * p = __GetImage(imgIdx); + + if (p) + p->GetBlockCRCList(crcList); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// Symbol +/////////////////////////////////////////////////////////////////////////////////////// +const CGuildMarkManager::TGuildSymbol * CGuildMarkManager::GetGuildSymbol(DWORD guildID) +{ + std::map::iterator it = m_mapSymbol.find(guildID); + + if (it == m_mapSymbol.end()) + return NULL; + + return &it->second; +} + +bool CGuildMarkManager::LoadSymbol(const char* filename) +{ + FILE* fp = fopen(filename, "rb"); + + if (!fp) + return true; + else + { + DWORD symbolCount; + fread(&symbolCount, 4, 1, fp); + + for (DWORD i = 0; i < symbolCount; i++) + { + DWORD guildID; + DWORD dwSize; + fread(&guildID, 4, 1, fp); + fread(&dwSize, 4, 1, fp); + + TGuildSymbol gs; + gs.raw.resize(dwSize); + fread(&gs.raw[0], 1, dwSize, fp); + gs.crc = GetCRC32(reinterpret_cast(&gs.raw[0]), dwSize); + m_mapSymbol.insert(std::make_pair(guildID, gs)); + } + } + + fclose(fp); + return true; +} + +void CGuildMarkManager::SaveSymbol(const char* filename) +{ + FILE* fp = fopen(filename, "wb"); + if (!fp) + { + sys_err("Cannot open Symbol file (name: %s)", filename); + return; + } + + DWORD symbolCount = m_mapSymbol.size(); + fwrite(&symbolCount, 4, 1, fp); + + for (std::map::iterator it = m_mapSymbol.begin(); it != m_mapSymbol.end(); ++it) + { + DWORD guildID = it->first; + DWORD dwSize = it->second.raw.size(); + fwrite(&guildID, 4, 1, fp); + fwrite(&dwSize, 4, 1, fp); + fwrite(&it->second.raw[0], 1, dwSize, fp); + } + + fclose(fp); +} + +void CGuildMarkManager::UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData) +{ + sys_log(0, "GuildSymbolUpload guildID %u Size %d", guildID, iSize); + + if (m_mapSymbol.find(guildID) == m_mapSymbol.end()) + m_mapSymbol.insert(std::make_pair(guildID, TGuildSymbol())); + + TGuildSymbol& rSymbol = m_mapSymbol[guildID]; + rSymbol.raw.clear(); + + if (iSize > 0) + { + rSymbol.raw.reserve(iSize); + std::copy(pbyData, (pbyData + iSize), std::back_inserter(rSymbol.raw)); + rSymbol.crc = GetCRC32(reinterpret_cast(pbyData), iSize); + } +} + +#ifdef __UNITTEST__ +#include "lzo_manager.h" + +void heartbeat(LPHEART ht, int pulse) +{ + return; +} + +void SaveMark(DWORD guildID, const char * filename) +{ + ILuint m_uImg; + + ilGenImages(1, &m_uImg); + ilBindImage(m_uImg); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_UPPER_LEFT); + + if (ilLoad(IL_TYPE_UNKNOWN, (const ILstring) filename)) + { + ILuint width = ilGetInteger(IL_IMAGE_WIDTH); + ILuint height = ilGetInteger(IL_IMAGE_HEIGHT); + + ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE); + + BYTE * data = (BYTE *) malloc(sizeof(DWORD) * width * height); + ilCopyPixels(0, 0, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data); + ilDeleteImages(1, &m_uImg); + + printf("%s w%u h%u ", filename, width, height); + CGuildMarkManager::instance().SaveMark(guildID, data); + } + else + printf("%s cannot open file.\n", filename); +} + +int main(int argc, char **argv) +{ + LZOManager lzo; + CGuildMarkManager mgr; + char f[64]; + + srandomdev(); + + ilInit(); // DevIL Initialize + thecore_init(25, heartbeat); + + mgr.SetMarkPathPrefix("mark"); + mgr.LoadMarkIndex(); + for (int i = 0; i < 1279; ++i) + { + snprintf(f, sizeof(f), "%u.jpg", (random() % 5) + 1); + SaveMark(i, f); + //mgr.DeleteMark(i); + } + //snprintf(f, sizeof(f), "%u.jpg", (random() % 5) + 1); + //SaveMark(1, f); + + DWORD idx_client[CGuildMarkImage::BLOCK_TOTAL_COUNT]; + DWORD idx_server[CGuildMarkImage::BLOCK_TOTAL_COUNT]; + + mgr.GetBlockCRCList(0, idx_client); + mgr.GetBlockCRCList(1, idx_server); + + std::map mapDiff; + mgr.GetDiffBlocks(1, idx_client, mapDiff); + + printf("#1 Diff %u\n", mapDiff.size()); + + for (itertype(mapDiff) it = mapDiff.begin(); it != mapDiff.end(); ++it) + { + printf("Put Block pos %u crc %u\n", it->first, it->second->m_crc); + mgr.SaveBlockFromCompressedData(0, it->first, it->second->m_abCompBuf, it->second->m_sizeCompBuf); + } + + mgr.GetBlockCRCList(0, idx_client); + mgr.GetDiffBlocks(1, idx_client, mapDiff); + printf("#2 Diff %u\n", mapDiff.size()); + return 1; +} +#endif diff --git a/game/src/MarkManager.h b/game/src/MarkManager.h new file mode 100644 index 0000000..f8fa142 --- /dev/null +++ b/game/src/MarkManager.h @@ -0,0 +1,82 @@ +#ifndef __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__ +#define __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__ + +#include "MarkImage.h" + +class CGuildMarkManager : public singleton +{ + public: + enum + { + MAX_IMAGE_COUNT = 5, + INVALID_MARK_ID = 0xffffffff, + }; + + // Symbol + struct TGuildSymbol + { + DWORD crc; + std::vector raw; + }; + + CGuildMarkManager(); + virtual ~CGuildMarkManager(); + + const TGuildSymbol * GetGuildSymbol(DWORD GID); + bool LoadSymbol(const char* filename); + void SaveSymbol(const char* filename); + void UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData); + + // + // Mark + // + void SetMarkPathPrefix(const char * prefix); + + bool LoadMarkIndex(); // ũ ε ҷ ( ) + bool SaveMarkIndex(); // ũ ε ϱ + + void LoadMarkImages(); // ũ ̹ ҷ + void SaveMarkImage(DWORD imgIdx); // ũ ̹ + + bool GetMarkImageFilename(DWORD imgIdx, std::string & path) const; + bool AddMarkIDByGuildID(DWORD guildID, DWORD markID); + DWORD GetMarkImageCount() const; + DWORD GetMarkCount() const; + DWORD GetMarkID(DWORD guildID); + + // SERVER + void CopyMarkIdx(char * pcBuf) const; + DWORD SaveMark(DWORD guildID, BYTE * pbMarkImage); + void DeleteMark(DWORD guildID); + void GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map & mapDiffBlocks); + + // CLIENT + bool SaveBlockFromCompressedData(DWORD imgIdx, DWORD idBlock, const BYTE * pbBlock, DWORD dwSize); + bool GetBlockCRCList(DWORD imgIdx, DWORD * crcList); + + private: + // + // Mark + // + CGuildMarkImage * __NewImage(); + void __DeleteImage(CGuildMarkImage * pkImgDel); + + DWORD __AllocMarkID(DWORD guildID); + + CGuildMarkImage * __GetImage(DWORD imgIdx); + CGuildMarkImage * __GetImagePtr(DWORD idMark); + + std::map m_mapIdx_Image; // index = image index + std::map m_mapGID_MarkID; // index = guild id + + std::set m_setFreeMarkID; + std::string m_pathPrefix; + + private: + // + // Symbol + // + std::map m_mapSymbol; +}; + +#endif diff --git a/game/src/OXEvent.cpp b/game/src/OXEvent.cpp new file mode 100644 index 0000000..02cb6e4 --- /dev/null +++ b/game/src/OXEvent.cpp @@ -0,0 +1,441 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "questmanager.h" +#include "start_position.h" +#include "packet.h" +#include "buffer_manager.h" +#include "log.h" +#include "char.h" +#include "char_manager.h" +#include "OXEvent.h" +#include "desc.h" + +bool COXEventManager::Initialize() +{ + m_timedEvent = NULL; + m_map_char.clear(); + m_map_attender.clear(); + m_vec_quiz.clear(); + + SetStatus(OXEVENT_FINISH); + + return true; +} + +void COXEventManager::Destroy() +{ + CloseEvent(); + + m_map_char.clear(); + m_map_attender.clear(); + m_vec_quiz.clear(); + + SetStatus(OXEVENT_FINISH); +} + +OXEventStatus COXEventManager::GetStatus() +{ + BYTE ret = quest::CQuestManager::instance().GetEventFlag("oxevent_status"); + + switch (ret) + { + case 0 : + return OXEVENT_FINISH; + + case 1 : + return OXEVENT_OPEN; + + case 2 : + return OXEVENT_CLOSE; + + case 3 : + return OXEVENT_QUIZ; + + default : + return OXEVENT_ERR; + } + + return OXEVENT_ERR; +} + +void COXEventManager::SetStatus(OXEventStatus status) +{ + BYTE val = 0; + + switch (status) + { + case OXEVENT_OPEN : + val = 1; + break; + + case OXEVENT_CLOSE : + val = 2; + break; + + case OXEVENT_QUIZ : + val = 3; + break; + + case OXEVENT_FINISH : + case OXEVENT_ERR : + default : + val = 0; + break; + } + quest::CQuestManager::instance().RequestSetEventFlag("oxevent_status", val); +} + +bool COXEventManager::Enter(LPCHARACTER pkChar) +{ + if (GetStatus() == OXEVENT_FINISH) + { + sys_log(0, "OXEVENT : map finished. but char enter. %s", pkChar->GetName()); + return false; + } + + PIXEL_POSITION pos = pkChar->GetXYZ(); + + if (pos.x == 896500 && pos.y == 24600) + { + return EnterAttender(pkChar); + } + else if (pos.x == 896300 && pos.y == 28900) + { + return EnterAudience(pkChar); + } + else + { + sys_log(0, "OXEVENT : wrong pos enter %d %d", pos.x, pos.y); + return false; + } + + return false; +} + +bool COXEventManager::EnterAttender(LPCHARACTER pkChar) +{ + DWORD pid = pkChar->GetPlayerID(); + + m_map_char.insert(std::make_pair(pid, pid)); + m_map_attender.insert(std::make_pair(pid, pid)); + + return true; +} + +bool COXEventManager::EnterAudience(LPCHARACTER pkChar) +{ + DWORD pid = pkChar->GetPlayerID(); + + m_map_char.insert(std::make_pair(pid, pid)); + + return true; +} + +bool COXEventManager::AddQuiz(unsigned char level, const char* pszQuestion, bool answer) +{ + if (m_vec_quiz.size() < (size_t) level + 1) + m_vec_quiz.resize(level + 1); + + struct tag_Quiz tmpQuiz; + + tmpQuiz.level = level; + strlcpy(tmpQuiz.Quiz, pszQuestion, sizeof(tmpQuiz.Quiz)); + tmpQuiz.answer = answer; + + m_vec_quiz[level].push_back(tmpQuiz); + return true; +} + +bool COXEventManager::ShowQuizList(LPCHARACTER pkChar) +{ + int c = 0; + + for (size_t i = 0; i < m_vec_quiz.size(); ++i) + { + for (size_t j = 0; j < m_vec_quiz[i].size(); ++j, ++c) + { + pkChar->ChatPacket(CHAT_TYPE_INFO, "%d %s %s", m_vec_quiz[i][j].level, m_vec_quiz[i][j].Quiz, m_vec_quiz[i][j].answer ? LC_TEXT("") : LC_TEXT("")); + } + } + + pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" : %d"), c); + return true; +} + +void COXEventManager::ClearQuiz() +{ + for (unsigned int i = 0; i < m_vec_quiz.size(); ++i) + { + m_vec_quiz[i].clear(); + } + + m_vec_quiz.clear(); +} + +EVENTINFO(OXEventInfoData) +{ + bool answer; + + OXEventInfoData() + : answer( false ) + { + } +}; + +EVENTFUNC(oxevent_timer) +{ + static BYTE flag = 0; + OXEventInfoData* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "oxevent_timer> Null pointer" ); + return 0; + } + + switch (flag) + { + case 0: + SendNoticeMap(LC_TEXT("10ʵ ϰڽϴ."), OXEVENT_MAP_INDEX, true); + flag++; + return PASSES_PER_SEC(10); + + case 1: + SendNoticeMap(LC_TEXT(""), OXEVENT_MAP_INDEX, true); + + if (info->answer == true) + { + COXEventManager::instance().CheckAnswer(true); + SendNoticeMap(LC_TEXT("O Դϴ"), OXEVENT_MAP_INDEX, true); + } + else + { + COXEventManager::instance().CheckAnswer(false); + SendNoticeMap(LC_TEXT("X Դϴ"), OXEVENT_MAP_INDEX, true); + } + + if (LC_IsJapan()) + { + SendNoticeMap("ԈႦXOɈړ܂B", OXEVENT_MAP_INDEX, true); + } + else + { + SendNoticeMap(LC_TEXT("5 Ʋ е ٱ ̵ Űڽϴ."), OXEVENT_MAP_INDEX, true); + } + + flag++; + return PASSES_PER_SEC(5); + + case 2: + COXEventManager::instance().WarpToAudience(); + COXEventManager::instance().SetStatus(OXEVENT_CLOSE); + SendNoticeMap(LC_TEXT(" غּ."), OXEVENT_MAP_INDEX, true); + flag = 0; + break; + } + return 0; +} + +bool COXEventManager::Quiz(unsigned char level, int timelimit) +{ + if (m_vec_quiz.size() == 0) return false; + if (level > m_vec_quiz.size()) level = m_vec_quiz.size() - 1; + if (m_vec_quiz[level].size() <= 0) return false; + + if (timelimit < 0) timelimit = 30; + + int idx = number(0, m_vec_quiz[level].size()-1); + + SendNoticeMap(LC_TEXT(" Դϴ."), OXEVENT_MAP_INDEX, true); + SendNoticeMap(m_vec_quiz[level][idx].Quiz, OXEVENT_MAP_INDEX, true); + SendNoticeMap(LC_TEXT(" O, Ʋ X ̵ּ"), OXEVENT_MAP_INDEX, true); + + if (m_timedEvent != NULL) { + event_cancel(&m_timedEvent); + } + + OXEventInfoData* answer = AllocEventInfo(); + + answer->answer = m_vec_quiz[level][idx].answer; + + timelimit -= 15; + m_timedEvent = event_create(oxevent_timer, answer, PASSES_PER_SEC(timelimit)); + + SetStatus(OXEVENT_QUIZ); + + m_vec_quiz[level].erase(m_vec_quiz[level].begin()+idx); + return true; +} + +bool COXEventManager::CheckAnswer(bool answer) +{ + if (m_map_attender.size() <= 0) return true; + + itertype(m_map_attender) iter = m_map_attender.begin(); + itertype(m_map_attender) iter_tmp; + + m_map_miss.clear(); + + int rect[4]; + if (answer != true) + { + rect[0] = 892600; + rect[1] = 22900; + rect[2] = 896300; + rect[3] = 26400; + } + else + { + rect[0] = 896600; + rect[1] = 22900; + rect[2] = 900300; + rect[3] = 26400; + } + + LPCHARACTER pkChar = NULL; + PIXEL_POSITION pos; + for (; iter != m_map_attender.end();) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + if (pkChar != NULL) + { + pos = pkChar->GetXYZ(); + + if (pos.x < rect[0] || pos.x > rect[2] || pos.y < rect[1] || pos.y > rect[3]) + { + pkChar->EffectPacket(SE_FAIL); + iter_tmp = iter; + iter++; + m_map_attender.erase(iter_tmp); + m_map_miss.insert(std::make_pair(pkChar->GetPlayerID(), pkChar->GetPlayerID())); + } + else + { + pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Դϴ!")); + // pkChar->CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), pkChar); + char chatbuf[256]; + int len = snprintf(chatbuf, sizeof(chatbuf), + "%s %u %u", number(0, 1) == 1 ? "cheer1" : "cheer2", (DWORD)pkChar->GetVID(), 0); + + // ϰ sizeof(chatbuf) ̻ truncateǾٴ .. + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + // \0 + ++len; + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + pkChar->PacketAround(buf.read_peek(), buf.size()); + pkChar->EffectPacket(SE_SUCCESS); + + ++iter; + } + } + else + { + itertype(m_map_char) err = m_map_char.find(iter->first); + if (err != m_map_char.end()) m_map_char.erase(err); + + itertype(m_map_miss) err2 = m_map_miss.find(iter->first); + if (err2 != m_map_miss.end()) m_map_miss.erase(err2); + + iter_tmp = iter; + ++iter; + m_map_attender.erase(iter_tmp); + } + } + return true; +} + +void COXEventManager::WarpToAudience() +{ + if (m_map_miss.size() <= 0) return; + + itertype(m_map_miss) iter = m_map_miss.begin(); + LPCHARACTER pkChar = NULL; + + for (; iter != m_map_miss.end(); ++iter) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar != NULL) + { + switch ( number(0, 3)) + { + case 0 : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break; + case 1 : pkChar->Show(OXEVENT_MAP_INDEX, 890900, 28100); break; + case 2 : pkChar->Show(OXEVENT_MAP_INDEX, 896600, 20500); break; + case 3 : pkChar->Show(OXEVENT_MAP_INDEX, 902500, 28100); break; + default : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break; + } + } + } + + m_map_miss.clear(); +} + +bool COXEventManager::CloseEvent() +{ + if (m_timedEvent != NULL) { + event_cancel(&m_timedEvent); + } + + itertype(m_map_char) iter = m_map_char.begin(); + + LPCHARACTER pkChar = NULL; + for (; iter != m_map_char.end(); ++iter) + { + pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar != NULL) + pkChar->WarpSet(EMPIRE_START_X(pkChar->GetEmpire()), EMPIRE_START_Y(pkChar->GetEmpire())); + } + + m_map_char.clear(); + + return true; +} + +bool COXEventManager::LogWinner() +{ + itertype(m_map_attender) iter = m_map_attender.begin(); + + for (; iter != m_map_attender.end(); ++iter) + { + LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar) + LogManager::instance().CharLog(pkChar, 0, "OXEVENT", "LastManStanding"); + } + + return true; +} + +bool COXEventManager::GiveItemToAttender(DWORD dwItemVnum, BYTE count) +{ + itertype(m_map_attender) iter = m_map_attender.begin(); + + for (; iter != m_map_attender.end(); ++iter) + { + LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second); + + if (pkChar) + { + pkChar->AutoGiveItem(dwItemVnum, count); + LogManager::instance().ItemLog(pkChar->GetPlayerID(), 0, count, dwItemVnum, "OXEVENT_REWARD", "", pkChar->GetDesc()->GetHostName(), dwItemVnum); + } + } + + return true; +} + diff --git a/game/src/OXEvent.h b/game/src/OXEvent.h new file mode 100644 index 0000000..c6d716e --- /dev/null +++ b/game/src/OXEvent.h @@ -0,0 +1,65 @@ + +#define OXEVENT_MAP_INDEX 113 + +struct tag_Quiz +{ + char level; + char Quiz[256]; + bool answer; +}; + +enum OXEventStatus +{ + OXEVENT_FINISH = 0, // OX̺Ʈ + OXEVENT_OPEN = 1, // OX̺Ʈ ۵. (20012) ؼ 尡 + OXEVENT_CLOSE = 2, // OX̺Ʈ . (20012) ܵ + OXEVENT_QUIZ = 3, //  . + + OXEVENT_ERR = 0xff +}; + +class COXEventManager : public singleton +{ + private : + std::map m_map_char; + std::map m_map_attender; + std::map m_map_miss; + + std::vector > m_vec_quiz; + + LPEVENT m_timedEvent; + + protected : + bool CheckAnswer(); + + bool EnterAudience(LPCHARACTER pChar); + bool EnterAttender(LPCHARACTER pChar); + + public : + bool Initialize(); + void Destroy(); + + OXEventStatus GetStatus(); + void SetStatus(OXEventStatus status); + + bool LoadQuizScript(const char* szFileName); + + bool Enter(LPCHARACTER pChar); + + bool CloseEvent(); + + void ClearQuiz(); + bool AddQuiz(unsigned char level, const char* pszQuestion, bool answer); + bool ShowQuizList(LPCHARACTER pChar); + + bool Quiz(unsigned char level, int timelimit); + bool GiveItemToAttender(DWORD dwItemVnum, BYTE count); + + bool CheckAnswer(bool answer); + void WarpToAudience(); + + bool LogWinner(); + + DWORD GetAttenderCount() { return m_map_attender.size(); } +}; + diff --git a/game/src/PetSystem.cpp b/game/src/PetSystem.cpp new file mode 100644 index 0000000..892d4e2 --- /dev/null +++ b/game/src/PetSystem.cpp @@ -0,0 +1,637 @@ +#include "stdafx.h" +#include "utils.h" +#include "vector.h" +#include "char.h" +#include "sectree_manager.h" +#include "char_manager.h" +#include "mob_manager.h" +#include "PetSystem.h" +#include "../../common/VnumHelper.h" +#include "packet.h" +#include "item_manager.h" +#include "item.h" + + +extern int passes_per_sec; +EVENTINFO(petsystem_event_info) +{ + CPetSystem* pPetSystem; +}; + +// PetSystem update ִ event. +// PetSystem CHRACTER_MANAGER FSM update ִ chracters ޸, +// Owner STATE update _UpdateFollowAI Լ update ش. +// ׷ owner state update CHRACTER_MANAGER ֱ , +// petsystem updateϴٰ pet unsummonϴ κп . +// (CHRACTER_MANAGER update ϸ chracter destroy pendingǾ, CPetSystem dangling ͸ ְ ȴ.) +// PetSystem Ʈ ִ event ߻Ŵ. +EVENTFUNC(petsystem_update_event) +{ + petsystem_event_info* info = dynamic_cast( event->info ); + if ( info == NULL ) + { + sys_err( "check_speedhack_event> Null pointer" ); + return 0; + } + + CPetSystem* pPetSystem = info->pPetSystem; + + if (NULL == pPetSystem) + return 0; + + + pPetSystem->Update(0); + // 0.25ʸ . + return PASSES_PER_SEC(1) / 4; +} + +/// NOTE: 1ijͰ  ִ ... ij͸ ٸ ҰŶ ֵ... .. +/// ִ ÿ ȯ ִ Ʋ ִµ ̷ ȹ ϴ +const float PET_COUNT_LIMIT = 3; + +/////////////////////////////////////////////////////////////////////////////////////// +// CPetActor +/////////////////////////////////////////////////////////////////////////////////////// + +CPetActor::CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options) +{ + m_dwVnum = vnum; + m_dwVID = 0; + m_dwOptions = options; + m_dwLastActionTime = 0; + + m_pkChar = 0; + m_pkOwner = owner; + + m_originalMoveSpeed = 0; + + m_dwSummonItemVID = 0; + m_dwSummonItemVnum = 0; +} + +CPetActor::~CPetActor() +{ + this->Unsummon(); + + m_pkOwner = 0; +} + +void CPetActor::SetName(const char* name) +{ + std::string petName = m_pkOwner->GetName(); + + if (0 != m_pkOwner && + 0 == name && + 0 != m_pkOwner->GetName()) + { + petName += "'s Pet"; + } + else + petName += name; + + if (true == IsSummoned()) + m_pkChar->SetName(petName); + + m_name = petName; +} + +bool CPetActor::Mount() +{ + if (0 == m_pkOwner) + return false; + + if (true == HasOption(EPetOption_Mountable)) + m_pkOwner->MountVnum(m_dwVnum); + + return m_pkOwner->GetMountVnum() == m_dwVnum;; +} + +void CPetActor::Unmount() +{ + if (0 == m_pkOwner) + return; + + if (m_pkOwner->IsHorseRiding()) + m_pkOwner->StopRiding(); +} + +void CPetActor::Unsummon() +{ + if (true == this->IsSummoned()) + { + // + this->ClearBuff(); + this->SetSummonItem(NULL); + if (NULL != m_pkOwner) + m_pkOwner->ComputePoints(); + + if (NULL != m_pkChar) + M2_DESTROY_CHARACTER(m_pkChar); + + m_pkChar = 0; + m_dwVID = 0; + } +} + +DWORD CPetActor::Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar) +{ + long x = m_pkOwner->GetX(); + long y = m_pkOwner->GetY(); + long z = m_pkOwner->GetZ(); + + if (true == bSpawnFar) + { + x += (number(0, 1) * 2 - 1) * number(2000, 2500); + y += (number(0, 1) * 2 - 1) * number(2000, 2500); + } + else + { + x += number(-100, 100); + y += number(-100, 100); + } + + if (0 != m_pkChar) + { + m_pkChar->Show (m_pkOwner->GetMapIndex(), x, y); + m_dwVID = m_pkChar->GetVID(); + + return m_dwVID; + } + + m_pkChar = CHARACTER_MANAGER::instance().SpawnMob( + m_dwVnum, + m_pkOwner->GetMapIndex(), + x, y, z, + false, (int)(m_pkOwner->GetRotation()+180), false); + + if (0 == m_pkChar) + { + sys_err("[CPetSystem::Summon] Failed to summon the pet. (vnum: %d)", m_dwVnum); + return 0; + } + + m_pkChar->SetPet(); + +// m_pkOwner->DetailLog(); +// m_pkChar->DetailLog(); + + // . + m_pkChar->SetEmpire(m_pkOwner->GetEmpire()); + + m_dwVID = m_pkChar->GetVID(); + + this->SetName(petName); + + // SetSummonItem(pSummonItem) θ Ŀ ComputePoints θ . + this->SetSummonItem(pSummonItem); + m_pkOwner->ComputePoints(); + m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z); + + return m_dwVID; +} + +bool CPetActor::_UpdatAloneActionAI(float fMinDist, float fMaxDist) +{ + float fDist = number(fMinDist, fMaxDist); + float r = (float)number (0, 359); + float dest_x = GetOwner()->GetX() + fDist * cos(r); + float dest_y = GetOwner()->GetY() + fDist * sin(r); + + //m_pkChar->SetRotation(number(0, 359)); // + + //GetDeltaByDegree(m_pkChar->GetRotation(), fDist, &fx, &fy); + + // Ӽ üũ; ġ ߰ ġ ٸ ʴ´. + //if (!(SECTREE_MANAGER::instance().IsMovablePosition(m_pkChar->GetMapIndex(), m_pkChar->GetX() + (int) fx, m_pkChar->GetY() + (int) fy) + // && SECTREE_MANAGER::instance().IsMovablePosition(m_pkChar->GetMapIndex(), m_pkChar->GetX() + (int) fx/2, m_pkChar->GetY() + (int) fy/2))) + // return true; + + m_pkChar->SetNowWalking(true); + + //if (m_pkChar->Goto(m_pkChar->GetX() + (int) fx, m_pkChar->GetY() + (int) fy)) + // m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + if (!m_pkChar->IsStateMove() && m_pkChar->Goto(dest_x, dest_y)) + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + m_dwLastActionTime = get_dword_time(); + + return true; +} + +// char_state.cpp StateHorseԼ ׳ C&P -_-; +bool CPetActor::_UpdateFollowAI() +{ + if (0 == m_pkChar->m_pkMobData) + { + //sys_err("[CPetActor::_UpdateFollowAI] m_pkChar->m_pkMobData is NULL"); + return false; + } + + // NOTE: ij() ̵ ӵ ˾ƾ ϴµ, ش (m_pkChar->m_pkMobData->m_table.sMovingSpeed) ؼ ˾Ƴ + // m_pkChar->m_pkMobData invalid 찡 ߻. ð ľϰ ϴ m_pkChar->m_pkMobData ƿ ʵ . + // ⼭ Ź ˻ϴ ʱȭ 쵵 .. -_-;; ФФФФФФФФ + if (0 == m_originalMoveSpeed) + { + const CMob* mobData = CMobManager::Instance().Get(m_dwVnum); + + if (0 != mobData) + m_originalMoveSpeed = mobData->m_table.sMovingSpeed; + } + float START_FOLLOW_DISTANCE = 300.0f; // Ÿ ̻ Ѿư + float START_RUN_DISTANCE = 900.0f; // Ÿ ̻ پ Ѿư. + + float RESPAWN_DISTANCE = 4500.f; // Ÿ ̻ ־ ȯ. + int APPROACH = 200; // Ÿ + + bool bDoMoveAlone = true; // ijͿ ȥ ϰ -_-; + bool bRun = false; // پ ϳ? + + DWORD currentTime = get_dword_time(); + + long ownerX = m_pkOwner->GetX(); long ownerY = m_pkOwner->GetY(); + long charX = m_pkChar->GetX(); long charY = m_pkChar->GetY(); + + float fDist = DISTANCE_APPROX(charX - ownerX, charY - ownerY); + + if (fDist >= RESPAWN_DISTANCE) + { + float fOwnerRot = m_pkOwner->GetRotation() * 3.141592f / 180.f; + float fx = -APPROACH * cos(fOwnerRot); + float fy = -APPROACH * sin(fOwnerRot); + if (m_pkChar->Show(m_pkOwner->GetMapIndex(), ownerX + fx, ownerY + fy)) + { + return true; + } + } + + + if (fDist >= START_FOLLOW_DISTANCE) + { + if( fDist >= START_RUN_DISTANCE) + { + bRun = true; + } + + m_pkChar->SetNowWalking(!bRun); // NOTE: Լ ̸ ߴ° ˾Ҵµ SetNowWalking(false) ϸ ٴ°.. -_-; + + Follow(APPROACH); + + m_pkChar->SetLastAttacked(currentTime); + m_dwLastActionTime = currentTime; + } + //else + //{ + // if (fabs(m_pkChar->GetRotation() - GetDegreeFromPositionXY(charX, charY, ownerX, ownerX)) > 10.f || fabs(m_pkChar->GetRotation() - GetDegreeFromPositionXY(charX, charY, ownerX, ownerX)) < 350.f) + // { + // m_pkChar->Follow(m_pkOwner, APPROACH); + // m_pkChar->SetLastAttacked(currentTime); + // m_dwLastActionTime = currentTime; + // } + //} + // Follow ΰ Ÿ ̳ ٸ + else + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + //else if (currentTime - m_dwLastActionTime > number(5000, 12000)) + //{ + // this->_UpdatAloneActionAI(START_FOLLOW_DISTANCE / 2, START_FOLLOW_DISTANCE); + //} + + return true; +} + +bool CPetActor::Update(DWORD deltaTime) +{ + bool bResult = true; + + // ׾ų, ȯ ° ̻ϴٸ . (NOTE: ̷ ȯ DEAD ¿ 찡 -_-;) + // ȯ ų, ° ƴ϶ . + if (m_pkOwner->IsDead() || (IsSummoned() && m_pkChar->IsDead()) + || NULL == ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID()) + || ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID())->GetOwner() != this->GetOwner() + ) + { + this->Unsummon(); + return true; + } + + if (this->IsSummoned() && HasOption(EPetOption_Followable)) + bResult = bResult && this->_UpdateFollowAI(); + + return bResult; +} + +//NOTE : !!! MinDistance ũ ŭ ȭ follow ʴ´, +bool CPetActor::Follow(float fMinDistance) +{ + // ġ ٶ Ѵ. + if( !m_pkOwner || !m_pkChar) + return false; + + float fOwnerX = m_pkOwner->GetX(); + float fOwnerY = m_pkOwner->GetY(); + + float fPetX = m_pkChar->GetX(); + float fPetY = m_pkChar->GetY(); + + float fDist = DISTANCE_SQRT(fOwnerX - fPetX, fOwnerY - fPetY); + if (fDist <= fMinDistance) + return false; + + m_pkChar->SetRotationToXY(fOwnerX, fOwnerY); + + float fx, fy; + + float fDistToGo = fDist - fMinDistance; + GetDeltaByDegree(m_pkChar->GetRotation(), fDistToGo, &fx, &fy); + + if (!m_pkChar->Goto((int)(fPetX+fx+0.5f), (int)(fPetY+fy+0.5f)) ) + return false; + + m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0, 0); + + return true; +} + +void CPetActor::SetSummonItem (LPITEM pItem) +{ + if (NULL == pItem) + { + m_dwSummonItemVID = 0; + m_dwSummonItemVnum = 0; + return; + } + + m_dwSummonItemVID = pItem->GetVID(); + m_dwSummonItemVnum = pItem->GetVnum(); +} + +void CPetActor::GiveBuff() +{ + // Ȳ ߻. + if (34004 == m_dwVnum || 34009 == m_dwVnum) + { + if (NULL == m_pkOwner->GetDungeon()) + { + return; + } + } + LPITEM item = ITEM_MANAGER::instance().FindByVID(m_dwSummonItemVID); + if (NULL != item) + item->ModifyPoints(true); + return ; +} + +void CPetActor::ClearBuff() +{ + if (NULL == m_pkOwner) + return ; + TItemTable* item_proto = ITEM_MANAGER::instance().GetTable(m_dwSummonItemVnum); + if (NULL == item_proto) + return; + for (int i = 0; i < ITEM_APPLY_MAX_NUM; i++) + { + if (item_proto->aApplies[i].bType == APPLY_NONE) + continue; + m_pkOwner->ApplyPoint(item_proto->aApplies[i].bType, -item_proto->aApplies[i].lValue); + } + + return ; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// CPetSystem +/////////////////////////////////////////////////////////////////////////////////////// + +CPetSystem::CPetSystem(LPCHARACTER owner) +{ +// assert(0 != owner && "[CPetSystem::CPetSystem] Invalid owner"); + + m_pkOwner = owner; + m_dwUpdatePeriod = 400; + + m_dwLastUpdateTime = 0; +} + +CPetSystem::~CPetSystem() +{ + Destroy(); +} + +void CPetSystem::Destroy() +{ + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor) + { + delete petActor; + } + } + event_cancel(&m_pkPetSystemUpdateEvent); + m_petActorMap.clear(); +} + +/// ý Ʈ. ϵ AI ó . +bool CPetSystem::Update(DWORD deltaTime) +{ + bool bResult = true; + + DWORD currentTime = get_dword_time(); + + // CHARACTER_MANAGER ijͷ Update Ű ִ (Pulse Ǿִ) Ӱ ð ˾Ҵµ + // ٸ ̶-_-; ⿡ Է deltaTime ǹ̰ Ф + + if (m_dwUpdatePeriod > currentTime - m_dwLastUpdateTime) + return true; + + std::vector v_garbageActor; + + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor && petActor->IsSummoned()) + { + LPCHARACTER pPet = petActor->GetCharacter(); + + if (NULL == CHARACTER_MANAGER::instance().Find(pPet->GetVID())) + { + v_garbageActor.push_back(petActor); + } + else + { + bResult = bResult && petActor->Update(deltaTime); + } + } + } + for (std::vector::iterator it = v_garbageActor.begin(); it != v_garbageActor.end(); it++) + DeletePet(*it); + + m_dwLastUpdateTime = currentTime; + + return bResult; +} + +/// Ͽ +void CPetSystem::DeletePet(DWORD mobVnum) +{ + TPetActorMap::iterator iter = m_petActorMap.find(mobVnum); + + if (m_petActorMap.end() == iter) + { + sys_err("[CPetSystem::DeletePet] Can't find pet on my list (VNUM: %d)", mobVnum); + return; + } + + CPetActor* petActor = iter->second; + + if (0 == petActor) + sys_err("[CPetSystem::DeletePet] Null Pointer (petActor)"); + else + delete petActor; + + m_petActorMap.erase(iter); +} + +/// Ͽ +void CPetSystem::DeletePet(CPetActor* petActor) +{ + for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + if (iter->second == petActor) + { + delete petActor; + m_petActorMap.erase(iter); + + return; + } + } + + sys_err("[CPetSystem::DeletePet] Can't find petActor(0x%x) on my list(size: %d) ", petActor, m_petActorMap.size()); +} + +void CPetSystem::Unsummon(DWORD vnum, bool bDeleteFromList) +{ + CPetActor* actor = this->GetByVnum(vnum); + + if (0 == actor) + { + sys_err("[CPetSystem::GetByVnum(%d)] Null Pointer (petActor)", vnum); + return; + } + actor->Unsummon(); + + if (true == bDeleteFromList) + this->DeletePet(actor); + + bool bActive = false; + for (TPetActorMap::iterator it = m_petActorMap.begin(); it != m_petActorMap.end(); it++) + { + bActive |= it->second->IsSummoned(); + } + if (false == bActive) + { + event_cancel(&m_pkPetSystemUpdateEvent); + m_pkPetSystemUpdateEvent = NULL; + } +} + + +CPetActor* CPetSystem::Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options) +{ + CPetActor* petActor = this->GetByVnum(mobVnum); + + // ϵ ƴ϶ Ͽ . + if (0 == petActor) + { + petActor = M2_NEW CPetActor(m_pkOwner, mobVnum, options); + m_petActorMap.insert(std::make_pair(mobVnum, petActor)); + } + + DWORD petVID = petActor->Summon(petName, pSummonItem, bSpawnFar); + + if (NULL == m_pkPetSystemUpdateEvent) + { + petsystem_event_info* info = AllocEventInfo(); + + info->pPetSystem = this; + + m_pkPetSystemUpdateEvent = event_create(petsystem_update_event, info, PASSES_PER_SEC(1) / 4); // 0.25 + } + + return petActor; +} + + +CPetActor* CPetSystem::GetByVID(DWORD vid) const +{ + CPetActor* petActor = 0; + + bool bFound = false; + + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + petActor = iter->second; + + if (0 == petActor) + { + sys_err("[CPetSystem::GetByVID(%d)] Null Pointer (petActor)", vid); + continue; + } + + bFound = petActor->GetVID() == vid; + + if (true == bFound) + break; + } + + return bFound ? petActor : 0; +} + +/// ߿ ־ VNUM ͸ ȯϴ Լ. +CPetActor* CPetSystem::GetByVnum(DWORD vnum) const +{ + CPetActor* petActor = 0; + + TPetActorMap::const_iterator iter = m_petActorMap.find(vnum); + + if (m_petActorMap.end() != iter) + petActor = iter->second; + + return petActor; +} + +size_t CPetSystem::CountSummoned() const +{ + size_t count = 0; + + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor) + { + if (petActor->IsSummoned()) + ++count; + } + } + + return count; +} + +void CPetSystem::RefreshBuff() +{ + for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter) + { + CPetActor* petActor = iter->second; + + if (0 != petActor) + { + if (petActor->IsSummoned()) + { + petActor->GiveBuff(); + } + } + } +} \ No newline at end of file diff --git a/game/src/PetSystem.h b/game/src/PetSystem.h new file mode 100644 index 0000000..586d3c2 --- /dev/null +++ b/game/src/PetSystem.h @@ -0,0 +1,163 @@ +#ifndef __HEADER_PET_SYSTEM__ +#define __HEADER_PET_SYSTEM__ + + +class CHARACTER; + +// TODO: μ ɷġ? ģе, Ÿ... ġ +struct SPetAbility +{ +}; + +/** +*/ +class CPetActor //: public CHARACTER +{ +public: + enum EPetOptions + { + EPetOption_Followable = 1 << 0, + EPetOption_Mountable = 1 << 1, + EPetOption_Summonable = 1 << 2, + EPetOption_Combatable = 1 << 3, + }; + + +protected: + friend class CPetSystem; + + CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options = EPetOption_Followable | EPetOption_Summonable); +// CPetActor(LPCHARACTER owner, DWORD vnum, const SPetAbility& petAbility, DWORD options = EPetOption_Followable | EPetOption_Summonable); + + virtual ~CPetActor(); + + virtual bool Update(DWORD deltaTime); + +protected: + virtual bool _UpdateFollowAI(); ///< ٴϴ AI ó + virtual bool _UpdatAloneActionAI(float fMinDist, float fMaxDist); ///< ó ȥ AI ó + + /// @TODO + //virtual bool _UpdateCombatAI(); + +private: + bool Follow(float fMinDistance = 50.f); + +public: + LPCHARACTER GetCharacter() const { return m_pkChar; } + LPCHARACTER GetOwner() const { return m_pkOwner; } + DWORD GetVID() const { return m_dwVID; } + DWORD GetVnum() const { return m_dwVnum; } + + bool HasOption(EPetOptions option) const { return m_dwOptions & option; } + + void SetName(const char* petName); + + bool Mount(); + void Unmount(); + + DWORD Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar = false); + void Unsummon(); + + bool IsSummoned() const { return 0 != m_pkChar; } + void SetSummonItem (LPITEM pItem); + DWORD GetSummonItemVID () { return m_dwSummonItemVID; } + // ִ Լ ŵδ Լ. + // ̰ Ѱ, , + // POINT_MOV_SPEED, POINT_ATT_SPEED, POINT_CAST_SPEED PointChange() Լ Ἥ ҿ °, + // PointChange() Ŀ 𼱰 ComputePoints() ϸ ϴ ʱȭǰ, + // , ComputePoints() θ Ŭ POINT ʴ´ٴ Ŵ. + // ׷ ִ ComputePoints() ο petsystem->RefreshBuff() θ Ͽ, + // ClearBuff() θ, ComputePoints ϴ Ѵ. + void GiveBuff(); + void ClearBuff(); + +private: + DWORD m_dwVnum; + DWORD m_dwVID; + DWORD m_dwOptions; + DWORD m_dwLastActionTime; + DWORD m_dwSummonItemVID; + DWORD m_dwSummonItemVnum; + + short m_originalMoveSpeed; + + std::string m_name; + + LPCHARACTER m_pkChar; // Instance of pet(CHARACTER) + LPCHARACTER m_pkOwner; + +// SPetAbility m_petAbility; // ɷġ +}; + +/** +*/ +class CPetSystem +{ +public: + typedef boost::unordered_map TPetActorMap; /// map. ( ijͰ vnum ..??) + +public: + CPetSystem(LPCHARACTER owner); + virtual ~CPetSystem(); + + CPetActor* GetByVID(DWORD vid) const; + CPetActor* GetByVnum(DWORD vnum) const; + + bool Update(DWORD deltaTime); + void Destroy(); + + size_t CountSummoned() const; ///< ȯ(üȭ ijͰ ִ) + +public: + void SetUpdatePeriod(DWORD ms); + + CPetActor* Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable); + + void Unsummon(DWORD mobVnum, bool bDeleteFromList = false); + void Unsummon(CPetActor* petActor, bool bDeleteFromList = false); + + // TODO: ¥ ý  . (ijͰ ߰ ...) + CPetActor* AddPet(DWORD mobVnum, const char* petName, const SPetAbility& ability, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable | CPetActor::EPetOption_Combatable); + + void DeletePet(DWORD mobVnum); + void DeletePet(CPetActor* petActor); + void RefreshBuff(); + +private: + TPetActorMap m_petActorMap; + LPCHARACTER m_pkOwner; ///< ý Owner + DWORD m_dwUpdatePeriod; ///< Ʈ ֱ (ms) + DWORD m_dwLastUpdateTime; + LPEVENT m_pkPetSystemUpdateEvent; +}; + +/** +// Summon Pet +CPetSystem* petSystem = mainChar->GetPetSystem(); +CPetActor* petActor = petSystem->Summon(~~~); + +DWORD petVID = petActor->GetVID(); +if (0 == petActor) +{ + ERROR_LOG(...) +}; + + +// Unsummon Pet +petSystem->Unsummon(petVID); + +// Mount Pet +petActor->Mount().. + + +CPetActor::Update(...) +{ + // AI : Follow, actions, etc... +} + +*/ + + + +#endif //__HEADER_PET_SYSTEM__ \ No newline at end of file diff --git a/game/src/SpeedServer.cpp b/game/src/SpeedServer.cpp new file mode 100644 index 0000000..e564ec4 --- /dev/null +++ b/game/src/SpeedServer.cpp @@ -0,0 +1,399 @@ +#include "stdafx.h" +#include +#include "SpeedServer.h" +#include "locale_service.h" + +// 赵 ʽ ġ ý +// by rtsummit + +CSpeedServerManager::CSpeedServerManager() +{ +} + +CSpeedServerManager::~CSpeedServerManager() +{ +} + +CSpeedServerEmpireExp::CSpeedServerEmpireExp() +{ +} + +CSpeedServerEmpireExp::~CSpeedServerEmpireExp() +{ +} + +bool CSpeedServerManager::Initialize() +{ + for (int i = 1; i < EMPIRE_MAX_NUM; i++) + { + sys_log (0,"speed manager init"); + if(!Empire[i].Initialize (i)) + { + sys_err ("EMPIRE %d Exp Bonus Manager Init fail",i); + return false; + } + } + return true; +} + +bool CSpeedServerEmpireExp::Initialize (BYTE e) +{ + empire = e; + sys_log (0, "empire exp init %d", empire); + snprintf (file_name, sizeof(file_name), "%s/exp_bonus_table_%d.txt", LocaleService_GetBasePath().c_str(), empire); + + for (int i = 1; i < 6; i++) + { + wday_exp_table[i].push_back (HME (18, 0, 50)); + wday_exp_table[i].push_back (HME (24, 0, 100)); + } + + wday_exp_table[0].push_back (HME (18, 0, 100)); + wday_exp_table[0].push_back (HME (24, 0, 150)); + wday_exp_table[6].push_back (HME (18, 0, 100)); + wday_exp_table[6].push_back (HME (24, 0, 150)); + + LoadExpTable(); + return true; +} + +bool CSpeedServerEmpireExp::LoadWdayExpTable(int wday, char *str) +{ + std::list &lst = wday_exp_table[wday]; + lst.clear(); + char *p, *n; + const char *delim = " \t\r\n"; + char *t; + char *h, *m, *e; + int hour, min, exp; + sys_log (0, "str %s", str); + strtok (str, delim); + p = strtok (NULL, ";"); + n = strtok (NULL, ";"); + while (p != NULL) + { + t = strtok (p, delim); + e = strtok (NULL, delim); + h = strtok (t, ":"); + m = strtok (NULL, delim); + if (!str_to_number (hour, h) || !str_to_number (min, m) || !str_to_number (exp, e)) + { + sys_log (0, "h m e : %s %s %s",h, m, e); + sys_err ("Invalid argument. Please insert hh:mm exp"); + return false; + } + sys_log (0, "h m e : %s %s %s",h, m, e); + + lst.push_back (HME (hour, min, exp)); + p = strtok (n, ";"); + n = strtok (NULL, ";"); + } + return true; +} + +bool CSpeedServerManager::WriteExpTableOfEmpire(BYTE empire) +{ + return Empire[empire].WriteExpTable(); +} + +bool CSpeedServerEmpireExp::WriteExpTable() +{ + FILE *fp; + + sys_log (0, "write"); + if (0==file_name || 0==file_name[0]) + return false; + + if ((fp = fopen(file_name, "w"))==0) + { + return false; + } + + char wday_name[7][4] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; + for (int i = 0; i < 7; i++) + { + fprintf (fp, "%s", wday_name[i]); + for (std::list ::iterator it = wday_exp_table[i].begin(); it != wday_exp_table[i].end(); it++) + { + fprintf (fp, " %d:%d %d;", it->hour, it->min, it->exp); + } + fprintf(fp, "\n"); + } + + for (std::map >::iterator holi_it = holiday_map.begin(); holi_it != holiday_map.end(); holi_it++) + { + fprintf (fp, "HOLIDAY %d.%d.%d", holi_it->first.year + 1900, holi_it->first.mon + 1, holi_it->first.day); + for (std::list ::iterator it = holi_it->second.begin(); it != holi_it->second.end(); it++) + { + fprintf (fp, " %d:%d %d;", it->hour, it->min, it->exp); + } + fprintf(fp, "\n"); + } + fclose (fp); + return true; +} + +bool CSpeedServerEmpireExp::LoadExpTable() +{ + FILE *fp; + char one_line[256]; + char temp[256]; + const char *delim = " \t\r\n"; + + sys_log (0, "load"); + if (0==file_name || 0==file_name[0]) + return false; + + if ((fp = fopen(file_name, "r"))==0) + return false; + + while (fgets(one_line, 256, fp)) + { + if (one_line[0]=='#') + continue; + + strcpy(temp, one_line); + + const char* token_string = strtok(one_line, delim); + + if (NULL==token_string) + continue; + + TOKEN("SUN") + { + LoadWdayExpTable (0, temp); + } + else TOKEN("MON") + { + LoadWdayExpTable (1, temp); + } + else TOKEN("TUE") + { + LoadWdayExpTable (2, temp); + } + else TOKEN("WED") + { + LoadWdayExpTable (3, temp); + } + else TOKEN("THU") + { + LoadWdayExpTable (4, temp); + } + else TOKEN("FRI") + { + LoadWdayExpTable (5, temp); + } + else TOKEN("SAT") + { + LoadWdayExpTable (6, temp); + } + else TOKEN("HOLIDAY") + { + std::list lst; + lst.clear(); + char *p, *n; + char *t, *v; + char *h, *m, *e; + int hour, min, exp; + + v = strtok (temp, delim); + v = strtok (NULL, delim); + sys_log (0, "holiday %s", v); + + p = strtok (NULL, ";"); + n = strtok (NULL, ";"); + while (p != NULL) + { + t = strtok (p, delim); + e = strtok (NULL, delim); + h = strtok (t, ":"); + m = strtok (NULL, delim); + if (!str_to_number (hour, h) || !str_to_number (min, m) || !str_to_number (exp, e)) + { + sys_log (0, "h m e : %s %s %s",h, m, e); + sys_err ("Invalid argument. Please insert hh:mm exp"); + return false; + } + sys_log (0, "h m e : %s %s %s",h, m, e); + + lst.push_back (HME (hour, min, exp)); + p = strtok (n, ";"); + n = strtok (NULL, ";"); + } + int year, mon, day; + if (!str_to_number (year, strtok (v, ".")) + || !str_to_number ( mon, strtok (NULL, ".")) + || !str_to_number ( day, strtok (NULL, "."))) + { + sys_err ("Invalid Date"); + return false; + } + + sys_log (0, "y m d %d %d %d",year, mon, day); + + holiday_map.insert (std::pair > (Date (year - 1900, mon - 1, day), lst)); + } + } + + fclose(fp); + + return true; +} + +std::list & CSpeedServerManager::GetWdayExpTableOfEmpire(BYTE empire, int wday) +{ + return Empire[empire].GetWdayExpTable(wday); +} + +std::list & CSpeedServerEmpireExp::GetWdayExpTable(int wday) +{ + return wday_exp_table[wday]; +} + +void CSpeedServerManager::SetWdayExpTableOfEmpire (BYTE empire, int wday, HME hme) +{ + Empire[empire].SetWdayExpTable (wday, hme); +} + +void CSpeedServerEmpireExp::SetWdayExpTable (int wday, HME hme) +{ + wday_exp_table[wday].push_back (hme); + WriteExpTable(); +} + +void CSpeedServerManager::InitWdayExpTableOfEmpire (BYTE empire, int wday) +{ + if (empire > EMPIRE_MAX_NUM) + { + sys_err ("invalid empire"); + return; + } + + Empire[empire].InitWdayExpTable (wday); +} + +void CSpeedServerEmpireExp::InitWdayExpTable(int wday) +{ + wday_exp_table[wday].clear(); +} + + +std::list & CSpeedServerManager::GetHolidayExpTableOfEmpire(BYTE empire, Date date, bool &is_exist) +{ + return Empire[empire].GetHolidayExpTable(date, is_exist); +} + +std::list & CSpeedServerEmpireExp::GetHolidayExpTable(Date date, bool &is_exist) +{ + std::map >::iterator it = holiday_map.find(date); + if (it != holiday_map.end()) + { + is_exist = true; + return it->second; + } + else + { + is_exist = false; + sys_err ("Cannot find Holiday %d %d %d",date.year, date.mon, date.day); + } + return it->second; +} + +void CSpeedServerManager::SetHolidayExpTableOfEmpire (BYTE empire, Date date, HME hme) +{ + Empire[empire].SetHolidayExpTable (date, hme); +} + +void CSpeedServerEmpireExp::SetHolidayExpTable (Date date, HME hme) +{ + std::map >::iterator it = holiday_map.find(date); + if (it != holiday_map.end()) + { + it->second.push_back (hme); + } + WriteExpTable(); +} + +void CSpeedServerManager::InitHolidayExpTableOfEmpire (BYTE empire, Date date) +{ + if (empire > EMPIRE_MAX_NUM) + { + sys_err ("invalid empire"); + return; + } + + Empire[empire].InitHolidayExpTable (date); +} + +void CSpeedServerEmpireExp::InitHolidayExpTable(Date date) +{ + sys_log (0, "init holiday"); + std::map >::iterator it = holiday_map.find(date); + if (it == holiday_map.end()) + { + std::list lst; + holiday_map.insert (std::pair > (date, lst)); + } + else + { + it->second.clear(); + } +} + +HME CSpeedServerManager::GetCurrentExpPrivOfEmpire (BYTE empire, int &duration, bool &is_change) +{ + return Empire[empire].GetCurrentExpPriv (duration, is_change); +} + +HME CSpeedServerEmpireExp::GetCurrentExpPriv(int &duration, bool &is_change) +{ + struct tm* datetime; + time_t t; + t = time(NULL); + datetime = localtime(&t); + + Date date (datetime -> tm_year, datetime -> tm_mon, + datetime -> tm_mday); + + std::map >::iterator holi_it = holiday_map.find(date); + + int total_sec = datetime->tm_hour * 3600 + datetime->tm_min * 60 + datetime->tm_sec; + + HME hme; + + // ¥ holiday̸ holiday bonus Ѵ. + if (holi_it != holiday_map.end()) + { + for (std::list ::iterator it = holi_it->second.begin(); + it != wday_exp_table[datetime->tm_wday].end(); it++) + { + // ð ð ȿ ԵǸ, + if (total_sec < (it->hour * 3600 + it->min * 60 )) + { + hme = *it; + break; + } + } + } + else + { + for (std::list ::iterator it = wday_exp_table[datetime->tm_wday].begin(); + it != wday_exp_table[datetime->tm_wday].end(); it++) + { + // ð ð ȿ ԵǸ, + if (total_sec < (it->hour * 3600 + it->min * 60 )) + { + hme = *it; + break; + } + } + } + + duration = hme.hour * 3600 + hme.min * 60 - total_sec; + + is_change = !(hme == current_hme); + current_hme = hme; + + return hme; + +} diff --git a/game/src/SpeedServer.h b/game/src/SpeedServer.h new file mode 100644 index 0000000..69c4bda --- /dev/null +++ b/game/src/SpeedServer.h @@ -0,0 +1,129 @@ +#ifndef __INC_METIN_II_GAME_SPEEDSERVER_H__ +#define __INC_METIN_II_GAME_SPEEDSERVER_H__ + +#include "../../common/length.h" +#include + +// castle.cpp ִ Ͽ +#define EMPIRE_NONE 0 // ƹ ƴ +#define EMPIRE_RED 1 // ż +#define EMPIRE_YELLOW 2 // õ +#define EMPIRE_BLUE 3 // + +class HME +{ + public : + int hour; + int min; + int exp; + + HME (int h=0, int m=0, int e=0){ + hour = h; min = m; + exp = e; + } + + HME& operator=(const HME &rhs) + { + hour = rhs.hour; + min = rhs.min; + exp = rhs.exp; + return *this; + } + + bool operator==(const HME &rhs) const + { + return hour == rhs.hour + && min == rhs.min + && exp == rhs.exp; + } + + bool operator<(const HME &rhs) const + { + return (hour& GetWdayExpTable(int wday); + void SetWdayExpTable(int wday, HME hme); + + std::list & GetHolidayExpTable(Date date, bool &is_exist); + void SetHolidayExpTable(Date date, HME hme); + + void InitWdayExpTable(int wday); + void InitHolidayExpTable(Date date); + HME GetCurrentExpPriv (int &duration, bool &is_change); + + bool WriteExpTable(); + + private : + bool LoadExpTable (); + bool LoadWdayExpTable (int wday, char *str); + + BYTE empire; + char file_name [256]; + HME current_hme; + std::map > holiday_map; + std::list wday_exp_table[7]; +}; + +class CSpeedServerManager : public singleton +{ + public: + CSpeedServerManager(); + ~CSpeedServerManager(); + + bool Initialize (); + + std::list & GetWdayExpTableOfEmpire (BYTE empire, int wday); + void SetWdayExpTableOfEmpire (BYTE empire, int wday, HME hme); + void InitWdayExpTableOfEmpire (BYTE empire, int wday); + + std::list & GetHolidayExpTableOfEmpire (BYTE empire, Date date, bool &is_exist); + void SetHolidayExpTableOfEmpire (BYTE empire, Date date, HME hme); + void InitHolidayExpTableOfEmpire (BYTE empire, Date date); + + bool WriteExpTableOfEmpire (BYTE empire); + + HME GetCurrentExpPrivOfEmpire (BYTE empire, int &duration, bool &is_change); + + private: + CSpeedServerEmpireExp Empire[EMPIRE_MAX_NUM]; + +}; + +#endif diff --git a/game/src/TrafficProfiler.cpp b/game/src/TrafficProfiler.cpp new file mode 100644 index 0000000..aa73ee1 --- /dev/null +++ b/game/src/TrafficProfiler.cpp @@ -0,0 +1,92 @@ +/** + * + * @file TrafficProfiler.cpp + * @brief TrafficProfiler class implementation file + * @author Bang2ni + * @version 05/07/07 Bang2ni - First release. + * + */ + +#include "stdafx.h" +#include "TrafficProfiler.h" + +TrafficProfiler::TrafficProfiler() + : m_pfProfileLogFile(NULL), m_dwFlushCycle(0), m_tmProfileStartTime(0), m_dwTotalTraffic(0), m_dwTotalPacket(0) +{ + m_aTrafficVec[ 0 ].resize( 256 ); + m_aTrafficVec[ 1 ].resize( 256 ); +} + +TrafficProfiler::~TrafficProfiler() +{ + if ( m_pfProfileLogFile ) + fclose( m_pfProfileLogFile ); +} + +bool TrafficProfiler::Initialize( DWORD dwFlushCycle, const char* pszFileName ) +{ + m_pfProfileLogFile = fopen( pszFileName, "w" ); + if ( !m_pfProfileLogFile ) + return false; + + m_dwFlushCycle = dwFlushCycle; + InitializeProfiling(); + + return true; +} + +bool TrafficProfiler::Flush() +{ + if ( !m_pfProfileLogFile ) + return false; + + // + // Profling result write to file + // + + fprintf( m_pfProfileLogFile, "# Profile Start: %s", ctime( &m_tmProfileStartTime ) ); + fprintf( m_pfProfileLogFile, "Total traffic: %u bytes\n", m_dwTotalTraffic ); + fprintf( m_pfProfileLogFile, "Total used packet: %u\n", m_dwTotalPacket ); + + fprintf( m_pfProfileLogFile, "------------------ Input ------------------\n" ); + + for ( int idx = 0; idx < (int)IODIR_MAX; idx++ ) + { + fprintf( m_pfProfileLogFile, "Packet\tCount\tTotal Size\tAverage\n" ); + + BYTE byHeader = 0; + for ( TrafficVec::iterator it = m_aTrafficVec[ idx ].begin(); it != m_aTrafficVec[ idx ].end(); ++it, byHeader++ ) + { + if ( it->second ) + fprintf( m_pfProfileLogFile, "%d\t%u\t%u\t\t%u\n", byHeader, it->second, it->first, it->first / it->second ); + } + + fprintf( m_pfProfileLogFile, "------------------ Output -----------------\n" ); + } + + time_t cur = time( NULL ); + fprintf( m_pfProfileLogFile, "# Profile End(Flush): %s", ctime( &cur ) ); + fflush( m_pfProfileLogFile ); + + // + // Initialization + // + + InitializeProfiling(); + + return true; +} + +void TrafficProfiler::InitializeProfiling() +{ + m_tmProfileStartTime = time( NULL ); + m_dwTotalPacket = 0; + m_dwTotalTraffic = 0; + + TrafficInfo empty( 0, 0 ); + for ( int idx = 0; idx < (int)IODIR_MAX; idx++ ) + { + for ( TrafficVec::iterator it = m_aTrafficVec[ idx ].begin(); it != m_aTrafficVec[ idx ].end(); ++it ) + *it = empty; + } +} diff --git a/game/src/TrafficProfiler.h b/game/src/TrafficProfiler.h new file mode 100644 index 0000000..50aaf20 --- /dev/null +++ b/game/src/TrafficProfiler.h @@ -0,0 +1,115 @@ +/** + * + * @file TrafficProfiler.h + * @brief TrafficProfiler class definition file + * @author Bang2ni + * @version 05/07/07 Bang2ni - First release. + * + */ + +#ifndef _METIN_II_TRAFFICPROFILER_H_ +#define _METIN_II_TRAFFICPROFILER_H_ + +/** + * @class TrafficProfiler + * @brief Network I/O traffic Ŷ ϴ profiler. + * @author Bang2ni + * @version 05/07/07 Bang2ni - First release. + * + * ð Network I/O traffic Ŷ ϰ, Text file · ۼѴ. + */ +class TrafficProfiler : public singleton< TrafficProfiler > +{ + public: + + /// I/O + enum IODirection { + IODIR_INPUT = 0, ///< Input + IODIR_OUTPUT, ///< Output + IODIR_MAX + }; + + public: + + /// Constructor + TrafficProfiler( void ); + + /// Destructor + ~TrafficProfiler( void ); + + /// Profiling ʿ ʱȭ Ѵ. + /** + * @param [in] dwFlushCycle Flush ֱ. ̴. + * @param [in] pszLogFileName Profiling log file ̸ + * @return false profiling log file open ߴ. + * + * profiling log file open() Ѵ. + */ + bool Initialize( DWORD dwFlushCycle, const char* pszLogFileName ); + + /// Profiling ۵ưų Packet Report Ѵ. + /** + * @param [in] dir Profiling Packet + * @param [in] byHeader Packet + * @param [in] dwSize Packet size + * @return Initialize ʾҴٸ false ȯѴ. + * + * Packet شϴ size Ų. + * Initialize ij ֱ Flush Ŀ Flush ֱ ŭ ð 帥 ȣȴٸ Report Flush Ѵ. + */ + bool Report( IODirection dir, BYTE byHeader, DWORD dwSize ) + { + ComputeTraffic( dir, byHeader, dwSize ); + if ( (DWORD)(time( NULL ) - m_tmProfileStartTime) >= m_dwFlushCycle ) + return Flush(); + return true; + } + + /// Report Ͽ . + /** + * @return Initialize ʾҴ. + */ + bool Flush( void ); + + private: + + /// Profling õ variables ʱȭ Ѵ. + void InitializeProfiling( void ); + + /// Report Packet traffic Ѵ. + /** + * @param [in] dir Profiling Packet + * @param [in] byHeader Packet + * @param [in] dwSize Packet size + */ + void ComputeTraffic( IODirection dir, BYTE byHeader, DWORD dwSize ) + { + + TrafficInfo& rTrafficInfo = m_aTrafficVec[ dir ][ byHeader ]; + + m_dwTotalTraffic += dwSize; + m_dwTotalPacket += !rTrafficInfo.second; + + rTrafficInfo.first += dwSize; + rTrafficInfo.second++; + } + + /// Traffic info type. + /** + * first: size + * second: packet ۵ Ƚ + */ + typedef std::pair< DWORD, DWORD > TrafficInfo; + + /// Traffic info vector. + typedef std::vector< TrafficInfo > TrafficVec; + + FILE* m_pfProfileLogFile; ///< Profile log file pointer + DWORD m_dwFlushCycle; ///< Flush ֱ + time_t m_tmProfileStartTime; ///< ð. Flush Update ȴ. + DWORD m_dwTotalTraffic; ///< Report Traffic 뷮 + DWORD m_dwTotalPacket; ///< Report Packet + TrafficVec m_aTrafficVec[ IODIR_MAX ]; ///< Report Traffic vector 迭. ⸶ vector . +}; + +#endif // _METIN_II_TRAFFICPROFILER_H_ diff --git a/game/src/XTrapManager.cpp b/game/src/XTrapManager.cpp new file mode 100644 index 0000000..bf4a92c --- /dev/null +++ b/game/src/XTrapManager.cpp @@ -0,0 +1,329 @@ +#include "stdafx.h" + +#include +#include + +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) +#include +#include +#include +#else +#include +#include +#endif + +#include + +#include "char.h" +#include "config.h" +#include "event.h" +#include "log.h" +#include "desc.h" +#include "packet.h" +#include "XTrapManager.h" + +#define CSFILE_NUM 2 +#define XTRAP_CS1_CHECK_CYCLE PASSES_PER_SEC(20) // per 20sec + +unsigned char g_XTrap_ClientMap[CSFILE_NUM][XTRAP_CS4_BUFSIZE_MAP]; + + +struct CXTrapManager::sXTrapContext +{ + //API function pointers + PFN_XTrap_S_Start XTrap_S_Start; + PFN_XTrap_S_SessionInit XTrap_S_SessionInit; + PFN_XTrap_CS_Step1 XTrap_CS_Step1; + PFN_XTrap_CS_Step3 XTrap_CS_Step3; + + PFN_XTrap_S_SetActiveCode XTrap_S_SetActiveCode; + PFN_XTrap_S_SetOption XTrap_S_SetOption; + PFN_XTrap_S_SetAllowDelay XTrap_S_SetAllowDelay; + PFN_XTrap_S_SendGamePacket XTrap_S_SendGamePacket; + PFN_XTrap_S_RecvGamePacket XTrap_S_RecvGamePacket; + + //handle + void* hXTrap4Server; +}; + + +CXTrapManager::CXTrapManager() +{ + m_pImpl = M2_NEW sXTrapContext; + memset( m_pImpl, 0x00, sizeof(sXTrapContext) ); +} + +CXTrapManager::~CXTrapManager() +{ +#ifdef __FreeBSD__ + if (m_pImpl->hXTrap4Server) + { + dlclose(m_pImpl->hXTrap4Server); + } +#endif + + M2_DELETE(m_pImpl); +} + +#ifdef __FreeBSD__ +void CXTrapManager::MapReloadSignalHandler( int signal ) +{ + for(int i=0; ihXTrap4Server = dlopen(sDllBinFile, RTLD_LAZY); + + if (m_pImpl->hXTrap4Server == 0) + { + sys_err("XTrap-failed to load so reason:%s", dlerror()) ; + return false; + } + + void* hXTrapHandle = m_pImpl->hXTrap4Server; + + m_pImpl->XTrap_S_Start = (PFN_XTrap_S_Start) dlsym(hXTrapHandle, "XTrap_S_Start"); + m_pImpl->XTrap_S_SessionInit = (PFN_XTrap_S_SessionInit) dlsym(hXTrapHandle, "XTrap_S_SessionInit"); + m_pImpl->XTrap_CS_Step1 = (PFN_XTrap_CS_Step1) dlsym(hXTrapHandle, "XTrap_CS_Step1"); + m_pImpl->XTrap_CS_Step3 = (PFN_XTrap_CS_Step3) dlsym(hXTrapHandle, "XTrap_CS_Step3"); + m_pImpl->XTrap_S_SetActiveCode = (PFN_XTrap_S_SetActiveCode) dlsym(hXTrapHandle, "XTrap_S_SetActiveCode"); + m_pImpl->XTrap_S_SetOption = (PFN_XTrap_S_SetOption) dlsym(hXTrapHandle, "XTrap_S_SetOption"); + m_pImpl->XTrap_S_SetAllowDelay = (PFN_XTrap_S_SetAllowDelay) dlsym(hXTrapHandle, "XTrap_S_SetAllowDelay"); + m_pImpl->XTrap_S_SendGamePacket = (PFN_XTrap_S_SendGamePacket) dlsym(hXTrapHandle, "XTrap_S_SendGamePacket"); + m_pImpl->XTrap_S_RecvGamePacket = (PFN_XTrap_S_RecvGamePacket) dlsym(hXTrapHandle, "XTrap_S_RecvGamePacket"); + + if (m_pImpl->XTrap_S_Start == NULL || + m_pImpl->XTrap_S_SessionInit == NULL || + m_pImpl->XTrap_CS_Step1 == NULL || + m_pImpl->XTrap_CS_Step3 == NULL || + m_pImpl->XTrap_S_SetOption == NULL || + m_pImpl->XTrap_S_SetAllowDelay == NULL || + m_pImpl->XTrap_S_SendGamePacket == NULL || + m_pImpl->XTrap_S_RecvGamePacket == NULL) + { + sys_err("XTrap-failed to load function ptrs"); + return false; + } + + //start server module + m_pImpl->XTrap_S_Start( 600, CSFILE_NUM, g_XTrap_ClientMap, NULL ); + + //NOTE : ϴ XProtect⿡ װ ־ ڵ念 üũ . + m_pImpl->XTrap_S_SetActiveCode( XTRAP_ACTIVE_CODE_THEMIDA ); + + //setup signal + signal(SIGUSR2, CXTrapManager::MapReloadSignalHandler); + +#endif + return true; +} + +bool CXTrapManager::LoadClientMapFile( unsigned int iMapIndex ) +{ +#ifdef __FreeBSD__ + //index check + if( iMapIndex >= CSFILE_NUM ) + { + return false; + } + + char szFileName[1024] = {0,}; + snprintf(szFileName, sizeof(szFileName), "map%d.CS3", iMapIndex+1); + + FILE* fi = 0; + fi = fopen(szFileName, "rb"); + if (fi == NULL) + { + return false; + } + + fread(g_XTrap_ClientMap[iMapIndex], XTRAP_CS4_BUFSIZE_MAP, 1, fi); + fclose(fi); +#endif + + return true; +} + +EVENTINFO(xtrap_cs1_check_info) +{ + DynamicCharacterPtr ptrPC; +}; + +EVENTFUNC(xtrap_cs1_check_event) +{ + xtrap_cs1_check_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( " info null pointer" ); + return 0; + } + + TPacketXTrapCSVerify pack; + pack.bHeader = HEADER_GC_XTRAP_CS1_REQUEST; + + bool bSuccess = CXTrapManager::instance().Verify_CSStep1( info->ptrPC, pack.bPacketData ); + + LPDESC lpClientDesc = info->ptrPC.Get()->GetDesc(); + if( !lpClientDesc ) + { + sys_err( " client session is invalid" ); + return 0; + } + + lpClientDesc->Packet( &pack, sizeof(pack) ); + + if( bSuccess ) + { + return XTRAP_CS1_CHECK_CYCLE; + } + + sys_err( "XTrap: hack is detected %s", lpClientDesc->GetHostName() ); + + info->ptrPC.Get()->Disconnect("XTrapCheckInvalid"); + lpClientDesc->SetPhase(PHASE_CLOSE); + + return 0; +} + +bool CXTrapManager::CreateClientSession( LPCHARACTER lpCharSession ) +{ + if( !bXTrapEnabled ) + return true; + + if( !lpCharSession ) + return false; + + DWORD dwSessionID = lpCharSession->GetPlayerID(); + + ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID ); + if( it != m_mapClientSessions.end() ) + { + sys_err("XTrap: client session is alreay registered"); + return false; + } + + //init session info + sSessionInfo infoData; + + //xtrap session init + DWORD dwReturn = m_pImpl->XTrap_S_SessionInit( 600, CSFILE_NUM, g_XTrap_ClientMap, infoData.szSessionBuf ); + if( dwReturn != 0 ) + { + sys_err("XTrap: client session init failed"); + } + + xtrap_cs1_check_info* event_info = AllocEventInfo(); + event_info->ptrPC = lpCharSession; + + infoData.m_pCheckEvent = event_create(xtrap_cs1_check_event, event_info, XTRAP_CS1_CHECK_CYCLE); + m_mapClientSessions[dwSessionID] = infoData; + + return true; +} + +void CXTrapManager::DestroyClientSession( LPCHARACTER lpCharSession ) +{ + if( !bXTrapEnabled ) + return; + + if( !lpCharSession ) + return; + + DWORD dwSessionID = lpCharSession->GetPlayerID(); + + ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID ); + if( it == m_mapClientSessions.end() ) + { + sys_err("XTrap: client session is already destroyed"); + return; + } + + event_cancel(&(it->second.m_pCheckEvent) ); + m_mapClientSessions.erase(it); +} + + +bool CXTrapManager::Verify_CSStep1( LPCHARACTER lpCharSession, BYTE* pBufData ) +{ + if( !bXTrapEnabled ) + return false; + + if( !lpCharSession ) + return false; + + DWORD dwSessionID = lpCharSession->GetPlayerID(); + + ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID ); + if( it == m_mapClientSessions.end() ) + { + sys_err("XTrap: client session is already destroyed"); + return false; + } + + int nReturn = m_pImpl->XTrap_CS_Step1( it->second.szSessionBuf, it->second.szPackBuf ); + + memcpy( pBufData, it->second.szPackBuf, VERIFY_PACK_LEN ); + + return (nReturn == 0) ? true : false; +} + +void CXTrapManager::Verify_CSStep3( LPCHARACTER lpCharSession, BYTE* pBufData ) +{ + if( !bXTrapEnabled ) + return; + + if( !lpCharSession ) + return; + + DWORD dwSessionID = lpCharSession->GetPlayerID(); + + ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID ); + if( it == m_mapClientSessions.end() ) + { + sys_log(0, "XTrap: client session is alreay destroyed"); + return; + } + + memcpy( it->second.szPackBuf, pBufData, VERIFY_PACK_LEN ); + m_pImpl->XTrap_CS_Step3( it->second.szSessionBuf, it->second.szPackBuf ); + + //if( XTRAP_API_RETURN_DETECTHACK == m_pImpl->XTrap_CS_Step3( it->second.szSessionBuf, pBufData ) ) + //{ + // sys_error(0, "XTrap: client session is alreay destroyed"); + //} +} diff --git a/game/src/XTrapManager.h b/game/src/XTrapManager.h new file mode 100644 index 0000000..3b0e2ab --- /dev/null +++ b/game/src/XTrapManager.h @@ -0,0 +1,67 @@ + +#ifndef _XTRAP_MANAGER_H_ +#define _XTRAP_MANAGER_H_ + +#include "IFileMonitor.h" + +#define SESSION_BUF_LEN 320 +#define VERIFY_PACK_LEN 128 +#define SESSION_CSSTEP1_LEN 256 + +#pragma pack(1) + +typedef struct PacketXTrapVerify +{ + BYTE bHeader; + BYTE bPacketData[VERIFY_PACK_LEN]; + +} TPacketXTrapCSVerify; + +#pragma pack() + +class CXTrapManager : public singleton +{ +public: + CXTrapManager(); + virtual ~CXTrapManager(); + + bool LoadXTrapModule(); + bool LoadClientMapFile( unsigned int iMapIndex ); + + bool CreateClientSession( LPCHARACTER lpCharSession ); + void DestroyClientSession( LPCHARACTER lpCharSession ); + + bool Verify_CSStep1( LPCHARACTER lpCharSession, BYTE* pOutBufData ); + void Verify_CSStep3( LPCHARACTER lpCharSession, BYTE* pBufData ); +#ifdef __FreeBSD__ + static void MapReloadSignalHandler( int signal ); + + static void NotifyMapFileChanged( const std::string& fileName, eFileUpdatedOptions eUpdateOption ); +#endif + +private: + //pimpl`s idiom + struct sXTrapContext; + sXTrapContext* m_pImpl; + + struct sSessionInfo + { + sSessionInfo() + { + m_pCheckEvent = NULL; + memset(szSessionBuf, 0x00, sizeof(szSessionBuf) ); + memset(szPackBuf, 0x00, sizeof(szPackBuf) ); + } + + BYTE szSessionBuf[SESSION_BUF_LEN]; + BYTE szPackBuf[VERIFY_PACK_LEN]; + LPEVENT m_pCheckEvent; + }; + + typedef boost::unordered_map ClientSessionMap; + + ClientSessionMap m_mapClientSessions; + +}; + +#endif /* _XTRAP_MANAGER_H_ */ diff --git a/game/src/affect.cpp b/game/src/affect.cpp new file mode 100644 index 0000000..cde2ac2 --- /dev/null +++ b/game/src/affect.cpp @@ -0,0 +1,31 @@ + +#include "stdafx.h" + +#ifndef DEBUG_ALLOC +#include +#endif + +#include "affect.h" + +#ifndef DEBUG_ALLOC +boost::object_pool affect_pool; +#endif + +CAffect* CAffect::Acquire() +{ +#ifndef DEBUG_ALLOC + return affect_pool.malloc(); +#else + return M2_NEW CAffect; +#endif +} + +void CAffect::Release(CAffect* p) +{ +#ifndef DEBUG_ALLOC + affect_pool.free(p); +#else + M2_DELETE(p); +#endif +} + diff --git a/game/src/affect.h b/game/src/affect.h new file mode 100644 index 0000000..756e778 --- /dev/null +++ b/game/src/affect.h @@ -0,0 +1,183 @@ +#ifndef __INC_AFFECT_H +#define __INC_AFFECT_H + +class CAffect +{ + public: + DWORD dwType; + BYTE bApplyOn; + long lApplyValue; + DWORD dwFlag; + long lDuration; + long lSPCost; + + static CAffect* Acquire(); + static void Release(CAffect* p); +}; + +enum EAffectTypes +{ + AFFECT_NONE, + + AFFECT_MOV_SPEED = 200, + AFFECT_ATT_SPEED, + AFFECT_ATT_GRADE, + AFFECT_INVISIBILITY, + AFFECT_STR, + AFFECT_DEX, // 205 + AFFECT_CON, + AFFECT_INT, + AFFECT_FISH_MIND_PILL, + + AFFECT_POISON, + AFFECT_STUN, // 210 + AFFECT_SLOW, + AFFECT_DUNGEON_READY, + AFFECT_DUNGEON_UNIQUE, + + AFFECT_BUILDING, + AFFECT_REVIVE_INVISIBLE, // 215 + AFFECT_FIRE, + AFFECT_CAST_SPEED, + AFFECT_HP_RECOVER_CONTINUE, + AFFECT_SP_RECOVER_CONTINUE, + + AFFECT_POLYMORPH, // 220 + AFFECT_MOUNT, + + AFFECT_WAR_FLAG, // 222 + + AFFECT_BLOCK_CHAT, // 223 + AFFECT_CHINA_FIREWORK, + + AFFECT_BOW_DISTANCE, // 225 + AFFECT_DEF_GRADE, // 226 + + AFFECT_PREMIUM_START = 500, + AFFECT_EXP_BONUS = 500, // + AFFECT_ITEM_BONUS = 501, // 尩 + AFFECT_SAFEBOX = 502, // PREMIUM_SAFEBOX, + AFFECT_AUTOLOOT = 503, // PREMIUM_AUTOLOOT, + AFFECT_FISH_MIND = 504, // PREMIUM_FISH_MIND, + AFFECT_MARRIAGE_FAST = 505, // + AFFECT_GOLD_BONUS = 506, // Ȯ 50% + AFFECT_PREMIUM_END = 509, + + AFFECT_MALL = 510, // Ʈ + AFFECT_NO_DEATH_PENALTY = 511, // ȣ (ġ гƼ ѹ ش) + AFFECT_SKILL_BOOK_BONUS = 512, // (å Ȯ 50% ) + AFFECT_SKILL_NO_BOOK_DELAY = 513, // ־ȼ + + AFFECT_HAIR = 514, // ȿ + AFFECT_COLLECT = 515, //Ʈ + AFFECT_EXP_BONUS_EURO_FREE = 516, // ( 14 ⺻ ȿ) + AFFECT_EXP_BONUS_EURO_FREE_UNDER_15 = 517, + AFFECT_UNIQUE_ABILITY = 518, + + AFFECT_CUBE_1, + AFFECT_CUBE_2, + AFFECT_CUBE_3, + AFFECT_CUBE_4, + AFFECT_CUBE_5, + AFFECT_CUBE_6, + AFFECT_CUBE_7, + AFFECT_CUBE_8, + AFFECT_CUBE_9, + AFFECT_CUBE_10, + AFFECT_CUBE_11, + AFFECT_CUBE_12, + + AFFECT_BLEND, + + AFFECT_HORSE_NAME, + AFFECT_MOUNT_BONUS, + + AFFECT_AUTO_HP_RECOVERY = 534, + AFFECT_AUTO_SP_RECOVERY = 535, + + AFFECT_DRAGON_SOUL_QUALIFIED = 540, + AFFECT_DRAGON_SOUL_DECK_0 = 541, + AFFECT_DRAGON_SOUL_DECK_1 = 542, + + + AFFECT_RAMADAN_ABILITY = 300, + AFFECT_RAMADAN_RING = 301, + + AFFECT_NOG_ABILITY = 302, + AFFECT_HOLLY_STONE_POWER = 303, + + AFFECT_QUEST_START_IDX = 1000 +}; + +enum EAffectBits +{ + AFF_NONE, + + AFF_YMIR, + AFF_INVISIBILITY, + AFF_SPAWN, + + AFF_POISON, + AFF_SLOW, + AFF_STUN, + + AFF_DUNGEON_READY, // غ + AFF_DUNGEON_UNIQUE, // ũ (Ŭ̾Ʈ ø) + + AFF_BUILDING_CONSTRUCTION_SMALL, + AFF_BUILDING_CONSTRUCTION_LARGE, + AFF_BUILDING_UPGRADE, + + AFF_MOV_SPEED_POTION, + AFF_ATT_SPEED_POTION, + + AFF_FISH_MIND, + + AFF_JEONGWIHON, // ȥ + AFF_GEOMGYEONG, // ˰ + AFF_CHEONGEUN, // õ + AFF_GYEONGGONG, // + AFF_EUNHYUNG, // + AFF_GWIGUM, // Ͱ + AFF_TERROR, // + AFF_JUMAGAP, // ָ + AFF_HOSIN, // ȣ + AFF_BOHO, // ȣ + AFF_KWAESOK, // + AFF_MANASHIELD, // + AFF_MUYEONG, // affect + AFF_REVIVE_INVISIBLE, // Ȱ õ + AFF_FIRE, // + AFF_GICHEON, // õ + AFF_JEUNGRYEOK, // ¼ + AFF_TANHWAN_DASH, // źȯݿ ޸Ʈ + AFF_PABEOP, // Ĺ + AFF_CHEONGEUN_WITH_FALL, // õ + AFF_POLYMORPH, + AFF_WAR_FLAG1, + AFF_WAR_FLAG2, + AFF_WAR_FLAG3, + + AFF_CHINA_FIREWORK, + AFF_HAIR, // + AFF_GERMANY, // + + AFF_BITS_MAX +}; + +extern void SendAffectAddPacket(LPDESC d, CAffect * pkAff); + +// AFFECT_DURATION_BUG_FIX +enum AffectVariable +{ + // Affect Ѵ  ־ . + // ð ̱ ſ ū Ѵ븦 ķ̼. + //// 24Ʈ Ƿ 25Ʈ . + // ... 25Ʈ Ѵٰ س 29bit ϰ ִ û ̶ּ... + // collect quest ð 60 ϰ Ƿ, ⵵ 60 . + + INFINITE_AFFECT_DURATION = 60 * 365 * 24 * 60 * 60 +}; +// END_AFFECT_DURATION_BUG_FIX + +#endif diff --git a/game/src/affect_flag.h b/game/src/affect_flag.h new file mode 100644 index 0000000..015c5f3 --- /dev/null +++ b/game/src/affect_flag.h @@ -0,0 +1,69 @@ +#ifndef __INC_METIN_II_AFFECT_FLAG_H__ +#define __INC_METIN_II_AFFECT_FLAG_H__ + +#ifndef IS_SET +#define IS_SET(flag, bit) ((flag) & (bit)) +#endif + +#ifndef SET_BIT +#define SET_BIT(var, bit) ((var) |= (bit)) +#endif + +#ifndef REMOVE_BIT +#define REMOVE_BIT(var, bit) ((var) &= ~(bit)) +#endif + +#ifndef TOGGLE_BIT +#define TOGGLE_BIT(var, bit) ((var) = (var) ^ (bit)) +#endif + +struct TAffectFlag +{ + DWORD bits[2]; + + inline TAffectFlag() { bits[0] = 0; bits[1] = 0; } + inline TAffectFlag(DWORD v1, DWORD v2 = 0) {bits[0] = v1; bits[1] = v2;} + + inline bool IsSet(int flag) const + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return false; + + return IS_SET(bits[(flag - 1) >> 5], (((DWORD)1) << ((flag - 1) & 31))); + } + + inline void Set(int flag) + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return; + + SET_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31))); + } + + inline void Reset(int flag) + { + if (AFF_BITS_MAX <= flag || 0 >= flag) + return; + + REMOVE_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31))); + } + + inline TAffectFlag& operator = (const TAffectFlag& rhs) + { + bits[0] = rhs.bits[0]; + bits[1] = rhs.bits[1]; + return *this; + } +}; + +inline bool operator == (const TAffectFlag& lhs, const TAffectFlag& rhs) +{ + return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1]; +} + +inline bool operator != (const TAffectFlag& lhs, const TAffectFlag& rhs) +{ + return !(lhs == rhs); +} + +#endif diff --git a/game/src/ani.cpp b/game/src/ani.cpp new file mode 100644 index 0000000..3f15084 --- /dev/null +++ b/game/src/ani.cpp @@ -0,0 +1,371 @@ +/********************************************************************* + * date : 2007.11.16 + * file : ani.cpp + * author : mhh + * description : + */ + +#define _ani_cpp_ + +#include "stdafx.h" +#include "char.h" +#include "item.h" +#include "ani.h" +#include "dev_log.h" + +const char* FN_race_name(int race) +{ +#define FN_NAME(race) case race: return #race + switch (race) + { + FN_NAME(MAIN_RACE_WARRIOR_M); + FN_NAME(MAIN_RACE_ASSASSIN_W); + FN_NAME(MAIN_RACE_SURA_M); + FN_NAME(MAIN_RACE_SHAMAN_W); + FN_NAME(MAIN_RACE_WARRIOR_W); + FN_NAME(MAIN_RACE_ASSASSIN_M); + FN_NAME(MAIN_RACE_SURA_W); + FN_NAME(MAIN_RACE_SHAMAN_M); + FN_NAME(MAIN_RACE_MAX_NUM); + } + + return "UNKNOWN"; +#undef FN_NAME +} + +const char* FN_weapon_type(int weapon) +{ +#define FN_NAME(weapon) case weapon: return #weapon + switch (weapon) + { + FN_NAME(WEAPON_SWORD); + FN_NAME(WEAPON_DAGGER); + FN_NAME(WEAPON_BOW); + FN_NAME(WEAPON_TWO_HANDED); + FN_NAME(WEAPON_BELL); + FN_NAME(WEAPON_FAN); + FN_NAME(WEAPON_ARROW); + FN_NAME(WEAPON_MOUNT_SPEAR); + FN_NAME(WEAPON_NUM_TYPES); + } + + return "UNKNOWN"; +#undef FN_NAME +} + +class ANI +{ + protected: + // [][Ϲ0Ż1][][޺] + DWORD m_speed[MAIN_RACE_MAX_NUM][2][WEAPON_NUM_TYPES][9]; + + public: + ANI(); + + public: + bool load(); + bool load_one_race(int race, const char *dir_name); + DWORD load_one_weapon(const char *dir_name, int weapon, BYTE combo, bool horse); + DWORD attack_speed(int race, int weapon, BYTE combo = 0, bool horse = false); + + void print_attack_speed(); +}; + +static class ANI s_ANI; + +DWORD FN_attack_speed_from_file(const char *file) +{ + FILE * fp = fopen(file, "r"); + + if (NULL == fp) + return 0; + + int speed = 1000; + + const char *key = "DirectInputTime"; + const char *delim = " \t\r\n"; + const char *field, *value; + + char buf[1024]; + + while (fgets(buf, 1024, fp)) + { + field = strtok(buf, delim); + value = strtok(NULL, delim); + + if (field && value) + { + if (0 == strcasecmp(field, key)) + { + float f_speed = strtof(value, NULL); + speed = (int) (f_speed * 1000.0); + break; + } + } + } + + fclose(fp); + return speed; +} + +ANI::ANI() +{ + // set default value + for (int race = 0; race < MAIN_RACE_MAX_NUM; ++race) + { + for (int weapon = 0; weapon < WEAPON_NUM_TYPES; ++weapon) + { + for (BYTE combo = 0; combo <= 8; ++combo) + { + m_speed[race][0][weapon][combo] = 1000; + m_speed[race][1][weapon][combo] = 1000; + } + } + } +} + +bool ANI::load() +{ + const char* dir_name[MAIN_RACE_MAX_NUM] = { + "data/pc/warrior", // () + "data/pc/assassin", // ڰ() + "data/pc/sura", // () + "data/pc/shaman", // () + "data/pc2/warrior", // () + "data/pc2/assassin", // ڰ() + "data/pc2/sura", // () + "data/pc2/shaman" // () + }; + + for (int race = 0; race GetWear(WEAR_WEAPON); + + if (NULL == item) + return speed; + + if (ITEM_WEAPON != item->GetType()) + return speed; + + int race = ch->GetRaceNum(); + int weapon = item->GetSubType(); + + /* + dev_log(LOG_DEB0, "%s : (race,weapon) = (%s,%s) POINT_ATT_SPEED = %d", + ch->GetName(), + FN_race_name(race), + FN_weapon_type(weapon), + ch->GetPoint(POINT_ATT_SPEED)); + */ + + /* ڵ ҵ ↓ݰ ¸ */ + /* Ѽհ ӵ */ + if (weapon == WEAPON_TWO_HANDED) + weapon = WEAPON_SWORD; + + return s_ANI.attack_speed(race, weapon); +} + +DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo) +{ + LPITEM item = ch->GetWear(WEAR_WEAPON); + + if (NULL == item || combo > 8) + return 1000; + + return s_ANI.attack_speed(ch->GetRaceNum(), item->GetSubType(), combo, ch->IsRiding()); +} + +void ani_print_attack_speed() +{ + s_ANI.print_attack_speed(); +} + +#if 0 +int main(int argc, char **argv) +{ + ani_init(); + ani_print_attack_speed(); + exit(0); +} +#endif diff --git a/game/src/ani.h b/game/src/ani.h new file mode 100644 index 0000000..54da182 --- /dev/null +++ b/game/src/ani.h @@ -0,0 +1,19 @@ +/********************************************************************* + * date : 2007.11.16 + * file : ani.h + * author : mhh + * description : + */ + +#ifndef _ani_h_ +#define _ani_h_ + + +void ani_init(); +DWORD ani_attack_speed(LPCHARACTER ch); +void ani_print_attack_speed(); +DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo); + +#endif /* _ani_h_ */ + + diff --git a/game/src/any_function.h b/game/src/any_function.h new file mode 100644 index 0000000..9f76b07 --- /dev/null +++ b/game/src/any_function.h @@ -0,0 +1,67 @@ +// See http://www.boost.org/libs/any for Documentation. + +#ifndef __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED +#define __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED + +// what: variant type boost::any +// who: contributed by Kevlin Henney, +// with features contributed and bugs found by +// Ed Brey, Mark Rodgers, Peter Dimov, and James Curran +// when: July 2001 +// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 + +#include +#include + +#define boost _boost_func_of_SQLMsg +#define func_arg_type SQLMsg* +#define func_arg pmsg +#include "any_function.inc" +#undef func_arg +#undef func_arg_type +#undef boost + +typedef _boost_func_of_SQLMsg::any any_function; + +#define boost _boost_func_of_void +#define func_arg_type +#define func_arg +#include "any_function.inc" +#undef func_arg +#undef func_arg_type +#undef boost + +typedef _boost_func_of_void::any any_void_function; + +template +class void_binder +{ + protected: + F f; + typename F::argument_type value; + public: + void_binder(const F& f, const typename F::argument_type x) + : f(f), value(x) {} + void operator()() const { + return f(value); + } +}; + + template +inline void_binder void_bind(const F& f, const Arg& arg) +{ + typedef typename F::argument_type arg_type; + return void_binder(f, arg_type(arg)); +} + +// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. +// +// Permission to use, copy, modify, and distribute this software for any +// purpose is hereby granted without fee, provided that this copyright and +// permissions notice appear in all copies and derivatives. +// +// This software is provided "as is" without express or implied warranty. + +#endif + + diff --git a/game/src/any_function.inc b/game/src/any_function.inc new file mode 100644 index 0000000..1658490 --- /dev/null +++ b/game/src/any_function.inc @@ -0,0 +1,116 @@ +namespace boost +{ + class any + { + public: // structors + + any() + : content(0) + { + } + + template + any(const ValueType & value) + : content(new holder(value)) + { + } + + any(const any & other) + : content(other.content ? other.content->clone() : 0) + { + } + + ~any() + { + delete content; + } + + public: // modifiers + + any & swap(any & rhs) + { + std::swap(content, rhs.content); + return *this; + } + + template + any & operator=(const ValueType & rhs) + { + any(rhs).swap(*this); + return *this; + } + + any & operator=(const any & rhs) + { + any(rhs).swap(*this); + return *this; + } + + void operator ()(func_arg_type func_arg) + { + (*content)(func_arg); + } + + public: // queries + + bool empty() const + { + return !content; + } + + private: // types + + class placeholder + { + public: // structors + + virtual ~placeholder() + { + } + + public: // queries + + virtual placeholder * clone() const = 0; + + virtual void operator()(func_arg_type func_arg) = 0; + + }; + + template + class holder : public placeholder + { + public: // structors + + holder(const ValueType & value) + : held(value) + { + } + + public: // queries + + virtual placeholder * clone() const + { + return new holder(held); + } + + virtual void operator ()(func_arg_type func_arg) + { + held(func_arg); + } + + public: // representation + + ValueType held; + + }; + + private: // representation + + template + friend ValueType * any_cast(any *); + + placeholder * content; + + }; + +} diff --git a/game/src/arena.cpp b/game/src/arena.cpp new file mode 100644 index 0000000..de2d8b1 --- /dev/null +++ b/game/src/arena.cpp @@ -0,0 +1,1138 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "packet.h" +#include "desc.h" +#include "buffer_manager.h" +#include "start_position.h" +#include "questmanager.h" +#include "char.h" +#include "char_manager.h" +#include "arena.h" + +CArena::CArena(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + m_StartPointA.x = startA_X; + m_StartPointA.y = startA_Y; + m_StartPointA.z = 0; + + m_StartPointB.x = startB_X; + m_StartPointB.y = startB_Y; + m_StartPointB.z = 0; + + m_ObserverPoint.x = (startA_X + startB_X) / 2; + m_ObserverPoint.y = (startA_Y + startB_Y) / 2; + m_ObserverPoint.z = 0; + + m_pEvent = NULL; + m_pTimeOutEvent = NULL; + + Clear(); +} + +void CArena::Clear() +{ + m_dwPIDA = 0; + m_dwPIDB = 0; + + if (m_pEvent != NULL) + { + event_cancel(&m_pEvent); + } + + if (m_pTimeOutEvent != NULL) + { + event_cancel(&m_pTimeOutEvent); + } + + m_dwSetCount = 0; + m_dwSetPointOfA = 0; + m_dwSetPointOfB = 0; +} + +bool CArenaManager::AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + CArenaMap *pArenaMap = NULL; + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) + { + pArenaMap = M2_NEW CArenaMap; + m_mapArenaMap.insert(std::make_pair(mapIdx, pArenaMap)); + } + else + { + pArenaMap = iter->second; + } + + if (pArenaMap->AddArena(mapIdx, startA_X, startA_Y, startB_X, startB_Y) == false) + { + sys_log(0, "CArenaManager::AddArena - AddMap Error MapID: %d", mapIdx); + return false; + } + + return true; +} + +bool CArenaMap::AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + if ((CArena*)(*iter)->CheckArea(startA_X, startA_Y, startB_X, startB_Y) == false) + { + sys_log(0, "CArenaMap::AddArena - Same Start Position set. stA(%d, %d) stB(%d, %d)", startA_X, startA_Y, startB_X, startB_Y); + return false; + } + } + + m_dwMapIndex = mapIdx; + + CArena *pArena = M2_NEW CArena(startA_X, startA_Y, startB_X, startB_Y); + m_listArena.push_back(pArena); + + return true; +} + +void CArenaManager::Destroy() +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + pArenaMap->Destroy(); + + M2_DELETE(pArenaMap); + } + m_mapArenaMap.clear(); +} + +void CArenaMap::Destroy() +{ + itertype(m_listArena) iter = m_listArena.begin(); + + sys_log(0, "ARENA: ArenaMap will be destroy. mapIndex(%d)", m_dwMapIndex); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + pArena->EndDuel(); + + M2_DELETE(pArena); + } + m_listArena.clear(); +} + +bool CArena::CheckArea(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y) +{ + if (m_StartPointA.x == startA_X && m_StartPointA.y == startA_Y && + m_StartPointB.x == startB_X && m_StartPointB.y == startB_Y) + return false; + return true; +} + +void CArenaManager::SendArenaMapListTo(LPCHARACTER pChar) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArena = iter->second; + pArena->SendArenaMapListTo(pChar, (iter->first)); + } +} + +void CArenaMap::SendArenaMapListTo(LPCHARACTER pChar, DWORD mapIdx) +{ + if (pChar == NULL) return; + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + pChar->ChatPacket(CHAT_TYPE_INFO, "ArenaMapInfo Map: %d stA(%d, %d) stB(%d, %d)", mapIdx, + (CArena*)(*iter)->GetStartPointA().x, (CArena*)(*iter)->GetStartPointA().y, + (CArena*)(*iter)->GetStartPointB().x, (CArena*)(*iter)->GetStartPointB().y); + } +} + +bool CArenaManager::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + if (pCharFrom == NULL || pCharTo == NULL) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap->StartDuel(pCharFrom, pCharTo, nSetPoint, nMinute) == true) + { + return true; + } + } + + return false; +} + +bool CArenaMap::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->IsEmpty() == true) + { + return pArena->StartDuel(pCharFrom, pCharTo, nSetPoint, nMinute); + } + } + + return false; +} + +EVENTINFO(TArenaEventInfo) +{ + CArena *pArena; + BYTE state; + + TArenaEventInfo() + : pArena(0) + , state(0) + { + } +}; + +EVENTFUNC(ready_to_start_event) +{ + if (event == NULL) + return 0; + + if (event->info == NULL) + return 0; + + TArenaEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "ready_to_start_event> Null pointer" ); + return 0; + } + + CArena* pArena = info->pArena; + + if (pArena == NULL) + { + sys_err("ARENA: Arena start event info is null."); + return 0; + } + + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA == NULL || chB == NULL) + { + sys_err("ARENA: Player err in event func ready_start_event"); + + if (chA != NULL) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + } + + if (chB != NULL) + { + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerBPID(), pArena->GetPlayerAPID()); + } + + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" 밡 մϴ.")); + + pArena->EndDuel(); + return 0; + } + + switch (info->state) + { + case 0: + { + chA->SetArena(pArena); + chB->SetArena(pArena); + + int count = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count"); + + if (count > 10000) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + else + { + chA->SetPotionLimit(count); + chB->SetPotionLimit(count); + + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d մϴ."), chA->GetPotionLimit()); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d մϴ."), chB->GetPotionLimit()); + } + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ ۵˴ϴ.")); + + info->state++; + return PASSES_PER_SEC(10); + } + break; + + case 1: + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart) + 4; + + DWORD dwOppList[8]; // ִ Ƽ 8 ̹Ƿ.. + + dwOppList[0] = (DWORD)chB->GetVID(); + TEMP_BUFFER buf; + + buf.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf.write(&dwOppList[0], 4); + chA->GetDesc()->Packet(buf.read_peek(), buf.size()); + + + dwOppList[0] = (DWORD)chA->GetVID(); + TEMP_BUFFER buf2; + + buf2.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf2.write(&dwOppList[0], 4); + chB->GetDesc()->Packet(buf2.read_peek(), buf2.size()); + + return 0; + } + break; + + case 2: + { + pArena->EndDuel(); + return 0; + } + break; + + case 3: + { + chA->Show(chA->GetMapIndex(), pArena->GetStartPointA().x * 100, pArena->GetStartPointA().y * 100); + chB->Show(chB->GetMapIndex(), pArena->GetStartPointB().x * 100, pArena->GetStartPointB().y * 100); + + chA->GetDesc()->SetPhase(PHASE_GAME); + chA->StartRecoveryEvent(); + chA->SetPosition(POS_STANDING); + chA->PointChange(POINT_HP, chA->GetMaxHP() - chA->GetHP()); + chA->PointChange(POINT_SP, chA->GetMaxSP() - chA->GetSP()); + chA->ViewReencode(); + + chB->GetDesc()->SetPhase(PHASE_GAME); + chB->StartRecoveryEvent(); + chB->SetPosition(POS_STANDING); + chB->PointChange(POINT_HP, chB->GetMaxHP() - chB->GetHP()); + chB->PointChange(POINT_SP, chB->GetMaxSP() - chB->GetSP()); + chB->ViewReencode(); + + TEMP_BUFFER buf; + TEMP_BUFFER buf2; + DWORD dwOppList[8]; // ִ Ƽ 8 ̹Ƿ.. + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart) + 4; + + dwOppList[0] = (DWORD)chB->GetVID(); + buf.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf.write(&dwOppList[0], 4); + chA->GetDesc()->Packet(buf.read_peek(), buf.size()); + + dwOppList[0] = (DWORD)chA->GetVID(); + buf2.write(&duelStart, sizeof(TPacketGCDuelStart)); + buf2.write(&dwOppList[0], 4); + chB->GetDesc()->Packet(buf2.read_peek(), buf2.size()); + + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" ۵Ǿϴ.")); + + pArena->ClearEvent(); + + return 0; + } + break; + + default: + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" Ͽ մϴ.")); + + sys_log(0, "ARENA: Something wrong in event func. info->state(%d)", info->state); + + pArena->EndDuel(); + + return 0; + } + } +} + +EVENTFUNC(duel_time_out) +{ + if (event == NULL) return 0; + if (event->info == NULL) return 0; + + TArenaEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "duel_time_out> Null pointer" ); + return 0; + } + + CArena* pArena = info->pArena; + + if (pArena == NULL) + { + sys_err("ARENA: Time out event error"); + return 0; + } + + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA == NULL || chB == NULL) + { + if (chA != NULL) + { + chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + } + + if (chB != NULL) + { + chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + sys_log(0, "ARENA: Oppernent is disappered. MyPID(%d) OppPID(%d)", pArena->GetPlayerBPID(), pArena->GetPlayerAPID()); + } + + pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT(" 밡 մϴ.")); + + pArena->EndDuel(); + return 0; + } + else + { + switch (info->state) + { + case 0: + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ð ʰ ߴմϴ.")); + chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10ʵ ̵մϴ.")); + + TPacketGCDuelStart duelStart; + duelStart.header = HEADER_GC_DUEL_START; + duelStart.wSize = sizeof(TPacketGCDuelStart); + + chA->GetDesc()->Packet(&duelStart, sizeof(TPacketGCDuelStart)); + chA->GetDesc()->Packet(&duelStart, sizeof(TPacketGCDuelStart)); + + info->state++; + + sys_log(0, "ARENA: Because of time over, duel is end. PIDA(%d) vs PIDB(%d)", pArena->GetPlayerAPID(), pArena->GetPlayerBPID()); + + return PASSES_PER_SEC(10); + break; + + case 1: + pArena->EndDuel(); + break; + } + } + + return 0; +} + +bool CArena::StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute) +{ + this->m_dwPIDA = pCharFrom->GetPlayerID(); + this->m_dwPIDB = pCharTo->GetPlayerID(); + this->m_dwSetCount = nSetPoint; + + pCharFrom->WarpSet(GetStartPointA().x * 100, GetStartPointA().y * 100); + pCharTo->WarpSet(GetStartPointB().x * 100, GetStartPointB().y * 100); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 0; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + + if (m_pTimeOutEvent != NULL) { + event_cancel(&m_pTimeOutEvent); + } + + info = AllocEventInfo(); + + info->pArena = this; + info->state = 0; + + m_pTimeOutEvent = event_create(duel_time_out, info, PASSES_PER_SEC(nMinute*60)); + + pCharFrom->PointChange(POINT_HP, pCharFrom->GetMaxHP() - pCharFrom->GetHP()); + pCharFrom->PointChange(POINT_SP, pCharFrom->GetMaxSP() - pCharFrom->GetSP()); + + pCharTo->PointChange(POINT_HP, pCharTo->GetMaxHP() - pCharTo->GetHP()); + pCharTo->PointChange(POINT_SP, pCharTo->GetMaxSP() - pCharTo->GetSP()); + + sys_log(0, "ARENA: Start Duel with PID_A(%d) vs PID_B(%d)", GetPlayerAPID(), GetPlayerBPID()); + return true; +} + +void CArenaManager::EndAllDuel() +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap *pArenaMap = iter->second; + if (pArenaMap != NULL) + pArenaMap->EndAllDuel(); + } + + return; +} + +void CArenaMap::EndAllDuel() +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena *pArena = *iter; + if (pArena != NULL) + pArena->EndDuel(); + } +} + +void CArena::EndDuel() +{ + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + if (m_pTimeOutEvent != NULL) { + event_cancel(&m_pTimeOutEvent); + } + + LPCHARACTER playerA = GetPlayerA(); + LPCHARACTER playerB = GetPlayerB(); + + if (playerA != NULL) + { + playerA->SetPKMode(PK_MODE_PEACE); + playerA->StartRecoveryEvent(); + playerA->SetPosition(POS_STANDING); + playerA->PointChange(POINT_HP, playerA->GetMaxHP() - playerA->GetHP()); + playerA->PointChange(POINT_SP, playerA->GetMaxSP() - playerA->GetSP()); + + playerA->SetArena(NULL); + + playerA->WarpSet(ARENA_RETURN_POINT_X(playerA->GetEmpire()), ARENA_RETURN_POINT_Y(playerA->GetEmpire())); + } + + if (playerB != NULL) + { + playerB->SetPKMode(PK_MODE_PEACE); + playerB->StartRecoveryEvent(); + playerB->SetPosition(POS_STANDING); + playerB->PointChange(POINT_HP, playerB->GetMaxHP() - playerB->GetHP()); + playerB->PointChange(POINT_SP, playerB->GetMaxSP() - playerB->GetSP()); + + playerB->SetArena(NULL); + + playerB->WarpSet(ARENA_RETURN_POINT_X(playerB->GetEmpire()), ARENA_RETURN_POINT_Y(playerB->GetEmpire())); + } + + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = CHARACTER_MANAGER::instance().FindByPID(iter->first); + if (pChar != NULL) + { + pChar->WarpSet(ARENA_RETURN_POINT_X(pChar->GetEmpire()), ARENA_RETURN_POINT_Y(pChar->GetEmpire())); + } + } + + m_mapObserver.clear(); + + sys_log(0, "ARENA: End Duel PID_A(%d) vs PID_B(%d)", GetPlayerAPID(), GetPlayerBPID()); + + Clear(); +} + +void CArenaManager::GetDuelList(lua_State* L) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + int index = 1; + lua_newtable(L); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap != NULL) + index = pArenaMap->GetDuelList(L, index); + } +} + +int CArenaMap::GetDuelList(lua_State* L, int index) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena == NULL) continue; + + if (pArena->IsEmpty() == false) + { + LPCHARACTER chA = pArena->GetPlayerA(); + LPCHARACTER chB = pArena->GetPlayerB(); + + if (chA != NULL && chB != NULL) + { + lua_newtable(L); + + lua_pushstring(L, chA->GetName()); + lua_rawseti(L, -2, 1); + + lua_pushstring(L, chB->GetName()); + lua_rawseti(L, -2, 2); + + lua_pushnumber(L, m_dwMapIndex); + lua_rawseti(L, -2, 3); + + lua_pushnumber(L, pArena->GetObserverPoint().x); + lua_rawseti(L, -2, 4); + + lua_pushnumber(L, pArena->GetObserverPoint().y); + lua_rawseti(L, -2, 5); + + lua_rawseti(L, -2, index++); + } + } + } + + return index; +} + +bool CArenaManager::CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim) +{ + if (pCharAttacker == NULL || pCharVictim == NULL) return false; + + if (pCharAttacker == pCharVictim) return false; + + long mapIndex = pCharAttacker->GetMapIndex(); + if (mapIndex != pCharVictim->GetMapIndex()) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIndex); + + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = (CArenaMap*)(iter->second); + return pArenaMap->CanAttack(pCharAttacker, pCharVictim); +} + +bool CArenaMap::CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim) +{ + if (pCharAttacker == NULL || pCharVictim == NULL) return false; + + DWORD dwPIDA = pCharAttacker->GetPlayerID(); + DWORD dwPIDB = pCharVictim->GetPlayerID(); + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->CanAttack(dwPIDA, dwPIDB) == true) + { + return true; + } + } + return false; +} + +bool CArena::CanAttack(DWORD dwPIDA, DWORD dwPIDB) +{ + // 1:1 ٴ ʿ + if (m_dwPIDA == dwPIDA && m_dwPIDB == dwPIDB) return true; + if (m_dwPIDA == dwPIDB && m_dwPIDB == dwPIDA) return true; + + return false; +} + +bool CArenaManager::OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim) +{ + if (pCharKiller == NULL || pCharVictim == NULL) return false; + + long mapIndex = pCharKiller->GetMapIndex(); + if (mapIndex != pCharVictim->GetMapIndex()) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIndex); + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = (CArenaMap*)(iter->second); + return pArenaMap->OnDead(pCharKiller, pCharVictim); +} + +bool CArenaMap::OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim) +{ + DWORD dwPIDA = pCharKiller->GetPlayerID(); + DWORD dwPIDB = pCharVictim->GetPlayerID(); + + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsMember(dwPIDA) == true && pArena->IsMember(dwPIDB) == true) + { + pArena->OnDead(dwPIDA, dwPIDB); + return true; + } + } + return false; +} + +bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB) +{ + bool restart = false; + + LPCHARACTER pCharA = GetPlayerA(); + LPCHARACTER pCharB = GetPlayerB(); + + if (pCharA == NULL && pCharB == NULL) + { + // Ѵ ?! + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ ߴմϴ.")); + restart = false; + } + else if (pCharA == NULL && pCharB != NULL) + { + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ij Ͽ մϴ.")); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ մϴ.")); + restart = false; + } + else if (pCharA != NULL && pCharB == NULL) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" ij Ͽ մϴ.")); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT(" Ͽ մϴ.")); + restart = false; + } + else if (pCharA != NULL && pCharB != NULL) + { + if (m_dwPIDA == dwPIDA) + { + m_dwSetPointOfA++; + + if (m_dwSetPointOfA >= m_dwSetCount) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharA->GetName()); + + sys_log(0, "ARENA: Duel is end. Winner %s(%d) Loser %s(%d)", + pCharA->GetName(), GetPlayerAPID(), pCharB->GetName(), GetPlayerBPID()); + } + else + { + restart = true; + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharA->GetName()); + pCharA->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharA->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + SendChatPacketToObserver(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + sys_log(0, "ARENA: %s(%d) won a round vs %s(%d)", + pCharA->GetName(), GetPlayerAPID(), pCharB->GetName(), GetPlayerBPID()); + } + } + else if (m_dwPIDB == dwPIDA) + { + m_dwSetPointOfB++; + if (m_dwSetPointOfB >= m_dwSetCount) + { + pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s ÿ ¸Ͽϴ."), pCharB->GetName()); + + sys_log(0, "ARENA: Duel is end. Winner(%d) Loser(%d)", GetPlayerBPID(), GetPlayerAPID()); + } + else + { + restart = true; + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharB->GetName()); + pCharA->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ¸Ͽϴ."), pCharB->GetName()); + pCharB->ChatPacket(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + SendChatPacketToObserver(CHAT_TYPE_NOTICE, "%s %d : %d %s", pCharA->GetName(), m_dwSetPointOfA, m_dwSetPointOfB, pCharB->GetName()); + + sys_log(0, "ARENA : PID(%d) won a round. Opp(%d)", GetPlayerBPID(), GetPlayerAPID()); + } + } + else + { + // wtf + sys_log(0, "ARENA : OnDead Error (%d, %d) (%d, %d)", m_dwPIDA, m_dwPIDB, dwPIDA, dwPIDB); + } + + int potion = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count"); + pCharA->SetPotionLimit(potion); + pCharB->SetPotionLimit(potion); + } + else + { + // ȵȴ ?! + } + + if (restart == false) + { + if (pCharA != NULL) + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + if ( pCharB != NULL) + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ ǵưϴ.")); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 2; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + } + else + { + if (pCharA != NULL) + pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + if (pCharB != NULL) + pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10ʵ մϴ.")); + + if (m_pEvent != NULL) { + event_cancel(&m_pEvent); + } + + TArenaEventInfo* info = AllocEventInfo(); + + info->pArena = this; + info->state = 3; + + m_pEvent = event_create(ready_to_start_event, info, PASSES_PER_SEC(10)); + } + + return true; +} + +bool CArenaManager::AddObserver(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) return false; + + CArenaMap* pArenaMap = iter->second; + return pArenaMap->AddObserver(pChar, ObserverX, ObserverY); +} + +bool CArenaMap::AddObserver(LPCHARACTER pChar, WORD ObserverX, WORD ObserverY) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsMyObserver(ObserverX, ObserverY) == true) + { + pChar->SetArena(pArena); + return pArena->AddObserver(pChar); + } + } + + return false; +} + +bool CArena::IsMyObserver(WORD ObserverX, WORD ObserverY) +{ + return ((ObserverX == m_ObserverPoint.x) && (ObserverY == m_ObserverPoint.y)); +} + +bool CArena::AddObserver(LPCHARACTER pChar) +{ + DWORD pid = pChar->GetPlayerID(); + + m_mapObserver.insert(std::make_pair(pid, (LPCHARACTER)NULL)); + + pChar->SaveExitLocation(); + pChar->WarpSet(m_ObserverPoint.x * 100, m_ObserverPoint.y * 100); + + return true; +} + +bool CArenaManager::IsArenaMap(DWORD dwMapIndex) +{ + return m_mapArenaMap.find(dwMapIndex) != m_mapArenaMap.end(); +} + +MEMBER_IDENTITY CArenaManager::IsMember(DWORD dwMapIndex, DWORD PID) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(dwMapIndex); + + if (iter != m_mapArenaMap.end()) + { + CArenaMap* pArenaMap = iter->second; + return pArenaMap->IsMember(PID); + } + + return MEMBER_NO; +} + +MEMBER_IDENTITY CArenaMap::IsMember(DWORD PID) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + + if (pArena->IsObserver(PID) == true) return MEMBER_OBSERVER; + if (pArena->IsMember(PID) == true) return MEMBER_DUELIST; + } + return MEMBER_NO; +} + +bool CArena::IsObserver(DWORD PID) +{ + itertype(m_mapObserver) iter = m_mapObserver.find(PID); + + return iter != m_mapObserver.end(); +} + +void CArena::OnDisconnect(DWORD pid) +{ + if (m_dwPIDA == pid) + { + if (GetPlayerB() != NULL) + GetPlayerB()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ijͰ Ͽ մϴ.")); + + sys_log(0, "ARENA : Duel is end because of Opp(%d) is disconnect. MyPID(%d)", GetPlayerAPID(), GetPlayerBPID()); + EndDuel(); + } + else if (m_dwPIDB == pid) + { + if (GetPlayerA() != NULL) + GetPlayerA()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ijͰ Ͽ մϴ.")); + + sys_log(0, "ARENA : Duel is end because of Opp(%d) is disconnect. MyPID(%d)", GetPlayerBPID(), GetPlayerAPID()); + EndDuel(); + } +} + +void CArena::RemoveObserver(DWORD pid) +{ + itertype(m_mapObserver) iter = m_mapObserver.find(pid); + + if (iter != m_mapObserver.end()) + { + m_mapObserver.erase(iter); + } +} + +void CArena::SendPacketToObserver(const void * c_pvData, int iSize) +{ + /* + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = iter->second; + + if (pChar != NULL) + { + if (pChar->GetDesc() != NULL) + { + pChar->GetDesc()->Packet(c_pvData, iSize); + } + } + } + */ +} + +void CArena::SendChatPacketToObserver(BYTE type, const char * format, ...) +{ + /* + char chatbuf[CHAT_MAX_LEN + 1]; + va_list args; + + va_start(args, format); + vsnprintf(chatbuf, sizeof(chatbuf), format, args); + va_end(args); + + itertype(m_mapObserver) iter = m_mapObserver.begin(); + + for (; iter != m_mapObserver.end(); iter++) + { + LPCHARACTER pChar = iter->second; + + if (pChar != NULL) + { + if (pChar->GetDesc() != NULL) + { + pChar->ChatPacket(type, chatbuf); + } + } + } + */ +} + +bool CArenaManager::EndDuel(DWORD pid) +{ + itertype(m_mapArenaMap) iter = m_mapArenaMap.begin(); + + for (; iter != m_mapArenaMap.end(); iter++) + { + CArenaMap* pArenaMap = iter->second; + if (pArenaMap->EndDuel(pid) == true) return true; + } + return false; +} + +bool CArenaMap::EndDuel(DWORD pid) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); iter++) + { + CArena* pArena = *iter; + if (pArena->IsMember(pid) == true) + { + pArena->EndDuel(); + return true; + } + } + return false; +} + +bool CArenaManager::RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + if (pChar == NULL) return false; + + itertype(m_mapArenaMap) iter = m_mapArenaMap.find(mapIdx); + + if (iter == m_mapArenaMap.end()) + { + sys_log(0, "ARENA : Cannot find ArenaMap. %d %d %d", mapIdx, ObserverX, ObserverY); + return false; + } + + CArenaMap* pArenaMap = iter->second; + return pArenaMap->RegisterObserverPtr(pChar, mapIdx, ObserverX, ObserverY); +} + +bool CArenaMap::RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY) +{ + itertype(m_listArena) iter = m_listArena.begin(); + + for (; iter != m_listArena.end(); ++iter) + { + CArena* pArena = *iter; + + if (pArena->IsMyObserver(ObserverX, ObserverY) == true) + { + return pArena->RegisterObserverPtr(pChar); + } + } + + return false; +} + +bool CArena::RegisterObserverPtr(LPCHARACTER pChar) +{ + DWORD pid = pChar->GetPlayerID(); + itertype(m_mapObserver) iter = m_mapObserver.find(pid); + + if (iter == m_mapObserver.end()) + { + sys_log(0, "ARENA : not in ob list"); + return false; + } + + m_mapObserver[pid] = pChar; + return true; +} + +bool CArenaManager::IsLimitedItem( long lMapIndex, DWORD dwVnum ) +{ + if ( IsArenaMap( lMapIndex ) == true ) + { + if ( LC_IsCanada() == true ) + { + switch ( dwVnum ) + { + case 50020: + case 50021: + case 50022: + case 50801: + case 50802: + case 50813: + case 50814: + case 50817: + case 50818: + case 50819: + case 50820: + case 50821: + case 50822: + case 50823: + case 50824: + case 50825: + case 50826: + case 71044: + case 71055: + return true; + } + } + } + + return false; +} + diff --git a/game/src/arena.h b/game/src/arena.h new file mode 100644 index 0000000..7c906b9 --- /dev/null +++ b/game/src/arena.h @@ -0,0 +1,139 @@ +#ifndef __CLASS_ARENA_MANAGER__ +#define __CLASS_ARENA_MANAGER__ + +#include + +enum MEMBER_IDENTITY +{ + MEMBER_NO, + MEMBER_DUELIST, + MEMBER_OBSERVER, + + MEMBER_MAX +}; + +class CArena +{ + friend class CArenaMap; + + private : + DWORD m_dwPIDA; + DWORD m_dwPIDB; + + LPEVENT m_pEvent; + LPEVENT m_pTimeOutEvent; + + PIXEL_POSITION m_StartPointA; + PIXEL_POSITION m_StartPointB; + PIXEL_POSITION m_ObserverPoint; + + DWORD m_dwSetCount; + DWORD m_dwSetPointOfA; + DWORD m_dwSetPointOfB; + + std::map m_mapObserver; + + protected : + CArena(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + + bool IsEmpty() const { return ((m_dwPIDA==0) && (m_dwPIDB==0)); } + bool IsMember(DWORD dwPID) const { return ((m_dwPIDA==dwPID) || (m_dwPIDB==dwPID)); } + + bool CheckArea(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + void Clear(); + + bool CanAttack(DWORD dwPIDA, DWORD dwPIDB); + bool OnDead(DWORD dwPIDA, DWORD dwPIDB); + + bool IsObserver(DWORD pid); + bool IsMyObserver(WORD ObserverX, WORD ObserverY); + bool AddObserver(LPCHARACTER pChar); + bool RegisterObserverPtr(LPCHARACTER pChar); + + public : + DWORD GetPlayerAPID() { return m_dwPIDA; } + DWORD GetPlayerBPID() { return m_dwPIDB; } + + LPCHARACTER GetPlayerA() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDA); } + LPCHARACTER GetPlayerB() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDB); } + + PIXEL_POSITION GetStartPointA() { return m_StartPointA; } + PIXEL_POSITION GetStartPointB() { return m_StartPointB; } + + PIXEL_POSITION GetObserverPoint() { return m_ObserverPoint; } + + void EndDuel(); + void ClearEvent() { m_pEvent = NULL; } + void OnDisconnect(DWORD pid); + void RemoveObserver(DWORD pid); + + void SendPacketToObserver(const void * c_pvData, int iSize); + void SendChatPacketToObserver(BYTE type, const char * format, ...); +}; + +class CArenaMap +{ + friend class CArenaManager; + + private : + DWORD m_dwMapIndex; + std::list m_listArena; + + protected : + void Destroy(); + + bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + void SendArenaMapListTo(LPCHARACTER pChar, DWORD dwMapIndex); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + void EndAllDuel(); + bool EndDuel(DWORD pid); + + int GetDuelList(lua_State* L, int index); + + bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim); + bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim); + + bool AddObserver(LPCHARACTER pChar, WORD ObserverX, WORD ObserverY); + bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + + MEMBER_IDENTITY IsMember(DWORD PID); +}; + +class CArenaManager : public singleton +{ + private : + std::map m_mapArenaMap; + + public : + bool Initialize(); + void Destroy(); + + bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5); + + bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y); + + void SendArenaMapListTo(LPCHARACTER pChar); + + void EndAllDuel(); + bool EndDuel(DWORD pid); + + void GetDuelList(lua_State* L); + + bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim); + + bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim); + + bool AddObserver(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY); + + bool IsArenaMap(DWORD dwMapIndex); + MEMBER_IDENTITY IsMember(DWORD dwMapIndex, DWORD PID); + + bool IsLimitedItem( long lMapIndex, DWORD dwVnum ); +}; + +#endif /*__CLASS_ARENA_MANAGER__*/ + diff --git a/game/src/auction_manager.cpp b/game/src/auction_manager.cpp new file mode 100644 index 0000000..ffd3994 --- /dev/null +++ b/game/src/auction_manager.cpp @@ -0,0 +1,1378 @@ +#include "stdafx.h" +#ifdef __AUCTION__ + +#include "desc_client.h" +#include "desc_manager.h" +#include "buffer_manager.h" +#include "packet.h" +#include "char.h" +#include "char_manager.h" +#include "item_manager.h" +#include "log.h" +#include "db.h" +#include +#include "item.h" +#include "desc_client.h" +#include "../../common/tables.h" +#include "protocol.h" +#include "auction_manager.h" +extern int auction_server; + +const char* auction_table_name [_AUCTION_MAX] = {"auction", "wish_auction", "my_auction", "my_wish_auction"}; + +bool CompareItemInfoByItemNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (strcmp (i->item_proto->szLocaleName, j->item_proto->szLocaleName) < 0); +} + +bool CompareItemInfoByItemNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (strcmp (i->item_proto->szLocaleName, j->item_proto->szLocaleName) > 0); +} + +bool CompareItemInfoByCategoryAC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->item_proto->bType < j->item_proto->bType); +} + +bool CompareItemInfoByCategoryDC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->item_proto->bType > j->item_proto->bType); +} + +bool CompareItemInfoByTimeAC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->expired_time > j->expired_time); +} + +bool CompareItemInfoByTimeDC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->expired_time < j->expired_time); +} + +bool CompareItemInfoByCharNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (strcmp (i->shown_name, j->shown_name) < 0); +} + +bool CompareItemInfoByCharNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (strcmp (i->shown_name, j->shown_name) > 0); +} + +bool CompareItemInfoByPriceAC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->get_price() < j->get_price()); +} + +bool CompareItemInfoByPriceDC (TAuctionItemInfo* i, TAuctionItemInfo* j) +{ + return (i->get_price() > j->get_price()); +} + +void AuctionBoard::Sort(TItemInfoVec& vec, BYTE order) +{ + switch (order) + { + case AUCTION_ITEM_NAME_AC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByItemNameAC); + break; + case AUCTION_ITEM_NAME_DC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByItemNameDC); + break; + case AUCTION_CATEGORY_AC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByCategoryAC); + break; + case AUCTION_CATEGORY_DC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByCategoryDC); + break; + case AUCTION_TIME_AC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByTimeAC); + break; + case AUCTION_TIME_DC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByTimeDC); + break; + case AUCTION_CHAR_NAME_AC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByCharNameAC); + break; + case AUCTION_CHAR_NAME_DC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByCharNameDC); + break; + case AUCTION_PRICE_AC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByPriceAC); + break; + case AUCTION_PRICE_DC: + std::stable_sort (vec.begin(), vec.end(), CompareItemInfoByPriceDC); + break; + } +} + +bool AuctionBoard::InsertItemInfo (TAuctionItemInfo* item_info) +{ + TAuctionItemInfo* c = GetItemInfo (item_info->item_id); + if (c != NULL) + { + return false; + } + + c = new TAuctionItemInfo(); + thecore_memcpy (c, item_info, sizeof(TAuctionItemInfo)); + + c->item_proto = ITEM_MANAGER::instance().GetTable(c->item_num); + item_map.insert(TItemInfoMap::value_type(item_info->item_id, c)); + + DWORD offer_id = item_info->get_offer_id(); + + TPCMap::iterator pc_it = offer_map.find (offer_id); + TItemMap* item_map; + if (pc_it == offer_map.end()) + { + item_map = new TItemMap(); + offer_map.insert(TPCMap::value_type (offer_id, item_map)); + } + else + { + item_map = pc_it->second; + } + DWORD item_id = item_info->item_id; + TItemMap::iterator item_it = item_map->find (item_id); + if (item_it == item_map->end()) + { + item_map->insert (TItemMap::value_type (item_id, c)); + return true; + } + + return false; +#ifdef FAST_SORT + SortByItemName::iterator it = item_name_map.find (std::string (c->item_proto->szName)); + TItemInfoVec* vec; + + if (it == item_name_map.end()) + { + vec = new TItemInfoVec(); + item_name_map.insert (SortByItemName::value_type (std::string (c->item_proto->szName), vec)); + } + else + { + vec = it->second; + } + vec.push_back (c); + + return true; +#endif +} + +bool AuctionBoard::UpdateItemInfo (TAuctionItemInfo* item_info) +{ + TAuctionItemInfo* c = GetItemInfo (item_info->item_id); + if (c == NULL) + { + return false; + } + + thecore_memcpy (c, item_info, sizeof(TAuctionItemInfo)); + return true; +} + +bool AuctionBoard::DeleteItemInfo (DWORD key) +{ + TItemInfoMap::iterator it = item_map.find (key); + if (it == item_map.end()) + return false; + else + { + TAuctionItemInfo* item_info = it->second; + delete item_info; + item_map.erase(it); + return true; + } +} + +TAuctionItemInfo* AuctionBoard::GetItemInfo (DWORD key) +{ + TItemInfoMap::iterator it = item_map.find (key); + if (it == item_map.end()) + return NULL; + else + return it->second; +} + +void AuctionBoard::YourItemInfoList (TItemInfoVec& vec, DWORD player_id, int start_idx, BYTE size) +{ + int pass = 0; + vec.clear(); + TPCMap::iterator offer_it = offer_map.find (player_id); + if (offer_it == offer_map.end()) + return; + TItemMap* item_map = offer_it->second; + for (TItemMap::iterator item_it = item_map->begin(); item_it != item_map->end() && vec.size() < size; item_it++) + { + if (pass >= start_idx) + { + vec.push_back (item_it->second); + } + } +} + +// 0~1, 2~3, 4~5, 6~7, 8~9 +// ¦ descending, Ȧ accending. +struct FCheckGradeSatisfied +{ + BYTE grade; + + bool operator() (int item_level) + { + switch (grade) + { + case 0: + return true; + break; + case 1: + if (item_level <= GRADE_LOW) + { + return true; + } + break; + case 2: + if (GRADE_LOW < item_level && item_level <= GRADE_MID) + { + return true; + } + break; + case 3: + if (GRADE_MID < item_level && item_level <= GRADE_HIGH) + { + return true; + } + break; + default: + return true; + } + return false; + } +}; + +void AuctionBoard::SortedItemInfos (TItemInfoVec& vec, BYTE grade, BYTE category, int start_idx, BYTE size, BYTE order[5]) +{ + FCheckGradeSatisfied f_grade = { grade }; + vec.clear(); + int pass = 0; + switch (order[4]) + { +#ifdef FAST_SORT + case AUCTION_ITEM_NAME_AC: + for (SortByItemName::iterator it = item_name_map.begin(); it != item_name_map.end() && vec.size() < size; it++) + { + TItemInfoVec& temp = *(it->second); + + pass += temp.size(); + if (pass < start_idx) + { + continue; + } + for (uint i = 0; i < temp.size(); i++) + { + bool check_grade = (grade == 0); + for (int j = 0; j < ITEM_LIMIT_MAX_NUM; j++) + { + if (temp[i]->item_proto->aLimits[j].bType == LIMIT_LEVEL) + { + check_grade = f_grade (temp[i]->item_proto->aLimits[j].lValue); + break; + } + } + + if (check_grade) + { + vec.push_back (temp[i]); + } + } + } + break; +#endif + default : + for (TItemInfoMap::iterator it = item_map.begin(); it != item_map.end(); it++) + { + vec.push_back (it->second); + } + break; + } + for (int i = 0; i < 5; i++) + { + Sort (vec, order[i]); + } +} + +TSaleItemInfo* SaleBoard::GetItemInfo (DWORD item_id) +{ + TItemInfoMap::iterator it = item_map.find (item_id); + if (it == item_map.end()) + return NULL; + + return it->second; +} + +bool SaleBoard::DeleteItemInfo (DWORD item_id) +{ + TItemInfoMap::iterator it = item_map.find (item_id); + if (it == item_map.end()) + return false; + + TSaleItemInfo* item_info = it->second; + DeleteFromPCMap (wisher_map, item_info->wisher_id, item_info->item_id); + DeleteFromPCMap (seller_map, item_info->offer_id, item_info->item_id); + + delete item_info; + item_map.erase(it); + return true; +} + +bool SaleBoard::DeleteFromPCMap (TPCMap& pc_map, DWORD player_id, DWORD item_id) +{ + TPCMap::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return false; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator item_it = item_map->find (item_id); + if (item_it == item_map->end()) + { + return false; + } + item_map->erase (item_it); + return true; +} + +bool SaleBoard::InsertItemInfo (TSaleItemInfo* item_info) +{ + TSaleItemInfo* c = GetItemInfo (DWORD (item_info->item_id)); + if (c != NULL) + { + return false; + } + c = new TSaleItemInfo (); + thecore_memcpy (c, item_info, sizeof(TSaleItemInfo)); + + c->item_proto = ITEM_MANAGER::instance().GetTable(c->item_num); + + InsertInPCMap (wisher_map, item_info->wisher_id, item_info); + InsertInPCMap (seller_map, item_info->offer_id, item_info); + + return true; +} + +bool SaleBoard::InsertInPCMap (TPCMap& pc_map, DWORD player_id, TSaleItemInfo* item_info) +{ + TPCMap::iterator pc_it = pc_map.find (player_id); + TItemMap* item_map; + if (pc_it == pc_map.end()) + { + item_map = new TItemMap(); + pc_map.insert(TPCMap::value_type (player_id, item_map)); + } + else + { + item_map = pc_it->second; + } + DWORD item_id = item_info->item_id; + TItemMap::iterator item_it = item_map->find (item_id); + if (item_it == item_map->end()) + { + item_map->insert (TItemMap::value_type (item_id, item_info)); + return true; + } + return false; +} + +void SaleBoard::WisherItemInfoList (TItemInfoVec& vec, DWORD wisher_id, int start_idx, BYTE size) +{ + int pass = 0; + vec.clear(); + TPCMap::iterator wish_it = wisher_map.find (wisher_id); + if (wish_it == wisher_map.end()) + return; + TItemMap* item_map = wish_it->second; + for (TItemMap::iterator item_it = item_map->begin(); item_it != item_map->end() && vec.size() < size; item_it++) + { + if (pass >= start_idx) + { + vec.push_back (item_it->second); + } + } +} + +TWishItemInfo* WishBoard::GetItemInfo (DWORD wisher_id, DWORD item_num) +{ + TPCMap::iterator wish_it = wisher_map.find (wisher_id); + if (wish_it == wisher_map.end()) + return NULL; + + TItemMap* item_map = wish_it->second; + TItemMap::iterator item_it = item_map->find (item_num); + if (item_it == item_map->end()) + return NULL; + else + { + return item_it->second; + } +} + +bool WishBoard::DeleteItemInfo (DWORD wisher_id, DWORD item_num) +{ + TPCMap::iterator wish_it = wisher_map.find (wisher_id); + if (wish_it == wisher_map.end()) + return false; + + TItemMap* item_map = wish_it->second; + TItemMap::iterator item_it = item_map->find (item_num); + if (item_it == item_map->end()) + return false; + else + { + delete item_it->second; + item_map->erase (item_it); + return true; + } +} + +bool WishBoard::InsertItemInfo (TWishItemInfo* item_info) +{ + DWORD wisher_id = item_info->offer_id; + DWORD item_num = item_info->item_num; + + TPCMap::iterator wish_it = wisher_map.find (wisher_id); + TItemMap* item_map; + if (wish_it == wisher_map.end()) + { + item_map = new TItemMap(); + wisher_map.insert (TPCMap::value_type (wisher_id, item_map)); + } + else + { + item_map = wish_it->second; + } + + TItemMap::iterator item_it = item_map->find (item_num); + if (item_it == item_map->end()) + { + TWishItemInfo* c = new TWishItemInfo(); + thecore_memcpy (c, item_info, sizeof(TWishItemInfo)); + + c->item_proto = ITEM_MANAGER::instance().GetTable(c->item_num); + item_map->insert (TItemMap::value_type (item_num, c)); + return true; + } + else + { + return false; + } +} + +std::pair MyBidBoard::GetMoney (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return BidInfo (-1, false); + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return BidInfo (-1, false); + else + return it->second; +} + +bool MyBidBoard::Delete (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return false; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return false; + else + { + item_map->erase(it); + return true; + } +} + +void MyBidBoard::Insert (DWORD player_id, DWORD item_id, int money) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + TItemMap* item_map; + if (pc_it == pc_map.end()) + { + item_map = new TItemMap(); + pc_map.insert (TMyBidBoard::value_type (player_id, item_map)); + } + else + item_map = pc_it->second; + + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + { + item_map->insert (TItemMap::value_type (item_id, std::pair (money, false))); + } + else + { + it->second = BidInfo (money, false); + } +} + +void MyBidBoard::Lock (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return; + else + it->second.second = true; +} + +void MyBidBoard::UnLock (DWORD player_id, DWORD item_id) +{ + TMyBidBoard::iterator pc_it = pc_map.find (player_id); + if (pc_it == pc_map.end()) + { + return; + } + TItemMap* item_map = pc_it->second; + TItemMap::iterator it = item_map->find (item_id); + if (it == item_map->end()) + return; + else + it->second.second = false; +} + +void MyBidBoard::YourBidInfo (TItemVec& vec, DWORD bidder_id, int start_idx, int size) +{ + vec.clear(); + TMyBidBoard::iterator pc_it = pc_map.find (bidder_id); + if (pc_it == pc_map.end()) + return; + TItemMap* item_map = pc_it->second; + int pass = 0; + for (TItemMap::iterator it = item_map->begin(); it != item_map->end() && vec.size() < size; it++, pass++) + { + if (pass >= start_idx) + { + vec.push_back (it->first); + } + } +} + +void AuctionManager::Boot (const char* &data, WORD size) +{ + if (decode_2bytes(data) != sizeof(TPlayerItem)) + { + sys_err("TPlayerItem table size error"); + thecore_shutdown(); + return; + } + data += 2; + + size = decode_2bytes(data); + data += 2; + + TPlayerItem* item = (TPlayerItem*) data; + data += size * sizeof(TPlayerItem); + if (auction_server) + { + for (WORD i = 0; i < size; ++i, ++item) + { + InsertItem (item); + } + + } + if (decode_2bytes(data) != sizeof(TAuctionItemInfo)) + { + sys_err("TAuctionItemInfo table size error"); + thecore_shutdown(); + return; + } + data += 2; + + size = decode_2bytes(data); + data += 2; + + TAuctionItemInfo* auction_item_info = (TAuctionItemInfo*) data; + data += size * sizeof(TAuctionItemInfo); + if (auction_server) + { + for (WORD i = 0; i < size; ++i, ++auction_item_info) + { + Auction.InsertItemInfo (auction_item_info); + } + } + if (decode_2bytes(data) != sizeof(TSaleItemInfo)) + { + sys_err("TSaleItemInfo table size error"); + thecore_shutdown(); + return; + } + data += 2; + + size = decode_2bytes(data); + data += 2; + + TSaleItemInfo* sale_item_info = (TSaleItemInfo*) data; + data += size * sizeof(TSaleItemInfo); + if (auction_server) + { + for (WORD i = 0; i < size; ++i, ++sale_item_info) + { + Sale.InsertItemInfo (sale_item_info); + } + } + + if (decode_2bytes(data) != sizeof(TWishItemInfo)) + { + sys_err("TWishItemInfo table size error"); + thecore_shutdown(); + return; + } + data += 2; + + size = decode_2bytes(data); + data += 2; + + TWishItemInfo* wish_item_info = (TWishItemInfo*) data; + data += size * sizeof(TWishItemInfo); + if (auction_server) + { + for (WORD i = 0; i < size; ++i, ++wish_item_info) + { + Wish.InsertItemInfo (wish_item_info); + } + } + + if (decode_2bytes(data) != (sizeof(DWORD) + sizeof(DWORD) + sizeof(int))) + { + sys_err("my_bid table size error"); + thecore_shutdown(); + return; + } + data += 2; + + size = decode_2bytes(data); + data += 2; + + if (auction_server) + { + for (WORD i = 0; i < size; i++) + { + DWORD player_id = *((DWORD*) data); + data += sizeof(DWORD); + DWORD item_id = *((DWORD*) data); + data += sizeof(DWORD); + int money = *((int*) data); + data += sizeof(int); + MyBid.Insert (player_id, item_id, money); + } + } + else + data += size * (sizeof(DWORD) + sizeof(DWORD) + sizeof(int)); +} + +bool AuctionManager::InsertItem (LPITEM item) +{ + TItemMap::iterator it = auction_item_map.find (item->GetID()); + if (it != auction_item_map.end()) + return false; + auction_item_map.insert(TItemMap::value_type (item->GetID(), item)); + return true; +} + +bool AuctionManager::InsertItem (TPlayerItem* player_item) +{ + if (player_item->window != AUCTION) + return false; + LPITEM item = ITEM_MANAGER::instance().CreateItem(player_item->vnum, player_item->count, player_item->id, false, -1, true); + + if (!item) + { + sys_err("cannot create item vnum %d id %u",player_item->vnum, player_item->id); + return false; + } + + item->SetSkipSave(true); + + if (!InsertItem(item)) + { + M2_DESTROY_ITEM(item); + return false; + } + item->SetSockets(player_item->alSockets); + item->SetAttributes(player_item->aAttr); + item->SetWindow(AUCTION); + + return true; +} + +LPITEM AuctionManager::GetInventoryItem (DWORD item_id) +{ + TItemMap::iterator it = auction_item_map.find (item_id); + if (it == auction_item_map.end()) + return NULL; + else + return it->second; +} + +bool AuctionManager::DeleteItem (DWORD item_id) +{ + TItemMap::iterator it = auction_item_map.find (item_id); + if (it == auction_item_map.end()) + return false; + else + { + auction_item_map.erase (it); + return true; + } +} +// +//bool AuctionManager::InsertAuctionMoney (DWORD player_id, int money) +//{ +// TAuctionMoneyMap::iterator it = auction_item_map.find (player_id); +// if (it == auction_item_map.end()) +// return false; +// auction_item_map.insert (TAuctionMoneyMap::value_type (player_id, money)); +//} +// +//bool AuctionManager::ChangeAuctionMoney (DWORD player_id, int changing_amount) +//{ +// TAuctionMoneyMap::iterator it = auction_item_map.find (player_id); +// if (it == auction_item_map.end()) +// return false; +// if (it->second + changing_amount < 0) +// { +// sys_err ("Cannot have money under 0."); +// return false; +// } +// +// TPacketGDCommnadAuction pack_cm; +// pack_cm.changing_money (it->second); +// +// db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, player_id, &pack_cm, sizeof(TPacketGDCommnadAuction)); +//} + + +bool AuctionManager::InsertAuctionItemInfo (TAuctionItemInfo* item_info) +{ + return Auction.InsertItemInfo (item_info); +} + +bool AuctionManager::InsertSaleItemInfo (TSaleItemInfo* item_info) +{ + return Sale.InsertItemInfo (item_info); +} + +bool AuctionManager::InsertWishItemInfo (TWishItemInfo* item_info) +{ + return Wish.InsertItemInfo (item_info); +} + +void AuctionManager::YourBidItemInfoList (AuctionBoard::TItemInfoVec& vec, DWORD bidder_id, int start_idx, int size) +{ + vec.clear(); + MyBidBoard::TItemVec item_id_vec; + MyBid.YourBidInfo (item_id_vec, bidder_id, 0, 6); + for (int i = 0; i < item_id_vec.size(); i++) + { + TAuctionItemInfo* item_info = Auction.GetItemInfo(item_id_vec[i]); + if (item_info != NULL) + { + vec.push_back (item_info); + } + else + { + // expired ⼭ ־Ѵ. + } + } +} + +void AuctionManager::get_auction_list (LPCHARACTER ch, int start_idx, int size, int cond) +{ + BYTE order[5] = {0,}; + AuctionBoard::TItemInfoVec vec; + switch (cond) + { + case 0: + order[4] = AUCTION_ITEM_NAME_AC; + order[3] = AUCTION_PRICE_DC; + Auction.SortedItemInfos (vec, 0, 0, 0, 12, order); + + // SortedItemInfos + break; + case 1: + order[4] = AUCTION_PRICE_DC; + order[3] = AUCTION_ITEM_NAME_DC; + Auction.SortedItemInfos (vec, 0, 0, 5, 12, order); + + break; + case 2: + order[4] = AUCTION_CHAR_NAME_DC; + order[3] = AUCTION_ITEM_NAME_DC; + order[2] = AUCTION_TIME_AC; + Auction.SortedItemInfos (vec, 0, 0, 0, 12, order); + + break; + + } + for (uint i = 0; i < vec.size(); i++) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s item id : %d price : %d", vec[i]->item_proto->szName, vec[i]->get_item_id(), vec[i]->get_price()); + } +} + +void AuctionManager::get_my_auction_list (LPCHARACTER ch, int start_idx, int size) +{ + AuctionBoard::TItemInfoVec auction_vec; + Auction.YourItemInfoList (auction_vec, ch->GetPlayerID(), 0, 6); + + for (uint i = 0; i < auction_vec.size(); i++) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s item id : %d price : %d", auction_vec[i]->item_proto->szName, auction_vec[i]->get_item_id(), auction_vec[i]->get_price()); + } + SaleBoard::TItemInfoVec wish_vec; + Sale.WisherItemInfoList (wish_vec, ch->GetPlayerID(), 0, 6); + for (uint i = 0; i < auction_vec.size(); i++) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s item id : %d price : %d", wish_vec[i]->item_proto->szName, wish_vec[i]->item_id, + wish_vec[i]->get_price()); + } +} + +void AuctionManager::get_my_purchase_list (LPCHARACTER ch, int start_idx, int size) +{ + AuctionBoard::TItemInfoVec auction_vec; + YourBidItemInfoList (auction_vec, ch->GetPlayerID(), 0, 6); + + for (uint i = 0; i < auction_vec.size(); i++) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s item id : %d price : %d", auction_vec[i]->item_proto->szName, auction_vec[i]->get_item_id(), auction_vec[i]->get_price()); + } + + //WishBoard::TItemInfoVec wish_vec; + //Wish.WisherItemInfoList (wish_vec, ch->GetPlayerID(), 0, 6); + //for (uint i = 0; i < auction_vec.size(); i++) + //{ + // ch->ChatPacket(CHAT_TYPE_INFO, "%s item id : %d price : %d", wish_vec[i]->item_proto->szName, wish_vec[i]->item_id, + // wish_vec[i]->get_price()); + //} +} + +void AuctionManager::enroll_auction (LPCHARACTER ch, LPITEM item, BYTE empire, int bidPrice, int immidiatePurchasePrice) +{ + if (ch != item->GetOwner()) + { + sys_err ("Item %d's owner is %s, not %s",ch->GetName(), item->GetOwner()->GetName()); + return; + } + if (item->IsEquipped()) + { + ch->ChatPacket(CHAT_TYPE_INFO, " ."); + return; + } + + if (GetAuctionItemInfo (item->GetID())) + { + sys_err ("Item %d is already in auction.", item->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, "̹ ž. ü ?"); + return; + } + + if (item->GetWindow() == AUCTION) + { + sys_err ("Item %d is already in auction.", item->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, " .."); + return; + } + + TPacketGDCommnadAuction pack_ea; + pack_ea.enroll_product (item->GetID(), empire, bidPrice, immidiatePurchasePrice); + + item->SetSkipSave(true); + item->RemoveFromCharacter(); + M2_DESTROY_ITEM (item); + + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_ea, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::enroll_sale (LPCHARACTER ch, LPITEM item, DWORD wisher_id, int salePrice) +{ + if (ch != item->GetOwner()) + { + sys_err ("Item %d's owner is %s, not %s",ch->GetName(), item->GetOwner()->GetName()); + return; + } + if (item->IsEquipped()) + { + ch->ChatPacket(CHAT_TYPE_INFO, " ."); + return; + } + + if (GetSaleItemInfo (item->GetID())) + { + sys_err ("Item %d is already in auction.", item->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, "̹ ž. ü ?"); + return; + } + + if (item->GetWindow() == AUCTION) + { + sys_err ("Item %d is already in auction.", item->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, " .."); + return; + } + + TPacketGDCommnadAuction pack_es; + pack_es.enroll_sale (item->GetID(), wisher_id, salePrice); + + item->SetSkipSave(true); + item->RemoveFromCharacter(); + M2_DESTROY_ITEM (item); + + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_es, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::enroll_wish (LPCHARACTER ch, DWORD item_num, BYTE empire, int wishPrice) +{ + TPacketGDCommnadAuction pack_ew; + pack_ew.enroll_wish (item_num, empire, wishPrice); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_ew, sizeof(TPacketGDCommnadAuction)); +} + +// fixme +void AuctionManager::bid (LPCHARACTER ch, DWORD item_id, int bid_price) +{ + std::pair mb = MyBid.GetMoney(ch->GetPlayerID(), item_id); + if (mb.first != -1) + { + ch->ChatPacket (CHAT_TYPE_INFO, " ϶ ̴."); + } + if (ch->GetGold() < bid_price) + { + ch->ChatPacket(CHAT_TYPE_INFO, " "); + return; + } + + ch->PointChange(POINT_GOLD, -bid_price, false); + + TPacketGDCommnadAuction pack_bid; + pack_bid.bid (item_id, bid_price); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_bid, sizeof(TPacketGDCommnadAuction)); +} + +// fixme +// ݵ !!! +void AuctionManager::immediate_purchase (LPCHARACTER ch, DWORD item_id) +{ + TAuctionItemInfo* item_info = GetAuctionItemInfo (item_id); + + if (item_info == NULL) + { + sys_err ("Invild item id : %d", item_id); + return; + } + + if (item_info->get_impur_price() == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ﱸ "); + return; + } + + if (ch->GetGold() < item_info->get_impur_price()) + { + ch->ChatPacket(CHAT_TYPE_INFO, " "); + return; + } + + ch->PointChange(POINT_GOLD, -item_info->get_impur_price(), false); + + TPacketGDCommnadAuction pack_impur; + pack_impur.impur (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_impur, sizeof(TPacketGDCommnadAuction)); +} + +// +void AuctionManager::get_auctioned_item (LPCHARACTER ch, DWORD item_id, DWORD item_num) +{ + TItemTable* proto = ITEM_MANAGER::instance().GetTable(item_num); + int pos = ch->GetEmptyInventory(proto->bSize); + + if (pos == -1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ڸ "); + return; + } + + //TAuctionItemInfo* + // if (Auction.GetItemInfo (item_id)) + // { + + // } + + TPacketGDCommnadAuction pack_gai; + pack_gai.get_auctioned_item (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_gai, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::buy_sold_item (LPCHARACTER ch, DWORD item_id) +{ + TPacketGDCommnadAuction pack_bsi; + pack_bsi.buy_sold_item (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_bsi, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::cancel_auction (LPCHARACTER ch, DWORD item_id) +{ + TPacketGDCommnadAuction pack_ca; + pack_ca.cancel_auction (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_ca, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::cancel_wish (LPCHARACTER ch, DWORD item_num) +{ + TPacketGDCommnadAuction pack_cw; + pack_cw.cancel_wish (item_num); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_cw, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::cancel_sale (LPCHARACTER ch, DWORD item_id) +{ + TPacketGDCommnadAuction pack_cs; + pack_cs.cancel_sale (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_cs, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::rebid (LPCHARACTER ch, DWORD item_id, int bid_price) +{ + std::pair mb = MyBid.GetMoney (ch->GetPlayerID(), item_id); + int money = mb.first; + bool lock = mb.second; + if (money == -1) + { + sys_err ("Do bid first. How can you rebid? pid %d, item_id %d",ch->GetPlayerID(), item_id); + return; + } + + if (lock) + { + ch->ChatPacket(CHAT_TYPE_INFO, " ̾."); + return; + } + + if (ch->GetGold() + money < bid_price) + { + ch->ChatPacket(CHAT_TYPE_INFO, " "); + return; + } + + MyBid.Lock (ch->GetPlayerID(), item_id); + + ch->PointChange(POINT_GOLD, -(bid_price - money), false); + + TPacketGDCommnadAuction pack_rebid; + pack_rebid.rebid (item_id, (bid_price - money)); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_rebid, sizeof(TPacketGDCommnadAuction)); +} + +void AuctionManager::bid_cancel (LPCHARACTER ch, DWORD item_id) +{ + std::pair mb = MyBid.GetMoney (ch->GetPlayerID(), item_id); + int money = mb.first; + bool lock = mb.second; + if (money == -1) + { + sys_err ("Do bid first. How can you bid cancel? pid %d, item_id %d",ch->GetPlayerID(), item_id); + return; + } + + if (lock) + { + ch->ChatPacket(CHAT_TYPE_INFO, " ̾."); + return; + } + + TAuctionItemInfo* item_info = GetAuctionItemInfo(item_id); + if (item_info->get_bidder_id() == ch->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ϰ ְ ھ. ."); + return; + } + + MyBid.Delete (ch->GetPlayerID(), item_id); + ch->PointChange(POINT_GOLD, money, true); + + TPacketGDCommnadAuction pack_bc; + pack_bc.bid_cancel (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_bc, sizeof(TPacketGDCommnadAuction)); +} + +// +void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuction* cmd_result) +{ + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(commander_id); + switch (cmd_result->cmd) + { + case AUCTION_ENR_AUC: + switch (cmd_result->result) + { + case AUCTION_SUCCESS: + { + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + + InsertItem (player_item); + player_item++; + + TAuctionItemInfo* item_info = (TAuctionItemInfo*)player_item; + + Auction.InsertItemInfo (item_info); + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "忡 ߾."); + } + break; + } + case AUCTION_FAIL: + { + if (ch != NULL) + { + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + LPITEM item = ITEM_MANAGER::instance().CreateItem(player_item->vnum, player_item->count, player_item->id); + + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID (player_item->owner); + + ch->AutoGiveItem (item, true); + ch->ChatPacket(CHAT_TYPE_INFO, "忡 ߾."); + } + break; + } + } + break; + case AUCTION_ENR_SALE: + switch (cmd_result->result) + { + case AUCTION_SUCCESS: + { + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + + InsertItem (player_item); + player_item++; + + TSaleItemInfo* item_info = (TSaleItemInfo*)player_item; + + Sale.InsertItemInfo (item_info); + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Ǹ忡 ߾."); + } + break; + } + case AUCTION_FAIL: + { + if (ch != NULL) + { + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + LPITEM item = ITEM_MANAGER::instance().CreateItem(player_item->vnum, player_item->count, player_item->id); + + + ch->AutoGiveItem (item, true); + ch->ChatPacket(CHAT_TYPE_INFO, "Ǹ忡 ߾."); + } + break; + } + } + break; + case AUCTION_ENR_WISH: + switch (cmd_result->result) + { + case AUCTION_SUCCESS: + { + cmd_result++; + TWishItemInfo* item_info = (TWishItemInfo*)cmd_result; + + Wish.InsertItemInfo (item_info); + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ϴٿ ߾."); + } + break; + } + case AUCTION_FAIL: + { + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ϴٿ ߾."); + } + break; + } + } + break; + case AUCTION_BID: + if (cmd_result->result <= AUCTION_FAIL) + { + + } + else + { + cmd_result++; + TAuctionItemInfo* new_item_info = (TAuctionItemInfo*)cmd_result; + TAuctionItemInfo* old_item_info = Auction.GetItemInfo (new_item_info->get_item_id()); + thecore_memcpy (old_item_info, new_item_info, sizeof(TAuctionItemInfo)); + MyBid.Insert(new_item_info->bidder_id, new_item_info->item_id, new_item_info->get_bid_price()); + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "߾."); + } + } + break; + case AUCTION_IMME_PUR: + if (cmd_result->result <= AUCTION_FAIL) + { + } + else + { + cmd_result++; + TAuctionItemInfo* new_item_info = (TAuctionItemInfo*)cmd_result; + TAuctionItemInfo* old_item_info = Auction.GetItemInfo (new_item_info->get_item_id()); + thecore_memcpy (old_item_info, new_item_info, sizeof(TAuctionItemInfo)); + if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ﱸ عȾ."); + } + } + break; + case AUCTION_GET_AUC: + case AUCTION_BUY_SOLD: + case AUCTION_CANCEL_AUC: + case AUCTION_CANCEL_SALE: + if (cmd_result->result <= AUCTION_FAIL) + { + } + else + { + BYTE cmd = cmd_result->cmd; + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + + DWORD item_id = player_item->id; + + if (ch != NULL) + { + LPITEM item = ITEM_MANAGER::instance().CreateItem(player_item->vnum, player_item->count, item_id); + ch->AutoGiveItem (item, true); + ch->ChatPacket(CHAT_TYPE_INFO, "Ծ."); + if (cmd == AUCTION_GET_AUC || cmd == AUCTION_CANCEL_AUC) + { + TPacketGDCommnadAuction pack_dai; + pack_dai.delete_auction_item (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_dai, sizeof(TPacketGDCommnadAuction)); + } + else if (cmd == AUCTION_BUY_SOLD || cmd == AUCTION_CANCEL_SALE) + { + TPacketGDCommnadAuction pack_dsi; + pack_dsi.delete_sale_item (item_id); + db_clientdesc->DBPacket(HEADER_GD_COMMAND_AUCTION, ch->GetPlayerID(), &pack_dsi, sizeof(TPacketGDCommnadAuction)); + } + } + } + case AUCTION_DELETE_AUCTION_ITEM: + case AUCTION_DELETE_SALE_ITEM: + { + AuctionCmd cmd = cmd_result->cmd; + cmd_result++; + TPlayerItem* player_item = (TPlayerItem*)cmd_result; + + DWORD item_id = player_item->id; + + if (!GetInventoryItem(player_item->id)) + { + sys_err ("AUCTION_CMD %d : invalid item_id %d", cmd, item_id); + break; + } + + if (cmd == AUCTION_DELETE_AUCTION_ITEM) + { + if (!GetAuctionItemInfo(item_id)) + { + DeleteItem (item_id); + Auction.DeleteItemInfo (item_id); + } + } + else if (cmd == AUCTION_DELETE_SALE_ITEM) + { + if (!GetSaleItemInfo(item_id)) + { + DeleteItem (item_id); + Sale.DeleteItemInfo (item_id); + } + } + } + break; + case AUCTION_CANCEL_WISH: + if (cmd_result->result <= AUCTION_FAIL) + { + } + else + { + if (!Wish.DeleteItemInfo (commander_id, cmd_result->target)) + { + sys_err ("Cannot cancel wish, invalid player_id : %d, item_num : %d", commander_id, cmd_result->target); + } + else if (ch != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "߾."); + } + } + break; + case AUCTION_REBID: + if (cmd_result->result <= AUCTION_FAIL) + { + BYTE result = cmd_result->result; + DWORD item_id = cmd_result->target; + cmd_result++; + int restore_money = *((int*)cmd_result); + ch->PointChange(POINT_GOLD, restore_money, true); + + MyBid.UnLock (commander_id, item_id); + } + else + { + // insertϸ lock Ǯ. + DWORD item_id = cmd_result->target; + cmd_result++; + TAuctionItemInfo* auction_info = (TAuctionItemInfo*)cmd_result; + ch->PointChange(POINT_GOLD, 0, true); + + MyBid.Insert (commander_id, item_id, auction_info->get_bid_price()); + Auction.UpdateItemInfo (auction_info); + } + break; + case AUCTION_BID_CANCEL: + if (cmd_result->result <= AUCTION_FAIL) + { + } + else + { + MyBid.Delete (commander_id, cmd_result->target); + } + } +} + +#endif diff --git a/game/src/auction_manager.h b/game/src/auction_manager.h new file mode 100644 index 0000000..248dd80 --- /dev/null +++ b/game/src/auction_manager.h @@ -0,0 +1,218 @@ +#ifndef __INC_AUCTION_MANAGER_H +#define __INC_AUCTION_MANAGER_H + +#include "../../libsql/AsyncSQL.h" +#include "../../common/auction_table.h" +#include +#include + +#define GRADE_LOW 30 +#define GRADE_MID 60 +#define GRADE_HIGH 90 + +template<> +class hash > +{ // hash functor +public: + typedef std::pair _Kty; + + size_t operator()(const _Kty& _Keyval) const + { // hash _Keyval to size_t value by pseudorandomizing transform + ldiv_t _Qrem = ldiv((size_t)_Keyval.first + (size_t)_Keyval.second, 127773); + + _Qrem.rem = 16807 * _Qrem.rem - 2836 * _Qrem.quot; + if (_Qrem.rem < 0) + _Qrem.rem += 2147483647; + return ((size_t)_Qrem.rem); + } +}; + +bool CompareItemInfoByItemNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j); +bool CompareItemInfoByItemNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j); + +bool CompareItemInfoByCategoryAC (TAuctionItemInfo* i, TAuctionItemInfo* j); +bool CompareItemInfoByCategoryDC (TAuctionItemInfo* i, TAuctionItemInfo* j); + +bool CompareItemInfoByTimeAC (TAuctionItemInfo* i, TAuctionItemInfo* j); +bool CompareItemInfoByTimeDC (TAuctionItemInfo* i, TAuctionItemInfo* j); + +bool CompareItemInfoByCharNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j); +bool CompareItemInfoByCharNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j); + +bool CompareItemInfoByPriceAC (TAuctionItemInfo* i, TAuctionItemInfo* j); +bool CompareItemInfoByPriceDC (TAuctionItemInfo* i, TAuctionItemInfo* j); + +class AuctionBoard +{ +public: + AuctionBoard() {} + ~AuctionBoard() {} + + TAuctionItemInfo* GetItemInfo (DWORD key); + bool DeleteItemInfo (DWORD key); + bool InsertItemInfo (TAuctionItemInfo* item_info); + bool UpdateItemInfo (TAuctionItemInfo* item_info); + + +private: + typedef boost::unordered_map TItemInfoMap; + TItemInfoMap item_map; + + typedef std::map TItemMap; + typedef boost::unordered_map TPCMap; + + TPCMap offer_map; + + // sorting members +public: + typedef std::vector TItemInfoVec; +private: + typedef std::map SortByItemName; + + SortByItemName item_name_map; + + void Sort(TItemInfoVec& vec, BYTE order); + +public: + void SortedItemInfos (TItemInfoVec& vec, BYTE grade, BYTE category, int start_idx, BYTE size, BYTE order[5]); + + // Լ. + void YourItemInfoList (TItemInfoVec& vec, DWORD player_id, int start_idx, BYTE size); + +}; +class SaleBoard +{ +private: + typedef boost::unordered_map TItemInfoMap; + TItemInfoMap item_map; + + typedef std::map TItemMap; + typedef boost::unordered_map TPCMap; + + TPCMap wisher_map; + TPCMap seller_map; + + bool DeleteFromPCMap (TPCMap& pc_map, DWORD player_id, DWORD item_id); + bool InsertInPCMap (TPCMap& pc_map, DWORD player_id, TSaleItemInfo* item_info); + +public: + SaleBoard() {} + ~SaleBoard() {} + + typedef std::vector TItemInfoVec; + void WisherItemInfoList (TItemInfoVec& vec, DWORD wisher_id, int start_idx, BYTE size); + + TSaleItemInfo* GetItemInfo (DWORD key); + bool DeleteItemInfo (DWORD key); + bool InsertItemInfo (TSaleItemInfo* item_info); +}; + +class WishBoard +{ +private: + typedef std::map TItemMap; + typedef boost::unordered_map TPCMap; + TPCMap wisher_map; + +public: + typedef TWishItemInfo ItemInfo; + + WishBoard() {} + ~WishBoard() {} + + TWishItemInfo* GetItemInfo (DWORD wisher_id, DWORD item_num); + bool DeleteItemInfo (DWORD wisher_id, DWORD item_num); + bool InsertItemInfo (TWishItemInfo* item_info); +}; + +class MyBidBoard +{ +private: + typedef std::pair BidInfo; + typedef std::map TItemMap; + typedef boost::unordered_map TMyBidBoard; + // bidder_id key + TMyBidBoard pc_map; + +public: + MyBidBoard() {} + ~MyBidBoard() {} + + typedef std::vector TItemVec; + + void YourBidInfo (TItemVec& vec, DWORD bidder_id, int start_idx, int size); + + BidInfo GetMoney (DWORD player_id, DWORD item_id); + bool Delete (DWORD player_id, DWORD item_id); + // ̹ . + void Insert (DWORD player_id, DWORD item_id, int money); + void Lock (DWORD player_id, DWORD item_id); + void UnLock (DWORD player_id, DWORD item_id); +}; + +class AuctionManager : public singleton +{ +private : + typedef boost::unordered_map TItemMap; + TItemMap auction_item_map; + + // auction ϵ , ̺ Ե ʴ ϴ ͵ + AuctionBoard Auction; + SaleBoard Sale; + WishBoard Wish; + MyBidBoard MyBid; + +public: + bool InsertItem (LPITEM item); + bool InsertItem (TPlayerItem* player_item); + LPITEM GetInventoryItem (DWORD item_id); + bool DeleteItem (DWORD item_id); + + bool InsertAuctionItemInfo (TAuctionItemInfo* item_info); + TAuctionItemInfo* GetAuctionItemInfo (DWORD item_id) + { + return Auction.GetItemInfo (item_id); + } + + bool InsertSaleItemInfo (TSaleItemInfo* item_info); + TSaleItemInfo* GetSaleItemInfo (DWORD item_id) + { + return Sale.GetItemInfo (item_id); + } + + bool InsertWishItemInfo (TWishItemInfo* item_info); + TWishItemInfo* GetWishItemInfo (DWORD wisher_id, DWORD item_id) + { + return Wish.GetItemInfo (wisher_id, item_id); + } + + void YourBidItemInfoList (AuctionBoard::TItemInfoVec& vec, DWORD bidder_id, int start_idx, int size); + + void Boot (const char* &pdata, WORD size); + + void get_auction_list (LPCHARACTER ch, int start_idx, int size, int cond); + void get_my_auction_list (LPCHARACTER ch, int start_idx, int size); + void get_my_purchase_list (LPCHARACTER ch, int start_idx, int size); + void enroll_auction (LPCHARACTER ch, LPITEM item, BYTE empire, int bidPrice, int immidiatePurchasePrice); + + void recv_result_auction (DWORD commander_id, TPacketDGResultAuction* cmd_result); + + void bid (LPCHARACTER ch, DWORD item_id, int price); + void immediate_purchase (LPCHARACTER ch, DWORD item_id); + + void enroll_sale (LPCHARACTER ch, LPITEM item, DWORD wisher_id, int salePrice); + void enroll_wish (LPCHARACTER ch, DWORD item_num, BYTE empire, int wishPrice); + + void get_auctioned_item (LPCHARACTER ch, DWORD item_id, DWORD item_num); + void buy_sold_item (LPCHARACTER ch, DWORD item_id); + void cancel_auction (LPCHARACTER ch, DWORD item_id); + void cancel_wish (LPCHARACTER ch, DWORD item_num); + void cancel_sale (LPCHARACTER ch, DWORD item_id); + + void rebid (LPCHARACTER ch, DWORD item_id, int price); + void bid_cancel (LPCHARACTER ch, DWORD item_id); +/* + void close_auction (LPCHARACTER ch);*/ +}; + +#endif diff --git a/game/src/auction_packet.h b/game/src/auction_packet.h new file mode 100644 index 0000000..7b1b1ec --- /dev/null +++ b/game/src/auction_packet.h @@ -0,0 +1,5 @@ +typedef struct packet_auction_simple_item_info +{ + BYTE header; + BYTE size; +} TPacketGCAuctionItemSimpleInfo; \ No newline at end of file diff --git a/game/src/auth_brazil.cpp b/game/src/auth_brazil.cpp new file mode 100644 index 0000000..dec588f --- /dev/null +++ b/game/src/auth_brazil.cpp @@ -0,0 +1,182 @@ +/* vi: set sw=4 ts=8 cino=g0,\:0 : */ +/********************************************************************* + * date : 2010.4.7 + * file : auth_brazil.c + * author : mhh + * description : + */ +#include "stdafx.h" + +#ifndef __WIN32__ +#include +#include +#endif +#include +#include +#ifdef __FreeBSD__ +#include +#else +#include "../../libthecore/include/xmd5.h" +#endif + +#include "auth_brazil.h" + +static const char* FN_md5(const char *src) +{ + static char s_buffer[512]; + + memset(s_buffer, 0x00, sizeof(s_buffer)); + + unsigned char digest[16] = {0}; + MD5_CTX md5; + MD5Init(&md5); + MD5Update(&md5, (const unsigned char*) src, strlen(src)); + MD5Final(digest, &md5); + + int offset = 0; + for (int i=0; i<16; ++i) { + offset += sprintf(s_buffer + offset, "%02x", digest[i]); + } + return s_buffer; +} + + +static int FN_make_request(const char *login, const char *password, /*out*/ char *dst, int dst_size) +{ + int len = snprintf(dst, dst_size, +// "GET /metin2/game_auth.php?ID=%s&PW=%s HTTP/1.1\r\n" + "GET /metin2/?ID=%s&PW=%s HTTP/1.1\r\n" + "Host: auth.ongame.com.br\r\n" + "Connection: Close\r\n\r\n", + login, FN_md5(password)); + + return len; +} + + +static int FN_parse_reply(char *reply) +{ + char buffer[2048]; + strlcpy(buffer, reply, sizeof(buffer)); + + const char *delim = "\r\n"; + char *last = 0; + char *v = strtok_r(buffer, delim, &last); + char *result = 0; + + while (v) + { + result = v; + v = strtok_r(NULL, delim, &last); + } + + if (result) + { + if (0 == strcasecmp("true", result)) + return AUTH_BRAZIL_SUCC; + else if (0 == strcasecmp("false", result)) + return AUTH_BRAZIL_WRONGPWD; + else if (0 == strcasecmp("unknown", result)) + return AUTH_BRAZIL_NOID; + else if (0 == strcasecmp("flash", result)) + return AUTH_BRAZIL_FLASHUSER; + } + + return AUTH_BRAZIL_SERVER_ERR; +} + + +extern void socket_timeout(socket_t s, long sec, long usec); + +int auth_brazil(const char *login, const char *pwd) +{ + + const char *host = "auth.ongame.com.br"; + int port = 80; + + socket_t fd = socket_connect(host, port); + if (fd < 0) + { + sys_err("[AUTH_BRAZIL] : could not connect to gsp server(%s)", host); + return AUTH_BRAZIL_SERVER_ERR; + } + + socket_block(fd); + socket_timeout(fd, 3, 0); + + // send request + { + char request[512]; + int len = FN_make_request(login, pwd, request, sizeof(request)); + +#ifndef __WIN32__ + if (write(fd, request, len) < 0) +#else + if (_write(fd, request, len) < 0) +#endif + { + sys_err("[AUTH_BRAZIL] : could not send auth-request (%s)", login); + close(fd); + return AUTH_BRAZIL_SERVER_ERR; + } + } + + // read reply + { + char reply[1024] = {0}; + int len = read(fd, reply, sizeof(reply)); + close(fd); + + if (len <= 0) + { + sys_err("[AUTH_BRAZIL] : could not recv auth-reply (%s)", login); + return AUTH_BRAZIL_SERVER_ERR; + } + + // 쿡 query count ø. + auth_brazil_inc_query_count(); + + return FN_parse_reply(reply); + } +} + + +static int s_query_count = 0; + +int auth_brazil_inc_query_count() +{ + return ++s_query_count; +} + +void auth_brazil_log() +{ + FILE *fp = 0; + + // open and try backup + { + fp = fopen("AUTH_COUNT.log", "a"); + + if (0 == fp) + return; + + struct stat sb; + fstat(fileno(fp), &sb); + if (sb.st_size > 1024 * 1024) + { + fclose(fp); + rename("AUTH_COUNT.log", "AUTH_COUNT.log.old"); + + fp = fopen("AUTH_COUNT.log", "a"); + } + } + + // write log + { + fprintf(fp, "%d\n", s_query_count); + fclose(fp); + } + + // reset query count + s_query_count = 0; +} + diff --git a/game/src/auth_brazil.h b/game/src/auth_brazil.h new file mode 100644 index 0000000..5a69ee9 --- /dev/null +++ b/game/src/auth_brazil.h @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=8 cino=g0,\:0 : */ +/********************************************************************* + * date : 2010.4.7 + * file : auth_brazil.h + * author : mhh + * description : + */ +#ifndef __auth_brazil_h_1270647899__ +#define __auth_brazil_h_1270647899__ + +#define AUTH_BRAZIL_SERVER_ERR 0 +#define AUTH_BRAZIL_SUCC 1 +#define AUTH_BRAZIL_NOID 2 +#define AUTH_BRAZIL_WRONGPWD 3 +#define AUTH_BRAZIL_FLASHUSER 4 + +int auth_brazil(const char *login, const char *pwd); + + +int auth_brazil_inc_query_count(); +void auth_brazil_log(); + +#endif // __auth_brazil_h_1270647899__ diff --git a/game/src/banword.cpp b/game/src/banword.cpp new file mode 100644 index 0000000..816b7c1 --- /dev/null +++ b/game/src/banword.cpp @@ -0,0 +1,126 @@ +#include "stdafx.h" +#include "constants.h" +#include "banword.h" +#include "config.h" + +extern void SendLog(const char * c_pszBuf); // ڿԸ + +CBanwordManager::CBanwordManager() +{ +} + +CBanwordManager::~CBanwordManager() +{ +} + +bool CBanwordManager::Initialize(TBanwordTable * p, WORD wSize) +{ + m_hashmap_words.clear(); + + for (WORD i = 0; i < wSize; ++i, ++p) + m_hashmap_words[p->szWord] = true; + + char szBuf[256]; + snprintf(szBuf, sizeof(szBuf), "Banword reloaded! (total %zu banwords)", m_hashmap_words.size()); + SendLog(szBuf); + return true; +} + +bool CBanwordManager::Find(const char * c_pszString) +{ + return m_hashmap_words.end() != m_hashmap_words.find(c_pszString); +} + +bool CBanwordManager::CheckString(const char * c_pszString, size_t _len) +{ + if (m_hashmap_words.empty()) + return false; + + typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin(); + + while (it != m_hashmap_words.end()) + { + const std::string & r = it->first; + const char * tmp = c_pszString; + ssize_t len = _len; + + while (len > 0) + { + if (is_twobyte(tmp)) + { + if (!strncmp(tmp, r.c_str(), r.size())) + return true; + + tmp += 2; + len -= 2; + } + else + { + if (!strncmp(tmp, r.c_str(), r.size())) + return true; + + ++tmp; + --len; + } + } + + it++; + } + + return false; +} + +void CBanwordManager::ConvertString(char * c_pszString, size_t _len) +{ + typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin(); + + while (it != m_hashmap_words.end()) + { + const std::string & r = it->first; + + char * tmp = c_pszString; + ssize_t len = _len; + + while (len > 0) + { + if (is_twobyte(tmp)) + { + if (!strncmp(tmp, r.c_str(), r.size())) + { + memset(tmp, '*', r.size()); + tmp += r.size(); + len -= r.size(); + } + else + { + tmp += 2; + len -= 2; + } + } + else + { + if (*tmp == '*') + { + ++tmp; + --len; + continue; + } + + if (!strncmp(tmp, r.c_str(), r.size())) + { + memset(tmp, '*', r.size()); + tmp += r.size(); + len -= r.size(); + } + else + { + ++tmp; + --len; + } + } + } + + it++; + } +} + diff --git a/game/src/banword.h b/game/src/banword.h new file mode 100644 index 0000000..644a8d0 --- /dev/null +++ b/game/src/banword.h @@ -0,0 +1,24 @@ + +#ifndef BANWORD_MANAGER_H_ +#define BANWORD_MANAGER_H_ + +#include + +class CBanwordManager : public singleton +{ + public: + CBanwordManager(); + virtual ~CBanwordManager(); + + bool Initialize(TBanwordTable * p, WORD wSize); + bool Find(const char * c_pszString); + bool CheckString(const char * c_pszString, size_t _len); + void ConvertString(char * c_pszString, size_t _len); + + protected: + typedef boost::unordered_map TBanwordHashmap; + TBanwordHashmap m_hashmap_words; +}; + +#endif /* BANWORD_MANAGER_H_ */ + diff --git a/game/src/battle.cpp b/game/src/battle.cpp new file mode 100644 index 0000000..2c7a2b2 --- /dev/null +++ b/game/src/battle.cpp @@ -0,0 +1,823 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc.h" +#include "char.h" +#include "char_manager.h" +#include "battle.h" +#include "item.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "vector.h" +#include "packet.h" +#include "pvp.h" +#include "profiler.h" +#include "guild.h" +#include "affect.h" +#include "unique_item.h" +#include "lua_incl.h" +#include "arena.h" +#include "castle.h" +#include "sectree.h" +#include "ani.h" +#include "locale_service.h" + +int battle_hit(LPCHARACTER ch, LPCHARACTER victim, int & iRetDam); + +bool battle_distance_valid_by_xy(long x, long y, long tx, long ty) +{ + long distance = DISTANCE_APPROX(x - tx, y - ty); + + if (distance > 170) + return false; + + return true; +} + +bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim) +{ + return battle_distance_valid_by_xy(ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY()); +} + +bool timed_event_cancel(LPCHARACTER ch) +{ + if (ch->m_pkTimedEvent) + { + event_cancel(&ch->m_pkTimedEvent); + return true; + } + + /* RECALL_DELAY + ȯ ̰ Ǿ ּ + if (ch->m_pk_RecallEvent) + { + event_cancel(&ch->m_pkRecallEvent); + return true; + } + END_OF_RECALL_DELAY */ + + return false; +} + +bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim) +{ + // ׾ ߴѴ. + if (victim->IsDead()) + return false; + + // ߴ + { + SECTREE *sectree = NULL; + + sectree = ch->GetSectree(); + if (sectree && sectree->IsAttr(ch->GetX(), ch->GetY(), ATTR_BANPK)) + return false; + + sectree = victim->GetSectree(); + if (sectree && sectree->IsAttr(victim->GetX(), victim->GetY(), ATTR_BANPK)) + return false; + } + + + // ׾ ߴѴ. + if (ch->IsStun() || ch->IsDead()) + return false; + + if (ch->IsPC() && victim->IsPC()) + { + CGuild* g1 = ch->GetGuild(); + CGuild* g2 = victim->GetGuild(); + + if (g1 && g2) + { + if (g1->UnderWar(g2->GetID())) + return true; + } + } + + if (IS_CASTLE_MAP(ch->GetMapIndex()) && false==castle_can_attack(ch, victim)) + return false; + + if (CArenaManager::instance().CanAttack(ch, victim) == true) + return true; + + return CPVPManager::instance().CanAttack(ch, victim); +} + +int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim) +{ + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + if (!victim || ch == victim) + return BATTLE_NONE; + + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + if (!battle_is_attackable(ch, victim)) + return BATTLE_NONE; + + if (test_server&&ch->IsPC()) + sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName()); + + // Ÿ üũ + int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY()); + + if (!victim->IsBuilding()) + { + int max = 300; + + if (false == ch->IsPC()) + { + // Ÿ + max = (int) (ch->GetMobAttackRange() * 1.15f); + } + else + { + // PC 밡 melee Ÿ ִ Ÿ + if (false == victim->IsPC() && BATTLE_TYPE_MELEE == victim->GetMobBattleType()) + max = MAX(300, (int) (victim->GetMobAttackRange() * 1.15f)); + } + + if (distance > max) + { + if (test_server) + sys_log(0, "VICTIM_FAR: %s distance: %d max: %d", ch->GetName(), distance, max); + + return BATTLE_NONE; + } + } + + if (timed_event_cancel(ch)) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿ Ǿϴ.")); + + if (timed_event_cancel(victim)) + victim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿ Ǿϴ.")); + + ch->SetPosition(POS_FIGHTING); + ch->SetVictim(victim); + + const PIXEL_POSITION & vpos = victim->GetXYZ(); + ch->SetRotationToXY(vpos.x, vpos.y); + + int dam; + int ret = battle_hit(ch, victim, dam); + return (ret); +} + +// GET_BATTLE_VICTIM NULL ̺Ʈ ĵ Ų. +void battle_end_ex(LPCHARACTER ch) +{ + if (ch->IsPosition(POS_FIGHTING)) + ch->SetPosition(POS_STANDING); +} + +void battle_end(LPCHARACTER ch) +{ + battle_end_ex(ch); +} + +// AG = Attack Grade +// AL = Attack Limit +int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev) +{ + if (iDam < 3) + iDam = number(1, 5); + + //return CALCULATE_DAMAGE_LVDELTA(iAttackerLev, iVictimLev, iDam); + return iDam; +} + +int CalcMagicDamageWithValue(int iDam, LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel()); +} + +int CalcMagicDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + int iDam = 0; + + if (pkAttacker->IsNPC()) + { + iDam = CalcMeleeDamage(pkAttacker, pkVictim, false, false); + } + + iDam += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + + return CalcMagicDamageWithValue(iDam, pkAttacker, pkVictim); +} + +float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating) +{ + int iARSrc; + int iERSrc; + + if (LC_IsYMIR()) // õ + { + iARSrc = MIN(90, pkAttacker->GetPolymorphPoint(POINT_DX)); + iERSrc = MIN(90, pkVictim->GetPolymorphPoint(POINT_DX)); + } + else + { + int attacker_dx = pkAttacker->GetPolymorphPoint(POINT_DX); + int attacker_lv = pkAttacker->GetLevel(); + + int victim_dx = pkVictim->GetPolymorphPoint(POINT_DX); + int victim_lv = pkAttacker->GetLevel(); + + iARSrc = MIN(90, (attacker_dx * 4 + attacker_lv * 2) / 6); + iERSrc = MIN(90, (victim_dx * 4 + victim_lv * 2) / 6); + } + + float fAR = ((float) iARSrc + 210.0f) / 300.0f; // fAR = 0.7 ~ 1.0 + + if (bIgnoreTargetRating) + return fAR; + + // ((Edx * 2 + 20) / (Edx + 110)) * 0.3 + float fER = ((float) (iERSrc * 2 + 5) / (iERSrc + 95)) * 3.0f / 10.0f; + + return fAR - fER; +} + +int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk) +{ + // PvP + if (!pkVictim->IsPC()) + iAtk += pkAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_ATTACK_BONUS); + + // PvP + if (!pkAttacker->IsPC()) + { + int iReduceDamagePct = pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_TRANSFER_DAMAGE); + iAtk = iAtk * (100 + iReduceDamagePct) / 100; + } + + if (pkAttacker->IsNPC() && pkVictim->IsPC()) + { + iAtk = (iAtk * CHARACTER_MANAGER::instance().GetMobDamageRate(pkAttacker)) / 100; + } + + if (pkVictim->IsNPC()) + { + if (pkVictim->IsRaceFlag(RACE_FLAG_ANIMAL)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ANIMAL)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_UNDEAD)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_UNDEAD)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_DEVIL)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DEVIL)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_HUMAN)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_HUMAN)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ORC)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ORC)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_MILGYO)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MILGYO)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_INSECT)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_INSECT)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_FIRE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_FIRE)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_ICE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ICE)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_DESERT)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DESERT)) / 100; + else if (pkVictim->IsRaceFlag(RACE_FLAG_TREE)) + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_TREE)) / 100; + + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MONSTER)) / 100; + } + else if (pkVictim->IsPC()) + { + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_HUMAN)) / 100; + + switch (pkVictim->GetJob()) + { + case JOB_WARRIOR: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_WARRIOR)) / 100; + break; + + case JOB_ASSASSIN: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ASSASSIN)) / 100; + break; + + case JOB_SURA: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SURA)) / 100; + break; + + case JOB_SHAMAN: + iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SHAMAN)) / 100; + break; + } + } + + if (pkAttacker->IsPC() == true) + { + switch (pkAttacker->GetJob()) + { + case JOB_WARRIOR: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_WARRIOR)) / 100; + break; + + case JOB_ASSASSIN: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_ASSASSIN)) / 100; + break; + + case JOB_SURA: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SURA)) / 100; + break; + + case JOB_SHAMAN: + iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SHAMAN)) / 100; + break; + } + } + + //[ mob -> PC ] Ӽ + //2013/01/17 + // Ӽ 30% شϴ ġ . + if (pkAttacker->IsNPC() && pkVictim->IsPC()) + { + if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ELEC)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ELEC)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_FIRE)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_FIRE)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ICE)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ICE)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_WIND)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_WIND)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_EARTH)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_EARTH)) / 10000; + else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_DARK)) + iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_DARK)) / 10000; + } + + + return iAtk; +} + +void Item_GetDamage(LPITEM pkItem, int* pdamMin, int* pdamMax) +{ + *pdamMin = 0; + *pdamMax = 1; + + if (!pkItem) + return; + + switch (pkItem->GetType()) + { + case ITEM_ROD: + case ITEM_PICK: + return; + } + + if (pkItem->GetType() != ITEM_WEAPON) + sys_err("Item_GetDamage - !ITEM_WEAPON vnum=%d, type=%d", pkItem->GetOriginalVnum(), pkItem->GetType()); + + *pdamMin = pkItem->GetValue(3); + *pdamMax = pkItem->GetValue(4); +} + +int CalcMeleeDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreDefense, bool bIgnoreTargetRating) +{ + LPITEM pWeapon = pkAttacker->GetWear(WEAR_WEAPON); + bool bPolymorphed = pkAttacker->IsPolymorphed(); + + if (pWeapon && !(bPolymorphed && !pkAttacker->IsPolyMaintainStat())) + { + if (pWeapon->GetType() != ITEM_WEAPON) + return 0; + + switch (pWeapon->GetSubType()) + { + case WEAPON_SWORD: + case WEAPON_DAGGER: + case WEAPON_TWO_HANDED: + case WEAPON_BELL: + case WEAPON_FAN: + case WEAPON_MOUNT_SPEAR: + break; + + case WEAPON_BOW: + sys_err("CalcMeleeDamage should not handle bows (name: %s)", pkAttacker->GetName()); + return 0; + + default: + return 0; + } + } + + int iDam = 0; + float fAR = CalcAttackRating(pkAttacker, pkVictim, bIgnoreTargetRating); + int iDamMin = 0, iDamMax = 0; + + // TESTSERVER_SHOW_ATTACKINFO + int DEBUG_iDamCur = 0; + int DEBUG_iDamBonus = 0; + // END_OF_TESTSERVER_SHOW_ATTACKINFO + + if (bPolymorphed && !pkAttacker->IsPolyMaintainStat()) + { + // MONKEY_ROD_ATTACK_BUG_FIX + Item_GetDamage(pWeapon, &iDamMin, &iDamMax); + // END_OF_MONKEY_ROD_ATTACK_BUG_FIX + + DWORD dwMobVnum = pkAttacker->GetPolymorphVnum(); + const CMob * pMob = CMobManager::instance().Get(dwMobVnum); + + if (pMob) + { + int iPower = pkAttacker->GetPolymorphPower(); + iDamMin += pMob->m_table.dwDamageRange[0] * iPower / 100; + iDamMax += pMob->m_table.dwDamageRange[1] * iPower / 100; + } + } + else if (pWeapon) + { + // MONKEY_ROD_ATTACK_BUG_FIX + Item_GetDamage(pWeapon, &iDamMin, &iDamMax); + // END_OF_MONKEY_ROD_ATTACK_BUG_FIX + } + else if (pkAttacker->IsNPC()) + { + iDamMin = pkAttacker->GetMobDamageMin(); + iDamMax = pkAttacker->GetMobDamageMax(); + } + + iDam = number(iDamMin, iDamMax) * 2; + + // TESTSERVER_SHOW_ATTACKINFO + DEBUG_iDamCur = iDam; + // END_OF_TESTSERVER_SHOW_ATTACKINFO + // + int iAtk = 0; + + // level must be ignored when multiply by fAR, so subtract it before calculation. + iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2); + iAtk = (int) (iAtk * fAR); + iAtk += pkAttacker->GetLevel() * 2; // and add again + + if (pWeapon) + { + iAtk += pWeapon->GetValue(5) * 2; + + // 2004.11.12.myevan.TESTSERVER_SHOW_ATTACKINFO + DEBUG_iDamBonus = pWeapon->GetValue(5) * 2; + /////////////////////////////////////////////// + } + + iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); // party attacker role bonus + iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100); + + iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk); + + int iDef = 0; + + if (!bIgnoreDefense) + { + iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkVictim->GetPoint(POINT_DEF_BONUS)) / 100); + + if (!pkAttacker->IsPC()) + iDef += pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_DEFENSE_BONUS); + } + + if (pkAttacker->IsNPC()) + iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply()); + + iDam = MAX(0, iAtk - iDef); + + if (test_server) + { + int DEBUG_iLV = pkAttacker->GetLevel()*2; + int DEBUG_iST = int((pkAttacker->GetPoint(POINT_ATT_GRADE) - DEBUG_iLV) * fAR); + int DEBUG_iPT = pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + int DEBUG_iWP = 0; + int DEBUG_iPureAtk = 0; + int DEBUG_iPureDam = 0; + char szRB[32] = ""; + char szGradeAtkBonus[32] = ""; + + DEBUG_iWP = int(DEBUG_iDamCur * fAR); + DEBUG_iPureAtk = DEBUG_iLV + DEBUG_iST + DEBUG_iWP+DEBUG_iDamBonus; + DEBUG_iPureDam = iAtk - iDef; + + if (pkAttacker->IsNPC()) + { + snprintf(szGradeAtkBonus, sizeof(szGradeAtkBonus), "=%d*%.1f", DEBUG_iPureAtk, pkAttacker->GetMobDamageMultiply()); + DEBUG_iPureAtk = int(DEBUG_iPureAtk * pkAttacker->GetMobDamageMultiply()); + } + + if (DEBUG_iDamBonus != 0) + snprintf(szRB, sizeof(szRB), "+RB(%d)", DEBUG_iDamBonus); + + char szPT[32] = ""; + + if (DEBUG_iPT != 0) + snprintf(szPT, sizeof(szPT), ", PT=%d", DEBUG_iPT); + + char szUnknownAtk[32] = ""; + + if (iAtk != DEBUG_iPureAtk) + snprintf(szUnknownAtk, sizeof(szUnknownAtk), "+?(%d)", iAtk-DEBUG_iPureAtk); + + char szUnknownDam[32] = ""; + + if (iDam != DEBUG_iPureDam) + snprintf(szUnknownDam, sizeof(szUnknownDam), "+?(%d)", iDam-DEBUG_iPureDam); + + char szMeleeAttack[128]; + + snprintf(szMeleeAttack, sizeof(szMeleeAttack), + "%s(%d)-%s(%d)=%d%s, ATK=LV(%d)+ST(%d)+WP(%d)%s%s%s, AR=%.3g%s", + pkAttacker->GetName(), + iAtk, + pkVictim->GetName(), + iDef, + iDam, + szUnknownDam, + DEBUG_iLV, + DEBUG_iST, + DEBUG_iWP, + szRB, + szUnknownAtk, + szGradeAtkBonus, + fAR, + szPT); + + pkAttacker->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack); + pkVictim->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack); + } + + return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel()); +} + +int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense) +{ + if (!pkBow || pkBow->GetType() != ITEM_WEAPON || pkBow->GetSubType() != WEAPON_BOW) + return 0; + + if (!pkArrow) + return 0; + + // Ÿġ + int iDist = (int) (DISTANCE_SQRT(pkAttacker->GetX() - pkVictim->GetX(), pkAttacker->GetY() - pkVictim->GetY())); + //int iGap = (iDist / 100) - 5 - pkBow->GetValue(5) - pkAttacker->GetPoint(POINT_BOW_DISTANCE); + int iGap = (iDist / 100) - 5 - pkAttacker->GetPoint(POINT_BOW_DISTANCE); + int iPercent = 100 - (iGap * 5); + + if (iPercent <= 0) + return 0; + else if (iPercent > 100) + iPercent = 100; + + int iDam = 0; + + float fAR = CalcAttackRating(pkAttacker, pkVictim, false); + iDam = number(pkBow->GetValue(3), pkBow->GetValue(4)) * 2 + pkArrow->GetValue(3); + int iAtk; + + // level must be ignored when multiply by fAR, so subtract it before calculation. + iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2); + iAtk = (int) (iAtk * fAR); + iAtk += pkAttacker->GetLevel() * 2; // and add again + + // Refine Grade + iAtk += pkBow->GetValue(5) * 2; + + iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); + iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100); + + iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk); + + int iDef = 0; + + if (!bIgnoreDefense) + iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkAttacker->GetPoint(POINT_DEF_BONUS)) / 100); + + if (pkAttacker->IsNPC()) + iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply()); + + iDam = MAX(0, iAtk - iDef); + + int iPureDam = iDam; + + iPureDam = (iPureDam * iPercent) / 100; + + if (test_server) + { + pkAttacker->ChatPacket(CHAT_TYPE_INFO, "ARROW %s -> %s, DAM %d DIST %d GAP %d %% %d", + pkAttacker->GetName(), + pkVictim->GetName(), + iPureDam, + iDist, iGap, iPercent); + } + + return iPureDam; + //return iDam; +} + + +void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim) +{ + // ƯϹǷ Ư ó + if (pkAttacker->GetPoint(POINT_POISON_PCT) && !pkVictim->IsAffectFlag(AFF_POISON)) + { + if (number(1, 100) <= pkAttacker->GetPoint(POINT_POISON_PCT)) + pkVictim->AttackedByPoison(pkAttacker); + } + + int iStunDuration = 2; + if (pkAttacker->IsPC() && !pkVictim->IsPC()) + iStunDuration = 4; + + AttackAffect(pkAttacker, pkVictim, POINT_STUN_PCT, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iStunDuration, "STUN"); + AttackAffect(pkAttacker, pkVictim, POINT_SLOW_PCT, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 20, "SLOW"); +} + +int battle_hit(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int & iRetDam) +{ + //PROF_UNIT puHit("Hit"); + if (test_server) + sys_log(0, "battle_hit : [%s] attack to [%s] : dam :%d type :%d", pkAttacker->GetName(), pkVictim->GetName(), iRetDam); + + int iDam = CalcMeleeDamage(pkAttacker, pkVictim); + + if (iDam <= 0) + return (BATTLE_DAMAGE); + + NormalAttackAffect(pkAttacker, pkVictim); + + // + //iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST)) / 100; + LPITEM pkWeapon = pkAttacker->GetWear(WEAR_WEAPON); + + if (pkWeapon) + switch (pkWeapon->GetSubType()) + { + case WEAPON_SWORD: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_SWORD)) / 100; + break; + + case WEAPON_TWO_HANDED: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_TWOHAND)) / 100; + break; + + case WEAPON_DAGGER: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_DAGGER)) / 100; + break; + + case WEAPON_BELL: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BELL)) / 100; + break; + + case WEAPON_FAN: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_FAN)) / 100; + break; + + case WEAPON_BOW: + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100; + break; + } + + + // . (2011 2 հŹ̿Ը .) + float attMul = pkAttacker->GetAttMul(); + float tempIDam = iDam; + iDam = attMul * tempIDam + 0.5f; + + iRetDam = iDam; + + //PROF_UNIT puDam("Dam"); + if (pkVictim->Damage(pkAttacker, iDam, DAMAGE_TYPE_NORMAL)) + return (BATTLE_DEAD); + + return (BATTLE_DAMAGE); +} + +DWORD GET_ATTACK_SPEED(LPCHARACTER ch) +{ + if (NULL == ch) + return 1000; + + LPITEM item = ch->GetWear(WEAR_WEAPON); + DWORD default_bonus = SPEEDHACK_LIMIT_BONUS * 3; // θ (⺻ 80) (Ϲ speed hack ɸ *3 ߰. 2013.09.11 CYH) + DWORD riding_bonus = 0; + + if (ch->IsRiding()) + { + // ߰ 50 + riding_bonus = 50; + } + + DWORD ani_speed = ani_attack_speed(ch); + DWORD real_speed = (ani_speed * 100) / (default_bonus + ch->GetPoint(POINT_ATT_SPEED) + riding_bonus); + + // ܰ 2 + if (item && item->GetSubType() == WEAPON_DAGGER) + real_speed /= 2; + + return real_speed; + +} + +void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + if (NULL == ch || NULL == victim) + return; + + if (!ch->IsPC()) + return; + + ch->m_kAttackLog.dwVID = victim->GetVID(); + ch->m_kAttackLog.dwTime = current_time; +} + +void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + if (NULL == ch || NULL == victim) + return; + + if (!ch->IsPC()) + return; + + victim->m_AttackedLog.dwPID = ch->GetPlayerID(); + victim->m_AttackedLog.dwAttackedTime= current_time; +} + +bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) +{ + // 2013 09 11 CYH debugging log + /*sys_log(0, "%s attack test log! time (delta, limit)=(%u, %u). ch->m_kAttackLog.dwvID(%u) victim->GetVID(%u)", + ch->GetName(), + current_time - ch->m_kAttackLog.dwTime, + GET_ATTACK_SPEED(ch), + ch->m_kAttackLog.dwVID, + victim->GetVID() + ); + + sys_log(0, "%s attack test log! time (delta, limit)=(%u, %u). victim->m_AttackedLog.dwPID(%u) ch->GetPlayerID(%u)", + ch->GetName(), + current_time - victim->m_AttackedLog.dwAttackedTime, + GET_ATTACK_SPEED(ch), + victim->m_AttackedLog.dwPID, + ch->GetPlayerID() + );*/ + + if (ch->m_kAttackLog.dwVID == victim->GetVID()) + { + if (current_time - ch->m_kAttackLog.dwTime < GET_ATTACK_SPEED(ch)) + { + INCREASE_SPEED_HACK_COUNT(ch); + + if (test_server) + { + sys_log(0, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", + ch->GetName(), + current_time - ch->m_kAttackLog.dwTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", + ch->GetName(), + current_time - ch->m_kAttackLog.dwTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + } + + SET_ATTACK_TIME(ch, victim, current_time); + SET_ATTACKED_TIME(ch, victim, current_time); + return true; + } + } + + SET_ATTACK_TIME(ch, victim, current_time); + + if (victim->m_AttackedLog.dwPID == ch->GetPlayerID()) + { + if (current_time - victim->m_AttackedLog.dwAttackedTime < GET_ATTACK_SPEED(ch)) + { + INCREASE_SPEED_HACK_COUNT(ch); + + if (test_server) + { + sys_log(0, "%s Attack Speed HACK! time (delta, limit)=(%u, %u), hack_count = %d", + ch->GetName(), + current_time - victim->m_AttackedLog.dwAttackedTime, + GET_ATTACK_SPEED(ch), + ch->m_speed_hack_count); + + ch->ChatPacket(CHAT_TYPE_INFO, "Attack Speed Hack(%s), (delta, limit)=(%u, %u)", + ch->GetName(), + current_time - victim->m_AttackedLog.dwAttackedTime, + GET_ATTACK_SPEED(ch)); + } + + SET_ATTACKED_TIME(ch, victim, current_time); + return true; + } + } + + SET_ATTACKED_TIME(ch, victim, current_time); + return false; +} + + diff --git a/game/src/battle.h b/game/src/battle.h new file mode 100644 index 0000000..84c1490 --- /dev/null +++ b/game/src/battle.h @@ -0,0 +1,100 @@ +#ifndef __INC_METIN_II_GAME_BATTLE_H__ +#define __INC_METIN_II_GAME_BATTLE_H__ + +#include "char.h" + +enum EBattleTypes // +{ + BATTLE_NONE, + BATTLE_DAMAGE, + BATTLE_DEFENSE, + BATTLE_DEAD +}; + +extern int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk); +extern int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev); +extern int CalcMeleeDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim, bool bIgnoreDefense = false, bool bIgnoreTargetRating = false); +extern int CalcMagicDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim); +extern int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense = false); +extern float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating = false); + +extern bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim); +extern int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim); +extern void battle_end(LPCHARACTER ch); + +extern bool battle_distance_valid_by_xy(long x, long y, long tx, long ty); +extern bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim); +extern int battle_count_attackers(LPCHARACTER ch); + +extern void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim); + +// Ư +inline void AttackAffect(LPCHARACTER pkAttacker, + LPCHARACTER pkVictim, + BYTE att_point, + DWORD immune_flag, + DWORD affect_idx, + BYTE affect_point, + long affect_amount, + DWORD affect_flag, + int time, + const char* name) +{ + if (pkAttacker->GetPoint(att_point) && !pkVictim->IsAffectFlag(affect_flag)) + { + if (number(1, 100) <= pkAttacker->GetPoint(att_point) && !pkVictim->IsImmune(immune_flag)) + { + pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true); + + if (test_server) + { + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) SUCCESS", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point)); + } + } + else if (test_server) + { + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) FAIL", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point)); + } + } +} + +inline void SkillAttackAffect(LPCHARACTER pkVictim, + int success_pct, + DWORD immune_flag, + DWORD affect_idx, + BYTE affect_point, + long affect_amount, + DWORD affect_flag, + int time, + const char* name) +{ + if (success_pct && !pkVictim->IsAffectFlag(affect_flag)) + { + if (number(1, 1000) <= success_pct && !pkVictim->IsImmune(immune_flag)) + { + pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true); + + // SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX + if (test_server) + pkVictim->ChatPacket(CHAT_TYPE_PARTY, + "%s(%d%%) -> %s SUCCESS", name, success_pct, name); + // END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX + } + else if (test_server) + { + // SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX + pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s(%d%%) -> %s FAIL", name, success_pct, name); + // END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX + } + } +} + + +#define GET_SPEED_HACK_COUNT(ch) ((ch)->m_speed_hack_count) +#define INCREASE_SPEED_HACK_COUNT(ch) (++GET_SPEED_HACK_COUNT(ch)) +DWORD GET_ATTACK_SPEED(LPCHARACTER ch); +void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); +void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); +bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time); + +#endif diff --git a/game/src/belt_inventory_helper.h b/game/src/belt_inventory_helper.h new file mode 100644 index 0000000..da1bd67 --- /dev/null +++ b/game/src/belt_inventory_helper.h @@ -0,0 +1,108 @@ +#ifndef __HEADER_BELT_INVENTORY_HELPER__ +#define __HEADER_BELT_INVENTORY_HELPER__ + +#include "char.h" +#include "item.h" + +class CBeltInventoryHelper +{ +public: + typedef BYTE TGradeUnit; + + static TGradeUnit GetBeltGradeByRefineLevel(int level) + { + static TGradeUnit beltGradeByLevelTable[] = + { + 0, // Ʈ+0 + 1, // +1 + 1, // +2 + 2, // +3 + 2, // +4, + 3, // +5 + 4, // +6, + 5, // +7, + 6, // +8, + 7, // +9 + }; + + if (level >= _countof(beltGradeByLevelTable)) + { + sys_err("CBeltInventoryHelper::GetBeltGradeByRefineLevel - Overflow level (%d", level); + return 0; + } + + return beltGradeByLevelTable[level]; + } + + // Ʈ ,  ̿ ִ + static const TGradeUnit* GetAvailableRuleTableByGrade() + { + /** + Ʈ +0 ~ +9 , 7ܰ еǾ κ丮 Ȱ ȭ ȴ. + Ʈ Ʒ ׸ . >= Ȱ ̸ . + (, 0̸ Ұ, ȣ ڴ ) + + 2(1) 4(2) 6(4) 8(6) + 5(3) 5(3) 6(4) 8(6) + 7(5) 7(5) 7(5) 8(6) + 9(7) 9(7) 9(7) 9(7) + + Ʈ κ丮 ũ 4x4 (16ĭ) + */ + + static TGradeUnit availableRuleByGrade[BELT_INVENTORY_SLOT_COUNT] = { + 1, 2, 4, 6, + 3, 3, 4, 6, + 5, 5, 5, 6, + 7, 7, 7, 7 + }; + + return availableRuleByGrade; + } + + static bool IsAvailableCell(WORD cell, int beltGrade /*int beltLevel*/) + { + // ȹ ٲ.. Ƴ... + //const TGradeUnit beltGrade = GetBeltGradeByRefineLevel(beltLevel); + const TGradeUnit* ruleTable = GetAvailableRuleTableByGrade(); + + return ruleTable[cell] <= beltGrade; + } + + /// pc Ʈ κ丮 ϳ ϴ ˻ϴ Լ. + static bool IsExistItemInBeltInventory(LPCHARACTER pc) + { + for (WORD i = BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END; ++i) + { + LPITEM beltInventoryItem = pc->GetInventoryItem(i); + + if (NULL != beltInventoryItem) + return true; + } + + return false; + } + + /// item Ʈ κ丮  ִ Ÿ ˻ϴ Լ. ( Ģ ȹڰ ) + static bool CanMoveIntoBeltInventory(LPITEM item) + { + bool canMove = false; + + if (item->GetType() == ITEM_USE) + { + switch (item->GetSubType()) + { + case USE_POTION: + case USE_POTION_NODELAY: + case USE_ABILITY_UP: + canMove = true; + break; + } + } + + return canMove; + } + +}; + +#endif //__HEADER_BELT_INVENTORY_HELPER__ \ No newline at end of file diff --git a/game/src/blend_item.cpp b/game/src/blend_item.cpp new file mode 100644 index 0000000..cd623c6 --- /dev/null +++ b/game/src/blend_item.cpp @@ -0,0 +1,249 @@ +/********************************************************************* + * date : 2007.02.24 + * file : blend_item.cpp + * author : mhh + * description : + */ + +#define _blend_item_cpp_ + +#include "stdafx.h" +#include "constants.h" +#include "log.h" +#include "dev_log.h" +#include "locale_service.h" +#include "item.h" +#include "blend_item.h" + +#define DO_ALL_BLEND_INFO(iter) for (iter=s_blend_info.begin(); iter!=s_blend_info.end(); ++iter) + + +struct BLEND_ITEM_INFO +{ + DWORD item_vnum; + int apply_type; + int apply_value[MAX_BLEND_ITEM_VALUE]; + int apply_duration[MAX_BLEND_ITEM_VALUE]; +}; + + +typedef std::vector T_BLEND_ITEM_INFO; +T_BLEND_ITEM_INFO s_blend_info; + +bool Blend_Item_init() +{ + BLEND_ITEM_INFO *blend_item_info = NULL; + T_BLEND_ITEM_INFO::iterator iter; + char file_name[256]; + snprintf (file_name, sizeof(file_name), "%s/blend.txt", LocaleService_GetBasePath().c_str()); + + sys_log(0, "Blend_Item_init %s ", file_name); + + DO_ALL_BLEND_INFO(iter) + { + blend_item_info = *iter; + M2_DELETE(blend_item_info); + } + s_blend_info.clear(); + + if (false==Blend_Item_load(file_name)) + { + sys_err(" fail"); + return false; + } + return true; +} + +bool Blend_Item_load(char *file) +{ + FILE *fp; + char one_line[256]; + const char *delim = " \t\r\n"; + char *v; + + BLEND_ITEM_INFO *blend_item_info; + + if (0==file || 0==file[0]) + return false; + + if ((fp = fopen(file, "r"))==0) + return false; + + while (fgets(one_line, 256, fp)) + { + if (one_line[0]=='#') + continue; + + const char* token_string = strtok(one_line, delim); + + if (NULL==token_string) + continue; + + TOKEN("section") + { + blend_item_info = M2_NEW BLEND_ITEM_INFO; + memset(blend_item_info, 0x00, sizeof(BLEND_ITEM_INFO)); + } + else TOKEN("item_vnum") + { + v = strtok(NULL, delim); + + if (NULL==v) + { + fclose(fp); + return false; + } + + str_to_number(blend_item_info->item_vnum, v); + } + else TOKEN("apply_type") + { + v = strtok(NULL, delim); + + if (NULL==v) + { + fclose(fp); + return false; + } + + if (0 == (blend_item_info->apply_type = FN_get_apply_type(v))) + { + sys_err ("Invalid apply_type(%s)", v); + return false; + } + } + else TOKEN("apply_value") + { + for (int i=0; iapply_value[i], v); + } + } + else TOKEN("apply_duration") + { + for (int i=0; iapply_duration[i], v); + } + } + else TOKEN("end") + { + s_blend_info.push_back(blend_item_info); + } + } + + fclose(fp); + + return true; +} + +static int FN_random_index() +{ + int percent = number(1,100); + + if (percent<=10) // level 1 :10% + return 0; + else if (percent<=30) // level 2 : 20% + return 1; + else if (percent<=70) // level 3 : 40% + return 2; + else if (percent<=90) // level 4 : 20% + return 3; + else + return 4; // level 5 : 10% + + return 0; +} + +// ȯ Ȯ ̺ +// blend.txt Ȯ ޵ ġ ϰ +// 󺰷 item proto ϹǷ, +// ȥ ־ ̷ ߰Ѵ. +// by rtsummit + +static int FN_ECS_random_index() +{ + int percent = number(1,100); + + if (percent<=5) // level 1 : 5% + return 0; + else if (percent<=15) // level 2 : 10% + return 1; + else if (percent<=60) // level 3 : 45% + return 2; + else if (percent<=85) // level 4 : 25% + return 3; + else + return 4; // level 5 : 15% + + return 0; +} + + +bool Blend_Item_set_value(LPITEM item) +{ + BLEND_ITEM_INFO *blend_info; + T_BLEND_ITEM_INFO::iterator iter; + + DO_ALL_BLEND_INFO(iter) + { + blend_info = *iter; + if (blend_info->item_vnum == item->GetVnum()) + { + int apply_type; + int apply_value; + int apply_duration; + + if (item->GetVnum() == 51002) + { + apply_type = blend_info->apply_type; + apply_value = blend_info->apply_value [FN_ECS_random_index()]; + apply_duration = blend_info->apply_duration [FN_ECS_random_index()]; + } + else + { + apply_type = blend_info->apply_type; + apply_value = blend_info->apply_value [FN_random_index()]; + apply_duration = blend_info->apply_duration [FN_random_index()]; + } + sys_log (0, "blend_item : type : %d, value : %d, du : %d", apply_type, apply_value, apply_duration); + item->SetSocket(0, apply_type); + item->SetSocket(1, apply_value); + item->SetSocket(2, apply_duration); + return true; + } + + } + return false; +} + +bool Blend_Item_find(DWORD item_vnum) +{ + BLEND_ITEM_INFO *blend_info; + T_BLEND_ITEM_INFO::iterator iter; + + DO_ALL_BLEND_INFO(iter) + { + blend_info = *iter; + if (blend_info->item_vnum == item_vnum) + return true; + } + return false; +} + diff --git a/game/src/blend_item.h b/game/src/blend_item.h new file mode 100644 index 0000000..4190df0 --- /dev/null +++ b/game/src/blend_item.h @@ -0,0 +1,20 @@ +/********************************************************************* + * date : 2007.02.24 + * file : blend_item.h + * author : mhh + * description : + */ + +#ifndef _blend_item_h_ +#define _blend_item_h_ + +#define MAX_BLEND_ITEM_VALUE 5 + + +bool Blend_Item_init(); +bool Blend_Item_load(char *file); +bool Blend_Item_set_value(LPITEM item); +bool Blend_Item_find(DWORD item_vnum); + +#endif /* _blend_item_h_ */ + diff --git a/game/src/block_country.cpp b/game/src/block_country.cpp new file mode 100644 index 0000000..5f59fe0 --- /dev/null +++ b/game/src/block_country.cpp @@ -0,0 +1,151 @@ +/********************************************************************* + * date : 2007.05.31 + * file : block_country.cpp + * author : mhh + * description : + */ + +#define _block_country_cpp_ + +#include "stdafx.h" +#include "constants.h" +#include "block_country.h" +#include "dev_log.h" + +#define DEC_ITER(iter) std::vector::iterator iter +#define DO_ALL_BLOCKED_IP(iter) for ((iter)=s_blocked_ip.begin(); (iter)!=s_blocked_ip.end(); ++(iter)) + +#define DEC_EXCEPTION_ITER(iter) std::set::iterator iter + + +typedef struct { + DWORD ip_from; + DWORD ip_to; +} T_BLOCK_IP; + +//-------------------------------------- +// static variables +std::vector s_blocked_ip; +std::set s_block_exception; +// static variables +//-------------------------------------- + + + +//-------------------------------------- +// static functions +static void __add_block_exception(const char *login) +{ +dev_log(LOG_DEB0, "BLOCK_EXCEPTION_ADD : %s", login); + + DEC_EXCEPTION_ITER(iter); + std::string string_login(login); + + iter = s_block_exception.find(string_login); + + // can not find + if (iter==s_block_exception.end()) + { + s_block_exception.insert(string_login); + } +} + +static void __del_block_exception(const char *login) +{ +dev_log(LOG_DEB0, "BLOCK_EXCEPTION_DEL : %s", login); + + DEC_EXCEPTION_ITER(iter); + std::string string_login(login); + + iter = s_block_exception.find(string_login); + + // ok : find + if (iter!=s_block_exception.end()) + { + s_block_exception.erase(iter); + } +} +// static functions +//-------------------------------------- + + + + +void add_blocked_country_ip(TPacketBlockCountryIp *data) +{ + T_BLOCK_IP *block_ip = M2_NEW T_BLOCK_IP; + + block_ip->ip_from = data->ip_from; + block_ip->ip_to = data->ip_to; + + s_blocked_ip.push_back(block_ip); + + dev_log(LOG_DEB0, "BLOCKED_IP = %u - %u", block_ip->ip_from, block_ip->ip_to); +} + + +void block_exception(TPacketBlockException *data) +{ + if (NULL==data) return; + + if (BLOCK_EXCEPTION_CMD_ADD!=data->cmd && BLOCK_EXCEPTION_CMD_DEL!=data->cmd) + return; + + + switch (data->cmd) + { + case BLOCK_EXCEPTION_CMD_ADD: + __add_block_exception(data->login); + break; + case BLOCK_EXCEPTION_CMD_DEL: + __del_block_exception(data->login); + break; + } +} + +bool is_blocked_country_ip(const char *user_ip) +{ + DEC_ITER(iter); + T_BLOCK_IP *block_ip; + DWORD ip_number; + struct in_addr st_addr; + +#ifndef __WIN32__ + if (0 == inet_aton(user_ip, &st_addr)) +#else + unsigned long in_address; + in_address = inet_addr(user_ip); + st_addr.s_addr = in_address; + if (INADDR_NONE == in_address) +#endif + { + dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : YES", user_ip); + return true; // ǰ ϴ ϴ ó + } + ip_number = htonl(st_addr.s_addr); + + DO_ALL_BLOCKED_IP(iter) + { + block_ip = *iter; + if ( block_ip->ip_from <= ip_number && ip_number <= block_ip->ip_to ) + { + dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : YES", user_ip); + return true; + } + } + + dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : NO", user_ip); + return false; +} + +bool is_block_exception(const char *login) +{ + std::string login_string(login); + std::set::iterator iter; + + iter = s_block_exception.find(login_string); + if (iter != s_block_exception.end()) + return true; + + return false; +} diff --git a/game/src/block_country.h b/game/src/block_country.h new file mode 100644 index 0000000..fe69e46 --- /dev/null +++ b/game/src/block_country.h @@ -0,0 +1,18 @@ +/********************************************************************* + * date : 2007.05.31 + * file : block_country.h + * author : mhh + * description : + */ + +#ifndef _block_country_h_ +#define _block_country_h_ + + +void add_blocked_country_ip(TPacketBlockCountryIp *data); +void block_exception(TPacketBlockException *data); +bool is_blocked_country_ip(const char *user_ip); +bool is_block_exception(const char *login); + +#endif // _block_country_h_ + diff --git a/game/src/buff_on_attributes.cpp b/game/src/buff_on_attributes.cpp new file mode 100644 index 0000000..64366a4 --- /dev/null +++ b/game/src/buff_on_attributes.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "../../common/tables.h" +#include "item.h" +#include "char.h" +#include "buff_on_attributes.h" +#include + +CBuffOnAttributes::CBuffOnAttributes(LPCHARACTER pOwner, BYTE point_type, std::vector * p_vec_buff_wear_targets) +: m_pBuffOwner(pOwner), m_bPointType(point_type), m_p_vec_buff_wear_targets(p_vec_buff_wear_targets) +{ + Initialize(); +} + +CBuffOnAttributes::~CBuffOnAttributes() +{ + Off(); +} + +void CBuffOnAttributes::Initialize() +{ + m_bBuffValue = 0; + m_map_additional_attrs.clear(); +} + +void CBuffOnAttributes::RemoveBuffFromItem(LPITEM pItem) +{ + if (0 == m_bBuffValue) + return ; + if (NULL != pItem) + { + if (pItem->GetCell() < INVENTORY_MAX_NUM) + return; + std::vector ::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM); + if (m_p_vec_buff_wear_targets->end() == it) + return; + + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + // m_map_additional_attrs ش attribute type ϰ, + // (m_bBuffValue)%ŭ ȿ + if (it != m_map_additional_attrs.end()) + { + int& sum_of_attr_value = it->second; + int old_value = sum_of_attr_value * m_bBuffValue / 100; + int new_value = (sum_of_attr_value - attr.sValue) * m_bBuffValue / 100; + m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value); + sum_of_attr_value -= attr.sValue; + } + else + { + sys_err ("Buff ERROR(type %d). This item(%d) attr_type(%d) was not in buff pool", m_bPointType, pItem->GetVnum(), attr.bType); + return; + } + } + } +} + +void CBuffOnAttributes::AddBuffFromItem(LPITEM pItem) +{ + if (0 == m_bBuffValue) + return ; + if (NULL != pItem) + { + if (pItem->GetCell() < INVENTORY_MAX_NUM) + return; + std::vector ::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM); + if (m_p_vec_buff_wear_targets->end() == it) + return; + + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + + // m_map_additional_attrs ش attribute type ٸ ߰. + // ߰ (m_bBuffValue)%ŭ ȿ ߰ + if (it == m_map_additional_attrs.end()) + { + m_pBuffOwner->ApplyPoint(attr.bType, attr.sValue * m_bBuffValue / 100); + m_map_additional_attrs.insert(TMapAttr::value_type(attr.bType, attr.sValue)); + } + // m_map_additional_attrs ش attribute type ִٸ, Ű, + // (m_bBuffValue)%ŭ ȿ ߰ + else + { + int& sum_of_attr_value = it->second; + int old_value = sum_of_attr_value * m_bBuffValue / 100; + int new_value = (sum_of_attr_value + attr.sValue) * m_bBuffValue / 100; + m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value); + sum_of_attr_value += attr.sValue; + } + } + } +} + +void CBuffOnAttributes::ChangeBuffValue(BYTE bNewValue) +{ + if (0 == m_bBuffValue) + On(bNewValue); + else if (0 == bNewValue) + Off(); + else + { + // , m_map_additional_attrs (m_bBuffValue)%ŭ  ־Ƿ, + // (bNewValue)%ŭ . + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + int& sum_of_attr_value = it->second; + int old_value = sum_of_attr_value * m_bBuffValue / 100; + int new_value = sum_of_attr_value * bNewValue / 100; + + m_pBuffOwner->ApplyPoint(it->first, -sum_of_attr_value * m_bBuffValue / 100); + } + m_bBuffValue = bNewValue; + } +} + +void CBuffOnAttributes::GiveAllAttributes() +{ + if (0 == m_bBuffValue) + return; + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + BYTE apply_type = it->first; + int apply_value = it->second * m_bBuffValue / 100; + + m_pBuffOwner->ApplyPoint(apply_type, apply_value); + } +} + +bool CBuffOnAttributes::On(BYTE bValue) +{ + if (0 != m_bBuffValue || 0 == bValue) + return false; + + int n = m_p_vec_buff_wear_targets->size(); + m_map_additional_attrs.clear(); + for (int i = 0; i < n; i++) + { + LPITEM pItem = m_pBuffOwner->GetWear(m_p_vec_buff_wear_targets->at(i)); + if (NULL != pItem) + { + int m = pItem->GetAttributeCount(); + for (int j = 0; j < m; j++) + { + TPlayerItemAttribute attr = pItem->GetAttribute(j); + TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType); + if (it != m_map_additional_attrs.end()) + { + it->second += attr.sValue; + } + else + { + m_map_additional_attrs.insert(TMapAttr::value_type(attr.bType, attr.sValue)); + } + } + } + } + + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + m_pBuffOwner->ApplyPoint(it->first, it->second * bValue / 100); + } + + m_bBuffValue = bValue; + + return true; +} + +void CBuffOnAttributes::Off() +{ + if (0 == m_bBuffValue) + return ; + + for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++) + { + m_pBuffOwner->ApplyPoint(it->first, -it->second * m_bBuffValue / 100); + } + Initialize(); +} diff --git a/game/src/buff_on_attributes.h b/game/src/buff_on_attributes.h new file mode 100644 index 0000000..ab88060 --- /dev/null +++ b/game/src/buff_on_attributes.h @@ -0,0 +1,42 @@ +#ifndef __METIN2_BUFF_ON_ATTRIBUTES_H +#define __METIN2_BUFF_ON_ATTRIBUTES_H + +class CHARACTER; + +class CBuffOnAttributes +{ +public: + CBuffOnAttributes(LPCHARACTER pOwner, BYTE m_point_type, std::vector * vec_buff_targets); + ~CBuffOnAttributes(); + + // ̸鼭, m_p_vec_buff_wear_targets شϴ , ش ȿ . + void RemoveBuffFromItem(LPITEM pItem); + // m_p_vec_buff_wear_targets شϴ , ش attribute ȿ ߰. + void AddBuffFromItem(LPITEM pItem); + // m_bBuffValue ٲٰ, ٲ. + void ChangeBuffValue(BYTE bNewValue); + // CHRACTRE::ComputePoints Ӽġ ʱȭϰ ٽ ϹǷ, + // Ӽġ owner . + void GiveAllAttributes(); + + // m_p_vec_buff_wear_targets شϴ attribute type ջϰ, + // attribute (m_bBuffValue)% ŭ . + bool On(BYTE bValue); + // , ʱȭ + void Off(); + + void Initialize(); +private: + LPCHARACTER m_pBuffOwner; + BYTE m_bPointType; + BYTE m_bBuffValue; + std::vector * m_p_vec_buff_wear_targets; + + // apply_type, apply_value + typedef std::map TMapAttr; + // m_p_vec_buff_wear_targets شϴ attribute type ջϿ . + TMapAttr m_map_additional_attrs; + +}; + +#endif diff --git a/game/src/buffer_manager.cpp b/game/src/buffer_manager.cpp new file mode 100644 index 0000000..801720f --- /dev/null +++ b/game/src/buffer_manager.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "buffer_manager.h" + +TEMP_BUFFER::TEMP_BUFFER(int Size, bool bForceDelete) +{ + forceDelete = bForceDelete; + + if (forceDelete) + Size = MAX(Size, 1024 * 128); + + buf = buffer_new(Size); +} + +TEMP_BUFFER::~TEMP_BUFFER() +{ + buffer_delete(buf); +} + +const void * TEMP_BUFFER::read_peek() +{ + return (buffer_read_peek(buf)); +} + +void TEMP_BUFFER::write(const void * data, int size) +{ + buffer_write(buf, data, size); +} + +int TEMP_BUFFER::size() +{ + return buffer_size(buf); +} + +void TEMP_BUFFER::reset() +{ + buffer_reset(buf); +} + diff --git a/game/src/buffer_manager.h b/game/src/buffer_manager.h new file mode 100644 index 0000000..65550b7 --- /dev/null +++ b/game/src/buffer_manager.h @@ -0,0 +1,22 @@ +#ifndef __INC_METIN_II_GAME_BUFFER_MANAGER_H__ +#define __INC_METIN_II_GAME_BUFFER_MANAGER_H__ + +class TEMP_BUFFER +{ + public: + TEMP_BUFFER(int Size = 8192, bool ForceDelete = false ); + ~TEMP_BUFFER(); + + const void * read_peek(); + void write(const void * data, int size); + int size(); + void reset(); + + LPBUFFER getptr() { return buf; } + + protected: + LPBUFFER buf; + bool forceDelete; +}; + +#endif diff --git a/game/src/build_locale_string.py b/game/src/build_locale_string.py new file mode 100644 index 0000000..c7bc19d --- /dev/null +++ b/game/src/build_locale_string.py @@ -0,0 +1,70 @@ +#!/usr/local/bin/python +import sys +import glob + +def IsHangulInLine(line): + for ch in line: + if ord(ch) >= 128: + return True + return False + +hanList = [] + +for fileName in glob.glob("*.cpp"): + isComment = False + + lines = open(fileName).readlines() + for line in lines: + linePos = lines.index(line) + if isComment: + commentEnd = line.find("*/") + if commentEnd < 0: + continue + else: + line = line[commentEnd+2:] + isComment = False + + + else: + commentBegin = line.find("/*") + if commentBegin >= 0: + commentEnd = line.find("*/") + if commentEnd >= 0: + line = line[:commentBegin] + line[commentEnd+2:] + else: + isComment = True + + while True: + pos = line.find("TEXT") + + if pos < 0: + break + + if len(line) < pos + 5: + break + + + if line[pos+4] != "(": + break + + pos += 5 + if line[pos] != '"': + break + + endPos = line[pos+1:].find('"') + if endPos < 0: + raise + + endPos += pos + endPos += 2 + + han = line[pos+1:endPos-1] + if not han in hanList: + hanList.append(han) + + line = line[endPos:] + +out = open("locale_string.txt", "w") +for han in hanList: + out.write("%s\n" % (han)) + print han diff --git a/game/src/building.cpp b/game/src/building.cpp new file mode 100644 index 0000000..12aec0c --- /dev/null +++ b/game/src/building.cpp @@ -0,0 +1,1269 @@ +#include "stdafx.h" +#include "constants.h" +#include "sectree_manager.h" +#include "item_manager.h" +#include "buffer_manager.h" +#include "config.h" +#include "packet.h" +#include "char.h" +#include "char_manager.h" +#include "guild.h" +#include "guild_manager.h" +#include "desc.h" +#include "desc_manager.h" +#include "desc_client.h" +#include "questmanager.h" +#include "building.h" + +enum +{ + // ADD_SUPPLY_BUILDING + BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL = 14061, + BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM = 14062, + BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE = 14063, + // END_OF_ADD_SUPPLY_BUILDING + + FLAG_VNUM = 14200, + WALL_DOOR_VNUM = 14201, + WALL_BACK_VNUM = 14202, + WALL_LEFT_VNUM = 14203, + WALL_RIGHT_VNUM = 14204, +}; + +using namespace building; + +CObject::CObject(TObject * pData, TObjectProto * pProto) + : m_pProto(pProto), m_dwVID(0), m_chNPC(NULL) +{ + CEntity::Initialize(ENTITY_OBJECT); + + thecore_memcpy(&m_data, pData, sizeof(TObject)); +} + +CObject::~CObject() +{ + Destroy(); +} + +void CObject::Destroy() +{ + if (m_pProto) + { + SECTREE_MANAGER::instance().ForAttrRegion(GetMapIndex(), + GetX() + m_pProto->lRegion[0], + GetY() + m_pProto->lRegion[1], + GetX() + m_pProto->lRegion[2], + GetY() + m_pProto->lRegion[3], + (long)m_data.zRot, // ADD_BUILDING_ROTATION + ATTR_OBJECT, + ATTR_REGION_MODE_REMOVE); + } + + CEntity::Destroy(); + + if (GetSectree()) + GetSectree()->RemoveEntity(this); + + // NPC should be destroyed in CHARACTER_MANAGER + // BUILDING_NPC + /* + if (m_chNPC) { + M2_DESTROY_CHARACTER(m_chNPC); + } + */ + + RemoveSpecialEffect(); + // END_OF_BUILDING_NPC +} + +// BUILDING_NPC +void CObject::Reconstruct(DWORD dwVnum) +{ + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(m_data.lMapIndex); + if (!r) + return; + + CLand* pLand = GetLand(); + pLand->RequestDeleteObject(GetID()); + pLand->RequestCreateObject(dwVnum, m_data.lMapIndex, m_data.x - r->sx, m_data.y - r->sy, m_data.xRot, m_data.yRot, m_data.zRot, false); +} +// END_OF_BUILDING_NPC + +void CObject::EncodeInsertPacket(LPENTITY entity) +{ + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + sys_log(0, "ObjectInsertPacket vid %u vnum %u rot %f %f %f", + m_dwVID, m_data.dwVnum, m_data.xRot, m_data.yRot, m_data.zRot); + + TPacketGCCharacterAdd pack; + + memset(&pack, 0, sizeof(TPacketGCCharacterAdd)); + + pack.header = HEADER_GC_CHARACTER_ADD; + pack.dwVID = m_dwVID; + pack.bType = CHAR_TYPE_BUILDING; + pack.angle = m_data.zRot; + pack.x = GetX(); + pack.y = GetY(); + pack.z = GetZ(); + pack.wRaceNum = m_data.dwVnum; + + // ȸ (϶ ġ) ȯ + pack.dwAffectFlag[0] = unsigned(m_data.xRot); + pack.dwAffectFlag[1] = unsigned(m_data.yRot); + + + if (GetLand()) + { + // pack.dwGuild = GetLand()->GetOwner(); + } + + d->Packet(&pack, sizeof(pack)); +} + +void CObject::EncodeRemovePacket(LPENTITY entity) +{ + LPDESC d; + + if (!(d = entity->GetDesc())) + return; + + sys_log(0, "ObjectRemovePacket vid %u", m_dwVID); + + TPacketGCCharacterDelete pack; + + pack.header = HEADER_GC_CHARACTER_DEL; + pack.id = m_dwVID; + + d->Packet(&pack, sizeof(TPacketGCCharacterDelete)); +} + +void CObject::SetVID(DWORD dwVID) +{ + m_dwVID = dwVID; +} + +bool CObject::Show(long lMapIndex, long x, long y) +{ + LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!tree) + { + sys_err("cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex); + return false; + } + + if (GetSectree()) + { + GetSectree()->RemoveEntity(this); + ViewCleanup(); + } + + m_data.lMapIndex = lMapIndex; + m_data.x = x; + m_data.y = y; + + Save(); + + SetMapIndex(lMapIndex); + SetXYZ(x, y, 0); + + tree->InsertEntity(this); + UpdateSectree(); + + SECTREE_MANAGER::instance().ForAttrRegion(lMapIndex, + x + m_pProto->lRegion[0], + y + m_pProto->lRegion[1], + x + m_pProto->lRegion[2], + y + m_pProto->lRegion[3], + (long)m_data.zRot, + ATTR_OBJECT, + ATTR_REGION_MODE_SET); + + return true; +} + +void CObject::Save() +{ +} + +void CObject::ApplySpecialEffect() +{ + if (m_pProto) + { + // ADD_SUPPLY_BUILDING + if (m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE) + { + CLand* pLand = GetLand(); + DWORD guild_id = 0; + if (pLand) + guild_id = pLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(guild_id); + if (pGuild) + { + switch (m_pProto->dwVnum) + { + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL: + pGuild->SetMemberCountBonus(6); + break; + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM: + pGuild->SetMemberCountBonus(12); + break; + case BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE: + pGuild->SetMemberCountBonus(18); + break; + } + if (map_allow_find(pLand->GetMapIndex())) + { + pGuild->BroadcastMemberCountBonus(); + } + } + } + // END_OF_ADD_SUPPLY_BUILDING + } +} + +void CObject::RemoveSpecialEffect() +{ + if (m_pProto) + { + // ADD_SUPPLY_BUILDING + if (m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_SMALL || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_MEDIUM || + m_pProto->dwVnum == BUILDING_INCREASE_GUILD_MEMBER_COUNT_LARGE) + { + CLand* pLand = GetLand(); + DWORD guild_id = 0; + if (pLand) + guild_id = pLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(guild_id); + if (pGuild) + { + pGuild->SetMemberCountBonus(0); + if (map_allow_find(pLand->GetMapIndex())) + pGuild->BroadcastMemberCountBonus(); + } + } + // END_OF_ADD_SUPPLY_BUILDING + } +} + +// BUILDING_NPC +void CObject::RegenNPC() +{ + if (!m_pProto) + return; + + if (!m_pProto->dwNPCVnum) + return; + + if (!m_pkLand) + return; + + DWORD dwGuildID = m_pkLand->GetOwner(); + CGuild* pGuild = CGuildManager::instance().FindGuild(dwGuildID); + + if (!pGuild) + return; + + int x = m_pProto->lNPCX; + int y = m_pProto->lNPCY; + int newX, newY; + + float rot = m_data.zRot * 2.0f * M_PI / 360.0f; + + newX = (int)(( x * cosf(rot)) + ( y * sinf(rot))); + newY = (int)(( y * cosf(rot)) - ( x * sinf(rot))); + + m_chNPC = CHARACTER_MANAGER::instance().SpawnMob(m_pProto->dwNPCVnum, + GetMapIndex(), + GetX() + newX, + GetY() + newY, + GetZ(), + false, + (int)m_data.zRot); + + + if (!m_chNPC) + { + sys_err("Cannot create guild npc"); + return; + } + + m_chNPC->SetGuild(pGuild); + + // 渶 س´ + if ( m_pProto->dwVnum == 14061 || m_pProto->dwVnum == 14062 || m_pProto->dwVnum == 14063 ) + { + quest::PC* pPC = quest::CQuestManager::instance().GetPC(pGuild->GetMasterPID()); + + if ( pPC != NULL ) + { + pPC->SetFlag("alter_of_power.build_level", pGuild->GetLevel()); + } + } +} +// END_OF_BUILDING_NPC + +//////////////////////////////////////////////////////////////////////////////////// + +CLand::CLand(TLand * pData) +{ + thecore_memcpy(&m_data, pData, sizeof(TLand)); +} + +CLand::~CLand() +{ + Destroy(); +} + +void CLand::Destroy() +{ + itertype(m_map_pkObject) it = m_map_pkObject.begin(); + + while (it != m_map_pkObject.end()) + { + LPOBJECT pkObj = (it++)->second; + CManager::instance().UnregisterObject(pkObj); + M2_DELETE(pkObj); + } + + m_map_pkObject.clear(); + m_map_pkObjectByVID.clear(); +} + +const TLand & CLand::GetData() +{ + return m_data; +} + +void CLand::PutData(const TLand * data) +{ + memcpy(&m_data, data, sizeof(TLand)); + + if (m_data.dwGuildID) + { + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(m_data.lMapIndex); + + if (r) + { + CharacterVectorInteractor i; + + if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(20040, i)) + { + CharacterVectorInteractor::iterator it = i.begin(); + + while (it != i.end()) + { + LPCHARACTER ch = *(it++); + + if (ch->GetMapIndex() != m_data.lMapIndex) + continue; + + int x = ch->GetX() - r->sx; + int y = ch->GetY() - r->sy; + + if (x > m_data.x + m_data.width || x < m_data.x) + continue; + + if (y > m_data.y + m_data.height || y < m_data.y) + continue; + + M2_DESTROY_CHARACTER(ch); + } + } + } + } +} + +void CLand::InsertObject(LPOBJECT pkObj) +{ + m_map_pkObject.insert(std::make_pair(pkObj->GetID(), pkObj)); + m_map_pkObjectByVID.insert(std::make_pair(pkObj->GetVID(), pkObj)); + + pkObj->SetLand(this); +} + +LPOBJECT CLand::FindObject(DWORD dwID) +{ + std::map::iterator it = m_map_pkObject.find(dwID); + + if (it == m_map_pkObject.end()) + return NULL; + + return it->second; +} + +LPOBJECT CLand::FindObjectByGroup(DWORD dwGroupVnum) +{ + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetGroup() == dwGroupVnum) + return pObj; + } + + return NULL; +} + +LPOBJECT CLand::FindObjectByVnum(DWORD dwVnum) +{ + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetVnum() == dwVnum) + return pObj; + } + + return NULL; +} + +// BUILDING_NPC +LPOBJECT CLand::FindObjectByNPC(LPCHARACTER npc) +{ + if (!npc) + return NULL; + + std::map::iterator it; + for (it = m_map_pkObject.begin(); it != m_map_pkObject.end(); ++it) + { + LPOBJECT pObj = it->second; + if (pObj->GetNPC() == npc) + return pObj; + } + + return NULL; +} +// END_OF_BUILDING_NPC + +LPOBJECT CLand::FindObjectByVID(DWORD dwVID) +{ + std::map::iterator it = m_map_pkObjectByVID.find(dwVID); + + if (it == m_map_pkObjectByVID.end()) + return NULL; + + return it->second; +} + +void CLand::DeleteObject(DWORD dwID) +{ + LPOBJECT pkObj; + + if (!(pkObj = FindObject(dwID))) + return; + + sys_log(0, "Land::DeleteObject %u", dwID); + CManager::instance().UnregisterObject(pkObj); + M2_DESTROY_CHARACTER (pkObj->GetNPC()); + + m_map_pkObject.erase(dwID); + m_map_pkObjectByVID.erase(dwID); + + M2_DELETE(pkObj); +} + +struct FIsIn +{ + long sx, sy; + long ex, ey; + + bool bIn; + FIsIn ( long sx_, long sy_, long ex_, long ey_) + : sx(sx_), sy(sy_), ex(ex_), ey(ey_), bIn(false) + {} + + void operator () (LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsMonster()) + { + return; + } + if (sx <= ch->GetX() && ch->GetX() <= ex + && sy <= ch->GetY() && ch->GetY() <= ey) + { + bIn = true; + } + } + } +}; + +bool CLand::RequestCreateObject(DWORD dwVnum, long lMapIndex, long x, long y, float xRot, float yRot, float zRot, bool checkAnother) +{ + SECTREE_MANAGER& rkSecTreeMgr = SECTREE_MANAGER::instance(); + TObjectProto * pkProto = CManager::instance().GetObjectProto(dwVnum); + + if (!pkProto) + { + sys_err("Invalid Object vnum %u", dwVnum); + return false; + } + const TMapRegion * r = rkSecTreeMgr.GetMapRegion(lMapIndex); + if (!r) + return false; + + sys_log(0, "RequestCreateObject(vnum=%u, map=%d, pos=(%d,%d), rot=(%.1f,%.1f,%.1f) region(%d,%d ~ %d,%d)", + dwVnum, lMapIndex, x, y, xRot, yRot, zRot, r->sx, r->sy, r->ex, r->ey); + + x += r->sx; + y += r->sy; + + int sx = r->sx + m_data.x; + int ex = sx + m_data.width; + int sy = r->sy + m_data.y; + int ey = sy + m_data.height; + + int osx = x + pkProto->lRegion[0]; + int osy = y + pkProto->lRegion[1]; + int oex = x + pkProto->lRegion[2]; + int oey = y + pkProto->lRegion[3]; + + float rad = zRot * 2.0f * M_PI / 360.0f; + + int tsx = (int)(pkProto->lRegion[0] * cosf(rad) + pkProto->lRegion[1] * sinf(rad) + x); + int tsy = (int)(pkProto->lRegion[0] * -sinf(rad) + pkProto->lRegion[1] * cosf(rad) + y); + + int tex = (int)(pkProto->lRegion[2] * cosf(rad) + pkProto->lRegion[3] * sinf(rad) + x); + int tey = (int)(pkProto->lRegion[2] * -sinf(rad) + pkProto->lRegion[3] * cosf(rad) + y); + + if (tsx < sx || tex > ex || tsy < sy || tey > ey) + { + sys_err("invalid position: object is outside of land region\nLAND: %d %d ~ %d %d\nOBJ: %d %d ~ %d %d", sx, sy, ex, ey, osx, osy, oex, oey); + return false; + } + + // ADD_BUILDING_ROTATION + if ( checkAnother ) + { + if (rkSecTreeMgr.ForAttrRegion(lMapIndex, osx, osy, oex, oey, (long)zRot, ATTR_OBJECT, ATTR_REGION_MODE_CHECK)) + { + sys_err("another object already exist"); + return false; + } + FIsIn f (osx, osy, oex, oey); + rkSecTreeMgr.GetMap(lMapIndex)->for_each(f); + + if (f.bIn) + { + sys_err("another object already exist"); + return false; + } + } + // END_OF_BUILDING_NPC + + TPacketGDCreateObject p; + + p.dwVnum = dwVnum; + p.dwLandID = m_data.dwID; + p.lMapIndex = lMapIndex; + p.x = x; + p.y = y; + p.xRot = xRot; + p.yRot = yRot; + p.zRot = zRot; + + db_clientdesc->DBPacket(HEADER_GD_CREATE_OBJECT, 0, &p, sizeof(TPacketGDCreateObject)); + return true; +} + +void CLand::RequestDeleteObject(DWORD dwID) +{ + if (!FindObject(dwID)) + { + sys_err("no object by id %u", dwID); + return; + } + + db_clientdesc->DBPacket(HEADER_GD_DELETE_OBJECT, 0, &dwID, sizeof(DWORD)); + sys_log(0, "RequestDeleteObject id %u", dwID); +} + +void CLand::RequestDeleteObjectByVID(DWORD dwVID) +{ + LPOBJECT pkObj; + + if (!(pkObj = FindObjectByVID(dwVID))) + { + sys_err("no object by vid %u", dwVID); + return; + } + + DWORD dwID = pkObj->GetID(); + db_clientdesc->DBPacket(HEADER_GD_DELETE_OBJECT, 0, &dwID, sizeof(DWORD)); + sys_log(0, "RequestDeleteObject vid %u id %u", dwVID, dwID); +} + +void CLand::SetOwner(DWORD dwGuild) +{ + if (m_data.dwGuildID != dwGuild) + { + m_data.dwGuildID = dwGuild; + RequestUpdate(dwGuild); + } +} + +void CLand::RequestUpdate(DWORD dwGuild) +{ + DWORD a[2]; + + a[0] = GetID(); + a[1] = dwGuild; + + db_clientdesc->DBPacket(HEADER_GD_UPDATE_LAND, 0, &a[0], sizeof(DWORD) * 2); + sys_log(0, "RequestUpdate id %u guild %u", a[0], a[1]); +} + +//////////////////////////////////////////////////////////////////////////////////// + +CManager::CManager() +{ +} + +CManager::~CManager() +{ + Destroy(); +} + +void CManager::Destroy() +{ + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + for ( ; it != m_map_pkLand.end(); ++it) { + M2_DELETE(it->second); + } + m_map_pkLand.clear(); +} + +bool CManager::LoadObjectProto(const TObjectProto * pProto, int size) // from DB +{ + m_vec_kObjectProto.resize(size); + thecore_memcpy(&m_vec_kObjectProto[0], pProto, sizeof(TObjectProto) * size); + + for (int i = 0; i < size; ++i) + { + TObjectProto & r = m_vec_kObjectProto[i]; + + // BUILDING_NPC + sys_log(0, "ObjectProto %u price %u upgrade %u upg_limit %u life %d NPC %u", + r.dwVnum, r.dwPrice, r.dwUpgradeVnum, r.dwUpgradeLimitTime, r.lLife, r.dwNPCVnum); + // END_OF_BUILDING_NPC + + for (int j = 0; j < OBJECT_MATERIAL_MAX_NUM; ++j) + { + if (!r.kMaterials[j].dwItemVnum) + break; + + if (NULL == ITEM_MANAGER::instance().GetTable(r.kMaterials[j].dwItemVnum)) + { + sys_err(" mat: ERROR!! no item by vnum %u", r.kMaterials[j].dwItemVnum); + return false; + } + + sys_log(0, " mat: %u %u", r.kMaterials[j].dwItemVnum, r.kMaterials[j].dwCount); + } + + m_map_pkObjectProto.insert(std::make_pair(r.dwVnum, &m_vec_kObjectProto[i])); + } + + return true; +} + +TObjectProto * CManager::GetObjectProto(DWORD dwVnum) +{ + itertype(m_map_pkObjectProto) it = m_map_pkObjectProto.find(dwVnum); + + if (it == m_map_pkObjectProto.end()) + return NULL; + + return it->second; +} + +bool CManager::LoadLand(TLand * pTable) // from DB +{ + // MapAllow load ؾѴ. + // ǹ(object) 忡 ִ ˱ ؼ ǹ Ҽ ˾Ѵ. + // load ǹ 忡 Ҽӵ ؼ + // ǹ Ѵ. + //if (!map_allow_find(pTable->lMapIndex)) + // return false; + + CLand * pkLand = M2_NEW CLand(pTable); + m_map_pkLand.insert(std::make_pair(pkLand->GetID(), pkLand)); + + sys_log(0, "LAND: %u map %d %dx%d w %u h %u", + pTable->dwID, pTable->lMapIndex, pTable->x, pTable->y, pTable->width, pTable->height); + + return true; +} + +void CManager::UpdateLand(TLand * pTable) +{ + CLand * pkLand = FindLand(pTable->dwID); + + if (!pkLand) + { + sys_err("cannot find land by id %u", pTable->dwID); + return; + } + + pkLand->PutData(pTable); + + const DESC_MANAGER::DESC_SET & cont = DESC_MANAGER::instance().GetClientSet(); + + itertype(cont) it = cont.begin(); + + TPacketGCLandList p; + + p.header = HEADER_GC_LAND_LIST; + p.size = sizeof(TPacketGCLandList) + sizeof(TLandPacketElement); + + TLandPacketElement e; + + e.dwID = pTable->dwID; + e.x = pTable->x; + e.y = pTable->y; + e.width = pTable->width; + e.height = pTable->height; + e.dwGuildID = pTable->dwGuildID; + + sys_log(0, "BUILDING: UpdateLand %u pos %dx%d guild %u", e.dwID, e.x, e.y, e.dwGuildID); + + CGuild *guild = CGuildManager::instance().FindGuild(pTable->dwGuildID); + while (it != cont.end()) + { + LPDESC d = *(it++); + + if (d->GetCharacter() && d->GetCharacter()->GetMapIndex() == pTable->lMapIndex) + { + // we must send the guild name first + d->GetCharacter()->SendGuildName(guild); + + d->BufferedPacket(&p, sizeof(TPacketGCLandList)); + d->Packet(&e, sizeof(TLandPacketElement)); + } + } +} + +CLand * CManager::FindLand(DWORD dwID) +{ + std::map::iterator it = m_map_pkLand.find(dwID); + + if (it == m_map_pkLand.end()) + return NULL; + + return it->second; +} + +CLand * CManager::FindLand(long lMapIndex, long x, long y) +{ + sys_log(0, "BUILDING: FindLand %d %d %d", lMapIndex, x, y); + + const TMapRegion * r = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex); + + if (!r) + return NULL; + + x -= r->sx; + y -= r->sy; + + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + const TLand & r = pkLand->GetData(); + + if (r.lMapIndex != lMapIndex) + continue; + + if (x < r.x || y < r.y) + continue; + + if (x > r.x + r.width || y > r.y + r.height) + continue; + + return pkLand; + } + + return NULL; +} + +CLand * CManager::FindLandByGuild(DWORD GID) +{ + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + + if (pkLand->GetData().dwGuildID == GID) + return pkLand; + } + + return NULL; +} + +bool CManager::LoadObject(TObject * pTable, bool isBoot) // from DB +{ + CLand * pkLand = FindLand(pTable->dwLandID); + + if (!pkLand) + { + sys_log(0, "Cannot find land by id %u", pTable->dwLandID); + return false; + } + + TObjectProto * pkProto = GetObjectProto(pTable->dwVnum); + + if (!pkProto) + { + sys_err("Cannot find object %u in prototype (id %u)", pTable->dwVnum, pTable->dwID); + return false; + } + + sys_log(0, "OBJ: id %u vnum %u map %d pos %dx%d", pTable->dwID, pTable->dwVnum, pTable->lMapIndex, pTable->x, pTable->y); + + LPOBJECT pkObj = M2_NEW CObject(pTable, pkProto); + + DWORD dwVID = CHARACTER_MANAGER::instance().AllocVID(); + pkObj->SetVID(dwVID); + + m_map_pkObjByVID.insert(std::make_pair(dwVID, pkObj)); + m_map_pkObjByID.insert(std::make_pair(pTable->dwID, pkObj)); + + pkLand->InsertObject(pkObj); + + if (!isBoot) + pkObj->Show(pTable->lMapIndex, pTable->x, pTable->y); + else + { + pkObj->SetMapIndex(pTable->lMapIndex); + pkObj->SetXYZ(pTable->x, pTable->y, 0); + } + + // BUILDING_NPC + if (!isBoot) + { + if (pkProto->dwNPCVnum) + pkObj->RegenNPC(); + + pkObj->ApplySpecialEffect(); + } + // END_OF_BUILDING_NPC + + return true; +} + +void CManager::FinalizeBoot() +{ + itertype(m_map_pkObjByID) it = m_map_pkObjByID.begin(); + + while (it != m_map_pkObjByID.end()) + { + LPOBJECT pkObj = (it++)->second; + + pkObj->Show(pkObj->GetMapIndex(), pkObj->GetX(), pkObj->GetY()); + // BUILDING_NPC + pkObj->RegenNPC(); + pkObj->ApplySpecialEffect(); + // END_OF_BUILDING_NPC + } + + // BUILDING_NPC + sys_log(0, "FinalizeBoot"); + // END_OF_BUILDING_NPC + + itertype(m_map_pkLand) it2 = m_map_pkLand.begin(); + + while (it2 != m_map_pkLand.end()) + { + CLand * pkLand = (it2++)->second; + + const TLand & r = pkLand->GetData(); + + // LAND_MASTER_LOG + sys_log(0, "LandMaster map_index=%d pos=(%d, %d)", r.lMapIndex, r.x, r.y); + // END_OF_LAND_MASTER_LOG + + if (r.dwGuildID != 0) + continue; + + if (!map_allow_find(r.lMapIndex)) + continue; + + const TMapRegion * region = SECTREE_MANAGER::instance().GetMapRegion(r.lMapIndex); + if (!region) + continue; + + CHARACTER_MANAGER::instance().SpawnMob(20040, r.lMapIndex, region->sx + r.x + (r.width / 2), region->sy + r.y + (r.height / 2), 0); + } +} + +void CManager::DeleteObject(DWORD dwID) // from DB +{ + sys_log(0, "OBJ_DEL: %u", dwID); + + itertype(m_map_pkObjByID) it = m_map_pkObjByID.find(dwID); + + if (it == m_map_pkObjByID.end()) + return; + + it->second->GetLand()->DeleteObject(dwID); +} + +LPOBJECT CManager::FindObjectByVID(DWORD dwVID) +{ + itertype(m_map_pkObjByVID) it = m_map_pkObjByVID.find(dwVID); + + if (it == m_map_pkObjByVID.end()) + return NULL; + + return it->second; +} + +void CManager::UnregisterObject(LPOBJECT pkObj) +{ + m_map_pkObjByID.erase(pkObj->GetID()); + m_map_pkObjByVID.erase(pkObj->GetVID()); +} + +void CManager::SendLandList(LPDESC d, long lMapIndex) +{ + TLandPacketElement e; + + TEMP_BUFFER buf; + + WORD wCount = 0; + + itertype(m_map_pkLand) it = m_map_pkLand.begin(); + + while (it != m_map_pkLand.end()) + { + CLand * pkLand = (it++)->second; + const TLand & r = pkLand->GetData(); + + if (r.lMapIndex != lMapIndex) + continue; + + // + LPCHARACTER ch = d->GetCharacter(); + if (ch) + { + CGuild *guild = CGuildManager::instance().FindGuild(r.dwGuildID); + ch->SendGuildName(guild); + } + // + + e.dwID = r.dwID; + e.x = r.x; + e.y = r.y; + e.width = r.width; + e.height = r.height; + e.dwGuildID = r.dwGuildID; + + buf.write(&e, sizeof(TLandPacketElement)); + ++wCount; + } + + sys_log(0, "SendLandList map %d count %u elem_size: %d", lMapIndex, wCount, buf.size()); + + if (wCount != 0) + { + TPacketGCLandList p; + + p.header = HEADER_GC_LAND_LIST; + p.size = sizeof(TPacketGCLandList) + buf.size(); + + d->BufferedPacket(&p, sizeof(TPacketGCLandList)); + d->Packet(buf.read_peek(), buf.size()); + } +} + +// LAND_CLEAR +void CManager::ClearLand(DWORD dwLandID) +{ + CLand* pLand = FindLand(dwLandID); + + if ( pLand == NULL ) + { + sys_log(0, "LAND_CLEAR: there is no LAND id like %d", dwLandID); + return; + } + + pLand->ClearLand(); + + sys_log(0, "LAND_CLEAR: request Land Clear. LandID: %d", pLand->GetID()); +} + +void CManager::ClearLandByGuildID(DWORD dwGuildID) +{ + CLand* pLand = FindLandByGuild(dwGuildID); + + if ( pLand == NULL ) + { + sys_log(0, "LAND_CLEAR: there is no GUILD id like %d", dwGuildID); + return; + } + + pLand->ClearLand(); + + sys_log(0, "LAND_CLEAR: request Land Clear. LandID: %d", pLand->GetID()); +} + +void CLand::ClearLand() +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + while ( iter != m_map_pkObject.end() ) + { + RequestDeleteObject(iter->second->GetID()); + iter++; + } + + SetOwner(0); + + const TLand & r = GetData(); + const TMapRegion * region = SECTREE_MANAGER::instance().GetMapRegion(r.lMapIndex); + + CHARACTER_MANAGER::instance().SpawnMob(20040, r.lMapIndex, region->sx + r.x + (r.width / 2), region->sy + r.y + (r.height / 2), 0); +} +// END_LAND_CLEAR + +// BUILD_WALL +void CLand::DrawWall(DWORD dwVnum, long nMapIndex, long& x, long& y, char length, float zRot) +{ + int rot = (int)zRot; + rot = ((rot%360) / 90) * 90; + + int dx=0, dy=0; + + switch ( rot ) + { + case 0 : + dx = -500; + dy = 0; + break; + + case 90 : + dx = 0; + dy = 500; + break; + + case 180 : + dx = 500; + dy = 0; + break; + + case 270 : + dx = 0; + dy = -500; + break; + } + + for ( int i=0; i < length; i++ ) + { + this->RequestCreateObject(dwVnum, nMapIndex, x, y, 0, 0, rot, false); + x += dx; + y += dy; + } +} + + +bool CLand::RequestCreateWall(long nMapIndex, float rot) +{ + const bool WALL_ANOTHER_CHECKING_ENABLE = false; + + const TLand& land = GetData(); + + int center_x = land.x + land.width / 2; + int center_y = land.y + land.height / 2; + + int wall_x = center_x; + int wall_y = center_y; + int wall_half_w = 1000; + int wall_half_h = 1362; + + if (rot == 0.0f) // + { + int door_x = wall_x; + int door_y = wall_y + wall_half_h; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x, wall_y + wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x, wall_y - wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x - wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x + wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 180.0f) // + { + int door_x = wall_x; + int door_y = wall_y - wall_half_h; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x, wall_y - wall_half_h, door_x, door_y, 180.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x, wall_y + wall_half_h, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x - wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x + wall_half_w, wall_y, door_x, door_y, 0.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 90.0f) // + { + int door_x = wall_x + wall_half_h; + int door_y = wall_y; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x + wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x - wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x, wall_y - wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x, wall_y + wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + else if (rot == 270.0f) // + { + int door_x = wall_x - wall_half_h; + int door_y = wall_y; + RequestCreateObject(WALL_DOOR_VNUM, nMapIndex, wall_x - wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_BACK_VNUM, nMapIndex, wall_x + wall_half_h, wall_y, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_LEFT_VNUM, nMapIndex, wall_x, wall_y - wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(WALL_RIGHT_VNUM, nMapIndex, wall_x, wall_y + wall_half_w, door_x, door_y, 90.0f, WALL_ANOTHER_CHECKING_ENABLE); + } + + if (test_server) + { + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + 50, land.y + 50, 0, 0, 0.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + land.width - 50, land.y + 50, 0, 0, 90.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + land.width - 50, land.y + land.height - 50, 0, 0, 180.0, WALL_ANOTHER_CHECKING_ENABLE); + RequestCreateObject(FLAG_VNUM, nMapIndex, land.x + 50, land.y + land.height - 50, 0, 0, 270.0, WALL_ANOTHER_CHECKING_ENABLE); + } + return true; +} + +void CLand::RequestDeleteWall() +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + while (iter != m_map_pkObject.end()) + { + unsigned id = iter->second->GetID(); + unsigned vnum = iter->second->GetVnum(); + + switch (vnum) + { + case WALL_DOOR_VNUM: + case WALL_BACK_VNUM: + case WALL_LEFT_VNUM: + case WALL_RIGHT_VNUM: + RequestDeleteObject(id); + break; + } + + + if (test_server) + { + if (FLAG_VNUM == vnum) + RequestDeleteObject(id); + + } + + iter++; + } +} + +bool CLand::RequestCreateWallBlocks(DWORD dwVnum, long nMapIndex, char wallSize, bool doorEast, bool doorWest, bool doorSouth, bool doorNorth) +{ + const TLand & r = GetData(); + + long startX = r.x + (r.width / 2) - (1300 + wallSize*500); + long startY = r.y + (r.height / 2) + (1300 + wallSize*500); + + DWORD corner = dwVnum - 4; + DWORD wall = dwVnum - 3; + DWORD door = dwVnum - 1; + + bool checkAnother = false; + long* ptr = NULL; + int delta = 1; + int rot = 270; + + bool doorOpen[4]; + doorOpen[0] = doorWest; + doorOpen[1] = doorNorth; + doorOpen[2] = doorEast; + doorOpen[3] = doorSouth; + + if ( wallSize > 3 ) wallSize = 3; + else if ( wallSize < 0 ) wallSize = 0; + + for ( int i=0; i < 4; i++, rot -= 90 ) + { + switch ( i ) + { + case 0 : + delta = -1; + ptr = &startY; + break; + case 1 : + delta = 1; + ptr = &startX; + break; + case 2 : + ptr = &startY; + delta = 1; + break; + case 3 : + ptr = &startX; + delta = -1; + break; + } + + this->RequestCreateObject(corner, nMapIndex, startX, startY, 0, 0, rot, checkAnother); + + *ptr = *ptr + ( 700 * delta ); + + if ( doorOpen[i] ) + { + this->DrawWall(wall, nMapIndex, startX, startY, wallSize, rot); + + *ptr = *ptr + ( 700 * delta ); + + this->RequestCreateObject(door, nMapIndex, startX, startY, 0, 0, rot, checkAnother); + + *ptr = *ptr + ( 1300 * delta ); + + this->DrawWall(wall, nMapIndex, startX, startY, wallSize, rot); + } + else + { + this->DrawWall(wall, nMapIndex, startX, startY, wallSize*2 + 4, rot); + } + + *ptr = *ptr + ( 100 * delta ); + } + + return true; +} + +void CLand::RequestDeleteWallBlocks(DWORD dwID) +{ + itertype(m_map_pkObject) iter = m_map_pkObject.begin(); + + DWORD corner = dwID - 4; + DWORD wall = dwID - 3; + DWORD door = dwID - 1; + DWORD dwVnum = 0; + + while ( iter != m_map_pkObject.end() ) + { + dwVnum = iter->second->GetVnum(); + + if ( dwVnum == corner || dwVnum == wall || dwVnum == door ) + { + RequestDeleteObject(iter->second->GetID()); + } + iter++; + } +} +// END_BUILD_WALL + diff --git a/game/src/building.h b/game/src/building.h new file mode 100644 index 0000000..90b9d61 --- /dev/null +++ b/game/src/building.h @@ -0,0 +1,152 @@ +#ifndef __INC_METIN_II_BUILDING_H__ +#define __INC_METIN_II_BUILDING_H__ + +#include "../../common/building.h" + +namespace building +{ + class CLand; + + class CObject : public CEntity + { + public: + CObject(TObject * pData, TObjectProto * pProto); + virtual ~CObject(); + + void Destroy(); + + virtual void EncodeInsertPacket(LPENTITY entity); + virtual void EncodeRemovePacket(LPENTITY entity); + + DWORD GetID() { return m_data.dwID; } + + void SetVID(DWORD dwVID); + DWORD GetVID() { return m_dwVID; } + + bool Show(long lMapIndex, long x, long y); + + void Save(); + + void SetLand(CLand * pkLand) { m_pkLand = pkLand; } + CLand * GetLand() { return m_pkLand; } + + DWORD GetVnum() { return m_pProto ? m_pProto->dwVnum : 0; } + DWORD GetGroup() { return m_pProto ? m_pProto->dwGroupVnum : 0; } + + void RegenNPC(); + + // BUILDING_NPC + void ApplySpecialEffect(); + void RemoveSpecialEffect(); + + void Reconstruct(DWORD dwVnum); + + LPCHARACTER GetNPC() { return m_chNPC; } + // END_OF_BUILDING_NPC + + protected: + TObjectProto * m_pProto; + TObject m_data; + DWORD m_dwVID; + CLand * m_pkLand; + + LPCHARACTER m_chNPC; + }; + + class CLand + { + public: + CLand(TLand * pData); + ~CLand(); + + void Destroy(); + + const TLand & GetData(); + void PutData(const TLand * data); + + DWORD GetID() const { return m_data.dwID; } + void SetOwner(DWORD dwGID); + DWORD GetOwner() const { return m_data.dwGuildID; } + + void InsertObject(LPOBJECT pkObj); + LPOBJECT FindObject(DWORD dwID); + LPOBJECT FindObjectByVID(DWORD dwVID); + LPOBJECT FindObjectByVnum(DWORD dwVnum); + LPOBJECT FindObjectByGroup(DWORD dwGroupVnum); + LPOBJECT FindObjectByNPC(LPCHARACTER npc); + void DeleteObject(DWORD dwID); + + bool RequestCreateObject(DWORD dwVnum, long lMapIndex, long x, long y, float xRot, float yRot, float zRot, bool checkAnother); + void RequestDeleteObject(DWORD dwID); + void RequestDeleteObjectByVID(DWORD dwVID); + + void RequestUpdate(DWORD dwGuild); + + // LAND_CLEAR + void ClearLand(); + // END_LAND_CLEAR + + // BUILD_WALL + bool RequestCreateWall(long nMapIndex, float rot); + void RequestDeleteWall(); + + bool RequestCreateWallBlocks(DWORD dwVnum, long nMapIndex, char wallSize, bool doorEast, bool doorWest, bool doorSouth, bool doorNorth); + void RequestDeleteWallBlocks(DWORD dwVnum); + // END_BUILD_WALL + + DWORD GetMapIndex() { return m_data.lMapIndex; } + + protected: + TLand m_data; + std::map m_map_pkObject; + std::map m_map_pkObjectByVID; + + // BUILD_WALL + private : + void DrawWall(DWORD dwVnum, long nMapIndex, long& centerX, long& centerY, char length, float zRot); + // END_BUILD_WALL + }; + + class CManager : public singleton + { + public: + CManager(); + virtual ~CManager(); + + void Destroy(); + + void FinalizeBoot(); + + bool LoadObjectProto(const TObjectProto * pProto, int size); + TObjectProto * GetObjectProto(DWORD dwVnum); + + bool LoadLand(TLand * pTable); + CLand * FindLand(DWORD dwID); + CLand * FindLand(long lMapIndex, long x, long y); + CLand * FindLandByGuild(DWORD GID); + void UpdateLand(TLand * pTable); + + bool LoadObject(TObject * pTable, bool isBoot=false); + void DeleteObject(DWORD dwID); + void UnregisterObject(LPOBJECT pkObj); + + LPOBJECT FindObjectByVID(DWORD dwVID); + + void SendLandList(LPDESC d, long lMapIndex); + + // LAND_CLEAR + void ClearLand(DWORD dwLandID); + void ClearLandByGuildID(DWORD dwGuildID); + // END_LAND_CLEAR + + protected: + std::vector m_vec_kObjectProto; + std::map m_map_pkObjectProto; + + std::map m_map_pkLand; + std::map m_map_pkObjByID; + std::map m_map_pkObjByVID; + }; +} + +#endif diff --git a/game/src/castle.cpp b/game/src/castle.cpp new file mode 100644 index 0000000..2866e4f --- /dev/null +++ b/game/src/castle.cpp @@ -0,0 +1,1079 @@ +/********************************************************************* + * date : 2007.04.07 + * file : castle.cpp + * author : mhh + * description : + * ȭ ȣ : 11506 - 11510 + * ƾ ȣ : 8012 - 8014, 8024-8027 + */ + +#define _castle_cpp_ + +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "char_manager.h" +#include "castle.h" +#include "start_position.h" +#include "monarch.h" +#include "questlua.h" +#include "log.h" +#include "char.h" +#include "sectree_manager.h" + +#define EMPIRE_NONE 0 // ƹ ƴ +#define EMPIRE_RED 1 // ż +#define EMPIRE_YELLOW 2 // õ +#define EMPIRE_BLUE 3 // + + +#define SIEGE_EVENT_PULSE PASSES_PER_SEC(60*5) // 5 + + +#define GET_CAHR_MANAGER() CHARACTER_MANAGER::instance() +#define GET_CASTLE(empire) (s_castle+(empire)) +#define GET_GUARD(empire, region_index, guard_index) (GET_CASTLE(empire)->guard[region_index][guard_index]) +#define GET_GUARD_REGION(empire, region_index) (s_guard_region[empire][region_index]) +#define GET_GUARD_GROUP(empire, region_index, guard_index) (GET_CASTLE(empire)->guard_group[region_index][guard_index]) +#define GET_FROG(empire, index) (GET_CASTLE(empire)->frog[index]) +#define GET_FROG_POS(empire, index) (s_frog_pos[empire][index]) +#define GET_TOWER(empire, index) (GET_CASTLE(empire)->tower[index]) + +#define DO_ALL_EMPIRE(empire) for (int empire = 1; empire < 4; ++empire) +#define DO_ALL_TOWER(i) for (int i = 0; i < MAX_CASTLE_TOWER; ++i) +#define DO_ALL_FROG(i) for (int i = 0; i < MAX_CASTLE_FROG; ++i) + + +#define GET_SIEGE_STATE() s_siege_state +#define GET_SIEGE_EMPIRE() s_sige_empire +#define GET_SIEGE_EVENT(empire) (GET_CASTLE(empire)->siege_event) +#define GET_STONE_EVENT(empire) (GET_CASTLE(empire)->stone_event) + +#define GET_TOWER_REGION(empire) s_tower_region[empire] +#define GET_STONE_REGION(empire) s_tower_region[empire] + + +static CASTLE_DATA *s_castle = NULL; +static CASTLE_STATE s_siege_state = CASTLE_SIEGE_NONE; +static int s_sige_empire = EMPIRE_NONE; + + +struct POSITION +{ + int x, y; +}; + +static POSITION s_frog_pos[4][MAX_CASTLE_FROG] = { + // EMPIRE_NONE + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + // EMPIRE_RED + { + { 225, 45 }, + { 231, 45 }, + { 237, 45 }, + { 243, 45 }, + { 249, 45 }, + + { 225, 50 }, + { 231, 50 }, + { 237, 50 }, + { 243, 50 }, + { 249, 50 }, + + { 261, 45 }, + { 267, 45 }, + { 273, 45 }, + { 279, 45 }, + { 285, 45 }, + + { 261, 50 }, + { 267, 50 }, + { 273, 50 }, + { 279, 50 }, + { 285, 50 } + }, + // EMPIRE_YELLOW + { + { 221, 36 }, + { 227, 36 }, + { 233, 36 }, + { 239, 36 }, + { 245, 36 }, + + { 269, 36 }, + { 275, 36 }, + { 281, 36 }, + { 287, 36 }, + { 293, 36 }, + + { 221, 41 }, + { 227, 41 }, + { 233, 41 }, + { 239, 41 }, + { 245, 41 }, + + { 269, 41 }, + { 275, 41 }, + { 281, 41 }, + { 287, 41 }, + { 293, 41 } + }, + // EMPIRE_BLUE + { + { 225, 45 }, + { 231, 45 }, + { 237, 45 }, + { 243, 45 }, + { 249, 45 }, + + { 225, 50 }, + { 231, 50 }, + { 237, 50 }, + { 243, 50 }, + { 249, 50 }, + + { 261, 45 }, + { 267, 45 }, + { 273, 45 }, + { 279, 45 }, + { 285, 45 }, + + { 261, 50 }, + { 267, 50 }, + { 273, 50 }, + { 279, 50 }, + { 285, 50 } + } +}; + + +/* 񱸿 */ +struct GUARD_REGION +{ + int sx, sy, ex, ey; +}; + +static GUARD_REGION s_guard_region[4][4] = { + // NULL_EMPIRE + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 } + }, + // EMPIRE_RED + { + { 74, 170, 96, 180 }, + { 237, 135, 270, 146 }, + { 235, 260, 278, 273 }, + { 421, 167, 435, 205 } + }, + // EMPIRE_YELLOW + { + { 109, 172, 128, 202 }, + { 237, 140, 282, 153 }, + { 232, 261, 279, 276 }, + { 390, 173, 403, 205 }, + }, + // EMPIRE_BLUE + { + { 74, 170, 96, 120 }, + { 237, 135, 270, 146 }, + { 235, 260, 278, 273 }, + { 421, 167, 435, 205 } + } +}; + +static GUARD_REGION s_tower_region[4] = { + // NULL_EMPIRE + { 0, 0, 0, 0 }, + // EMPIRE_RED + { 85, 135, 420, 265 }, + // EMPIRE_YELLOW + { 120, 130, 405, 276 }, + // EMPIRE_BLUE + { 85, 135, 420, 265 } +}; + + +static long FN_castle_map_index(int empire); + +EVENTINFO(castle_event_info) +{ + int empire; + int pulse; + + castle_event_info() + : empire( 0 ) + , pulse( 0 ) + { + } +}; + +EVENTFUNC(castle_siege_event) +{ + char buf[1024] = {0}; + struct castle_event_info *info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "castle_siege_event> Null pointer" ); + return 0; + } + + info->pulse += SIEGE_EVENT_PULSE; + + // 30 ̳ ȳ + if (info->pulse < PASSES_PER_SEC(30*60)) + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ѷΰ Դϴ."), + EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + return SIEGE_EVENT_PULSE; + } + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + break; + + case CASTLE_SIEGE_STRUGGLE: + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ߽ϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + snprintf(buf, sizeof(buf), LC_TEXT("ݺ %s 30а ȭ ıϿ ȹ ֽϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + + GET_SIEGE_STATE() = CASTLE_SIEGE_END; + + return PASSES_PER_SEC(60*30); // 30 + } + break; + case CASTLE_SIEGE_END: + BroadcastNotice(LC_TEXT("30 ߽ϴ.. ȭ ϴ.")); + castle_end_siege(); + break; + } + return 0; +} + + +static DWORD FN_random_stone() +{ + DWORD vnum[7] = { + 8012, + 8013, + 8014, + 8024, + 8025, + 8026, + 8027 + }; + + int index = number(0, 6); + + return vnum[index]; +} + +EVENTINFO(castle_stone_event_info) +{ + int empire; + int spawn_count; + + castle_stone_event_info() + : empire( 0 ) + , spawn_count( 0 ) + { + } +}; + + +EVENTFUNC(castle_stone_event) +{ + struct castle_stone_event_info *info = dynamic_cast( event->info ); + + if (info == NULL) + { + sys_err( "castle_stone_event> Null pointer" ); + return 0; + } + + long map_index = FN_castle_map_index(GET_SIEGE_EMPIRE()); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + + if (NULL == sectree_map) + return 0; + + /* 15 2 ȯ */ + const int SPAWN_COUNT = 15; + + if (info->spawn_count < (SPAWN_COUNT * 2)) + { + for (int i = 0; i < SPAWN_COUNT; ++i) + { + DWORD sx = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(info->empire).sx; + DWORD sy = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(info->empire).sy; + DWORD ex = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(info->empire).ex; + DWORD ey = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(info->empire).ey; + + CHARACTER_MANAGER::instance().SpawnMobRange(FN_random_stone(), + FN_castle_map_index(info->empire), + sx, sy, ex, ey); + } + + info->spawn_count += SPAWN_COUNT; + + if (info->spawn_count < (SPAWN_COUNT * 2)) + return PASSES_PER_SEC(30 * 60); // 30 + else + return 0; + } + + return 0; +} + + + +LPCHARACTER castle_spawn_frog_force(int empire, int empty_index); + +static long FN_castle_map_index(int empire) +{ + switch (empire) + { + case EMPIRE_RED: return 181; + case EMPIRE_YELLOW: return 183; + case EMPIRE_BLUE: return 182; + } + return 0; +} + +static int FN_empty_frog_index(int empire) +{ + DO_ALL_FROG(i) + { + if (NULL == GET_FROG(empire, i)) + return i; + } + return (-1); +} + +static POSITION* FN_empty_frog_pos(int empire) +{ + int frog_index = FN_empty_frog_index(empire); + + if (frog_index < 0) + return NULL; + + switch (empire) + { + case EMPIRE_RED: + case EMPIRE_YELLOW: + case EMPIRE_BLUE: + return &GET_FROG_POS(empire, frog_index); + } + + return NULL; +} + +static int FN_empty_guard_pos(int empire, int region_index) +{ + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if (NULL == GET_GUARD(empire, region_index, i)) + { + return i; + } + } + + return -1; +} + +static bool FN_is_castle_map(int map_index) +{ + switch (map_index) + { + case 181: + case 182: + case 183: + return true; + } + return false; +} + + +bool castle_boot() +{ + FILE *fp; + char one_line[256]; + const char *delim = " \t\r\n"; + char *v; + int empire = 0; + int guard_region = 0; + + CREATE(s_castle, CASTLE_DATA, 4); + + const char *castle_file = "castle_data.txt"; + + if ((fp = fopen(castle_file, "r")) == 0) + return false; + + while (fgets(one_line, 256, fp)) + { + int value = 0; + + if (one_line[0] == '#') + continue; + + const char* token_string = strtok(one_line, delim); + + if (NULL == token_string) + continue; + + TOKEN("section") + { + continue; + } + else TOKEN("empire") + { + v = strtok(NULL, delim); + if (v) + { + str_to_number(empire, v); + } + else + { + fclose(fp); + sys_err("wrong empire number is null"); + return false; + } + } + else TOKEN("frog") + { + int pos = 0; + + while ((v = strtok(NULL, delim))) + { + str_to_number(value, v); + if (value) + { + castle_spawn_frog_force(empire, pos); + } + ++pos; + } + } + else TOKEN("guard") + { + int group_vnum = 0; + + while ((v = strtok(NULL, delim))) + { + str_to_number(group_vnum, v); + if (group_vnum) + { + castle_spawn_guard(empire, group_vnum, guard_region); + } + } + + ++guard_region; + } + else TOKEN("end") + { + guard_region = 0; + } + } + + fclose(fp); + + return true; +} + +void castle_save() +{ + if (NULL == s_castle) + return; + + const char *castle_file = "castle_data.txt"; + FILE *fp; + + fp = fopen(castle_file, "w"); + + if (NULL == fp) + { + sys_err(" fopen(%s)", castle_file); + return; + } + + // write castle data + DO_ALL_EMPIRE(empire) + { + fprintf(fp, "section\n"); + + // write empire + fprintf(fp, "\tempire %d\n", empire); + + // write frog + fprintf(fp, "\tfrog "); + for (int i = 0; i < MAX_CASTLE_FROG; ++i) + { + fprintf(fp, " %d", GET_FROG(empire, i) ? 1 : 0); + } + fprintf(fp, "\n"); + + // write guard group + for (int region_index = 0; region_index < MAX_CASTLE_GUARD_REGION; ++region_index) + { + fprintf(fp, "\tguard "); + for (int guard_index = 0; guard_index < MAX_CASTLE_GUARD_PER_REGION; ++guard_index) + { + fprintf(fp, " %u", GET_GUARD_GROUP(empire, region_index, guard_index)); + } + fprintf(fp, "\n"); + } + fprintf(fp, "end\n"); + } + + fclose(fp); +} + +int castle_siege(int empire, int tower_count) +{ + // check siege map + { + if (NULL == SECTREE_MANAGER::instance().GetMap(181)) return 0; + if (NULL == SECTREE_MANAGER::instance().GetMap(182)) return 0; + if (NULL == SECTREE_MANAGER::instance().GetMap(183)) return 0; + } + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + castle_start_siege(empire, tower_count); + return 1; + break; + + case CASTLE_SIEGE_STRUGGLE: + case CASTLE_SIEGE_END: + castle_end_siege(); + return 2; + break; + } + + return 0; +} + +void castle_start_siege(int empire, int tower_count) +{ + if (CASTLE_SIEGE_NONE != GET_SIEGE_STATE()) + return; + + GET_SIEGE_STATE() = CASTLE_SIEGE_STRUGGLE; + GET_SIEGE_EMPIRE() = empire; + + castle_spawn_tower(empire, tower_count); + + /* Ÿ̸ */ + { + castle_event_info* info = AllocEventInfo(); + + info->empire = empire; + info->pulse = 0; + + GET_SIEGE_EVENT(empire) = event_create(castle_siege_event, info, /*5*/SIEGE_EVENT_PULSE); + } + + /* ƾ ȯ Ÿ̸ */ + { + castle_stone_event_info* info = AllocEventInfo(); + + info->spawn_count = 0; + info->empire = empire; + + GET_STONE_EVENT(empire) = event_create(castle_stone_event, info, /* 1 */PASSES_PER_SEC(1)); + } +} + +void castle_end_siege() +{ + GET_SIEGE_EMPIRE() = EMPIRE_NONE; + GET_SIEGE_STATE() = CASTLE_SIEGE_NONE; + + DO_ALL_EMPIRE(empire) + { + if (GET_SIEGE_EVENT(empire)) + { + event_cancel(&GET_SIEGE_EVENT(empire)); + } + + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + { + LPCHARACTER npc = GET_TOWER(empire, i); + M2_DESTROY_CHARACTER(npc); + GET_TOWER(empire, i) = NULL; + } + } + } +} + + +LPCHARACTER castle_spawn_frog(int empire) +{ + int dir = 1; + long map_index = FN_castle_map_index(empire); + + /* Ȳݵβ ȯ ֳ? */ + POSITION *empty_pos = FN_empty_frog_pos(empire); + if (NULL == empty_pos) + return NULL; + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return NULL; + DWORD x = sectree_map->m_setting.iBaseX + 100*empty_pos->x; + DWORD y = sectree_map->m_setting.iBaseY + 100*empty_pos->y; + + LPCHARACTER frog = CHARACTER_MANAGER::instance().SpawnMob(CASTLE_FROG_VNUM, map_index, + x, y, 0 , + false, dir); + if (frog) + { + frog->SetEmpire(empire); + int empty_index = FN_empty_frog_index(empire); + // + GET_FROG(empire, empty_index) = frog; + return frog; + } + return NULL; +} + +LPCHARACTER castle_spawn_frog_force(int empire, int empty_index) +{ + int dir = 1; + long map_index = FN_castle_map_index(empire); + + POSITION *empty_pos = &GET_FROG_POS(empire, empty_index); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + { + return NULL; + } + DWORD x = sectree_map->m_setting.iBaseX + 100*empty_pos->x; + DWORD y = sectree_map->m_setting.iBaseY + 100*empty_pos->y; + + LPCHARACTER frog = CHARACTER_MANAGER::instance().SpawnMob(CASTLE_FROG_VNUM, map_index, + x, y, 0 , + false, dir); + if (frog) + { + frog->SetEmpire(empire); + GET_FROG(empire, empty_index) = frog; + return frog; + } + return NULL; +} + + +LPCHARACTER castle_spawn_guard(int empire, DWORD group_vnum, int region_index) +{ + LPCHARACTER mob; + int sx, sy, ex, ey; + long map_index = FN_castle_map_index(empire); + + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return NULL; + + if (castle_guard_count(empire, region_index) >= MAX_CASTLE_GUARD_PER_REGION) + return NULL; + + sx = sectree_map->m_setting.iBaseX + 100*GET_GUARD_REGION(empire, region_index).sx; + sy = sectree_map->m_setting.iBaseY + 100*GET_GUARD_REGION(empire, region_index).sy; + ex = sectree_map->m_setting.iBaseX + 100*GET_GUARD_REGION(empire, region_index).ex; + ey = sectree_map->m_setting.iBaseY + 100*GET_GUARD_REGION(empire, region_index).ey; + + mob = CHARACTER_MANAGER::instance().SpawnGroup(group_vnum, map_index, + sx, sy, ex, ey); + if (mob) + { + mob->SetEmpire(empire); + + int pos = FN_empty_guard_pos(empire, region_index); + GET_GUARD(empire, region_index, pos) = mob; + GET_GUARD_GROUP(empire, region_index, pos) = group_vnum; + } + + return mob; +} + + +static DWORD FN_random_tower() +{ + DWORD vnum[5] = + { + 11506, + 11507, + 11508, + 11509, + 11510 + }; + + int index = number(0, 4); + return vnum[index]; +} + +static void FN_spawn_tower(int empire, LPSECTREE_MAP sectree_map) +{ + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + continue; + + DWORD sx = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(empire).sx; + DWORD sy = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(empire).sy; + DWORD ex = sectree_map->m_setting.iBaseX + 100 * GET_TOWER_REGION(empire).ex; + DWORD ey = sectree_map->m_setting.iBaseY + 100 * GET_TOWER_REGION(empire).ey; + + GET_TOWER(empire, i) = + CHARACTER_MANAGER::instance().SpawnMobRange(FN_random_tower(), + FN_castle_map_index(empire), + sx, sy, ex, ey); + GET_TOWER(empire, i)->SetEmpire(empire); + return; + } +} + +bool castle_spawn_tower(int empire, int tower_count) +{ + int map_index = FN_castle_map_index(empire); + SECTREE_MAP *sectree_map = SECTREE_MANAGER::instance().GetMap(map_index); + if (NULL == sectree_map) + return false; + + // ʱȭ + DO_ALL_TOWER(i) + { + if (GET_TOWER(empire, i)) + GET_TOWER(empire, i)->Dead(NULL, true); + GET_TOWER(empire, i) = NULL; + } + + int spawn_count = MINMAX(MIN_CASTLE_TOWER, tower_count, MAX_CASTLE_TOWER); // 5 ~ 10 + + for (int j = 0; j < spawn_count; ++j) + { + FN_spawn_tower(empire, sectree_map); + } + + // broad cast + { + char buf[1024]; + snprintf(buf, sizeof(buf), LC_TEXT("%s ˸ ȭ Ÿϴ."), EMPIRE_NAME(empire)); + BroadcastNotice(buf); + } + return true; +} + +/* 񺴸 ܼϰ Ը . */ +void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer) +{ + int empire = ch->GetEmpire(); + + for (int region_index = 0; region_index < MAX_CASTLE_GUARD_REGION; ++region_index) + { + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if (GET_GUARD(empire, region_index, i) == ch) + { + GET_GUARD(empire, region_index, i) = NULL; + GET_GUARD_GROUP(empire, region_index, i) = 0; + } + } + } + + castle_save(); +} + + +/* Ȳ β killer 1õ */ +void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer) +{ + if (NULL == ch || NULL == killer) + return; + + int empire = ch->GetEmpire(); + + DO_ALL_FROG(i) + { + if (ch == GET_FROG(empire, i)) + { + GET_FROG(empire, i) = NULL; + + killer->PointChange(POINT_GOLD, 10000000 /*1õ*/, true); + //CMonarch::instance().SendtoDBAddMoney(30000000/*3õ*/, killer->GetEmpire(), killer); + castle_save(); + return; + } + } +} + +/* ȭ (?) */ +void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer) +{ + char buf[1024] = {0}; + + if (NULL == ch || NULL == killer) + return; + + int killer_empire = killer->GetEmpire(); + + switch (GET_SIEGE_STATE()) + { + case CASTLE_SIEGE_NONE: + break; + + case CASTLE_SIEGE_STRUGGLE: + case CASTLE_SIEGE_END: + { + int siege_end = true; + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ı߽ϴ."), EMPIRE_NAME(killer_empire)); + BroadcastNotice(buf); + + LogManager::instance().CharLog(killer, 0, "CASTLE_TORCH_KILL", ""); + + DO_ALL_TOWER(i) + { + if (ch == GET_TOWER(GET_SIEGE_EMPIRE(), i)) + GET_TOWER(GET_SIEGE_EMPIRE(), i) = NULL; + } + + DO_ALL_TOWER(i) + { + if (GET_TOWER(GET_SIEGE_EMPIRE(), i)) + siege_end = false; + } + + if (siege_end) + { + if (GET_SIEGE_STATE() == CASTLE_SIEGE_STRUGGLE) + { + snprintf(buf, sizeof(buf), LC_TEXT("%s Ͽ £ йϿϴ.."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + } + else + { + snprintf(buf, sizeof(buf), LC_TEXT("%s ȭ ıϿϴ."), EMPIRE_NAME(GET_SIEGE_EMPIRE())); + BroadcastNotice(buf); + } + castle_end_siege(); + } + } + break; + } +} + + +int castle_guard_count(int empire, int region_index) +{ + int count = 0; + + for (int i = 0; i < MAX_CASTLE_GUARD_PER_REGION; ++i) + { + if ( GET_GUARD(empire, region_index, i) ) + ++count; + } + return count; +} + + +int castle_frog_count(int empire) +{ + int count = 0; + DO_ALL_FROG(i) + { + if (GET_FROG(empire, i)) + ++count; + } + return count; +} + +bool castle_is_guard_vnum(DWORD vnum) +{ + switch (vnum) + { + /* â */ + case 11112: + case 11114: + case 11116: + /* ߱ â */ + case 11106: + case 11108: + case 11110: + /* ϱ â */ + case 11100: + case 11102: + case 11104: + /* Ȱ */ + case 11113: + case 11115: + case 11117: + /* ߱ Ȱ */ + case 11107: + case 11109: + case 11111: + /* ϱ Ȱ */ + case 11101: + case 11103: + case 11105: + return true; + } + + return false; +} + +int castle_cost_of_hiring_guard(DWORD group_vnum) +{ + switch (group_vnum) + { + /* ϱ */ + case 9501: /* ż â */ + case 9511: /* â */ + case 9521: /* õ â */ + + case 9502: /* ż Ȱ */ + case 9512: /* Ȱ */ + case 9522: /* õ Ȱ */ + return (100*10000); + + /* ߱ */ + case 9503: /* ż â */ + case 9513: /* â */ + case 9523: /* õ â */ + + case 9504: /* ż Ȱ */ + case 9514: /* Ȱ */ + case 9524: /* õ Ȱ */ + return (300*10000); + + /* */ + case 9505: /* ż â */ + case 9515: /* â */ + case 9525: /* õ â */ + + case 9506: /* ż Ȱ */ + case 9516: /* Ȱ */ + case 9526: /* õ Ȱ */ + return (1000*10000); + } + + return 0; +} + +bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim) +{ + if (NULL == ch || NULL == victim) + return false; + + if (false == FN_is_castle_map(ch->GetMapIndex())) + return false; + + if (ch->IsPC() && victim->IsPC()) + return true; + + if (CASTLE_SIEGE_END == GET_SIEGE_STATE()) + { + // ȭ ĥ + if (castle_is_tower_vnum(victim->GetRaceNum())) + { + if (ch->GetEmpire() == victim->GetEmpire()) + return true; + else + return false; + } + } + + // ı Ұ + if (ch->GetEmpire() == victim->GetEmpire()) + return false; + + return true; +} + +bool castle_frog_to_empire_money(LPCHARACTER ch) +{ + if (NULL == ch) + return false; + + int empire = ch->GetEmpire(); + + DO_ALL_FROG(i) + { + if (NULL == GET_FROG(empire, i)) + continue; + + LPCHARACTER npc = GET_FROG(empire, i); + + if (false == CMonarch::instance().SendtoDBAddMoney(CASTLE_FROG_PRICE, empire, ch)) + return false; + + GET_FROG(empire, i) = NULL; // + npc->Dead(/*killer*/NULL, /*immediate_dead*/true); + return true; + } + + return false; +} + +bool castle_is_my_castle(int empire, int map_index) +{ + switch (empire) + { + case EMPIRE_RED: return (181 == map_index); + case EMPIRE_YELLOW: return (183 == map_index); + case EMPIRE_BLUE: return (182 == map_index); + } + return false; +} + +bool castle_is_tower_vnum(DWORD vnum) +{ + switch (vnum) + { + case 11506: + case 11507: + case 11508: + case 11509: + case 11510: + return true; + } + return false; +} + diff --git a/game/src/castle.h b/game/src/castle.h new file mode 100644 index 0000000..b1439fd --- /dev/null +++ b/game/src/castle.h @@ -0,0 +1,77 @@ +/********************************************************************* + * date : 2007.04.07 + * file : castle.h + * author : mhh + * description : + */ + +#ifndef _castle_h_ +#define _castle_h_ + + +#define MAX_CASTLE_GUARD_REGION 4 // ġ +#define MAX_CASTLE_GUARD_PER_REGION 10 // ġҼִ 񺴱׷ +#define MAX_CASTLE_FROG 20 // Ȳ β +#define MAX_CASTLE_TOWER 10 // ȭ ִ ȯ +#define MIN_CASTLE_TOWER 5 // ȭ ּ ȯ + + +#define CASTLE_FROG_PRICE 100000000 // Ȳݵβ (1) +#define CASTLE_FROG_VNUM 11505 // Ȳݵβ ȣ +//#define CASTLE_TOWER_VNUM 11506 // ȭ ȣ +#define IS_CASTLE_MAP(map) (181==(map)||182==(map)||(183)==(map)) +//#define IS_CASTLE_TOWER(vnum) (11506==(vnum)||11507==(vnum)||11508==(vnum)||11509==(vnum) || 11510==(vnum)) + + +enum CASTLE_STATE +{ + CASTLE_SIEGE_NONE, // ȭ + CASTLE_SIEGE_STRUGGLE, // + CASTLE_SIEGE_END // ߴٸ 1ð ȭ ִ. +}; + + +struct CASTLE_DATA +{ + LPCHARACTER frog[MAX_CASTLE_FROG]; // Ȳݵβ + + LPCHARACTER guard[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; // + DWORD guard_group[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; // + + LPCHARACTER tower[MAX_CASTLE_TOWER]; // ȭ + + LPEVENT siege_event; + LPEVENT stone_event; +}; + + + + +/* extern functions */ +bool castle_boot(); +void castle_save(); +int castle_siege(int empire, int tower_count); +void castle_start_siege(int empire, int tower_count); +void castle_end_siege(); + +LPCHARACTER castle_spawn_frog(int empire); +LPCHARACTER castle_spawn_guard(int empire, DWORD group_vnum, int region_index); +bool castle_spawn_tower(int empire, int tower_count); + +void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer); +void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer); +void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer); + +int castle_guard_count(int empire, int region_index); +int castle_frog_count(int empire); + +bool castle_is_guard_vnum(DWORD vnum); +int castle_cost_of_hiring_guard(DWORD vnum); +bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim); + +bool castle_frog_to_empire_money(LPCHARACTER ch); +bool castle_is_my_castle(int empire, int map_index); +bool castle_is_tower_vnum(DWORD vnum); + +#endif /* _castle_h_ */ + diff --git a/game/src/char.cpp b/game/src/char.cpp new file mode 100644 index 0000000..2f16362 --- /dev/null +++ b/game/src/char.cpp @@ -0,0 +1,7225 @@ +#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 "XTrapManager.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; +} + +// DynamicCharacterPtr member function definitions + +LPCHARACTER DynamicCharacterPtr::Get() const { + LPCHARACTER p = NULL; + if (is_pc) { + p = CHARACTER_MANAGER::instance().FindByPID(id); + } else { + p = CHARACTER_MANAGER::instance().Find(id); + } + return p; +} + +DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) { + if (character == NULL) { + Reset(); + return *this; + } + if (character->IsPC()) { + is_pc = true; + id = character->GetPlayerID(); + } else { + is_pc = false; + id = character->GetVID(); + } + return *this; +} + +CHARACTER::CHARACTER() +{ + m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty); + m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty); + m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty); + + Initialize(); +} + +CHARACTER::~CHARACTER() +{ + Destroy(); +} + +void CHARACTER::Initialize() +{ + CEntity::Initialize(ENTITY_CHARACTER); + + m_bNoOpenedShop = true; + + m_bOpeningSafebox = false; + + m_fSyncTime = get_float_time()-3; + m_dwPlayerID = 0; + m_dwKillerPID = 0; + + m_iMoveCount = 0; + + m_pkRegen = NULL; + regen_id_ = 0; + m_posRegen.x = m_posRegen.y = m_posRegen.z = 0; + m_posStart.x = m_posStart.y = 0; + m_posDest.x = m_posDest.y = 0; + m_fRegenAngle = 0.0f; + + m_pkMobData = NULL; + m_pkMobInst = NULL; + + m_pkShop = NULL; + m_pkChrShopOwner = NULL; + m_pkMyShop = NULL; + m_pkExchange = NULL; + m_pkParty = NULL; + m_pkPartyRequestEvent = NULL; + + m_pGuild = NULL; + + m_pkChrTarget = NULL; + + m_pkMuyeongEvent = NULL; + + m_pkWarpNPCEvent = NULL; + m_pkDeadEvent = NULL; + m_pkStunEvent = NULL; + m_pkSaveEvent = NULL; + m_pkRecoveryEvent = NULL; + m_pkTimedEvent = NULL; + m_pkFishingEvent = NULL; + m_pkWarpEvent = NULL; + + // MINING + m_pkMiningEvent = NULL; + // END_OF_MINING + + m_pkPoisonEvent = NULL; + 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(); + } + + // 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((pTable+n)->price); + } + + nTotalMoney += static_cast(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 itemkind; // , first: vnum, second: + // END_OF_MYSHOP_PRICE_LIST + + std::set cont; + for (BYTE i = 0; i < bItemCount; ++i) + { + if (cont.find((pTable + i)->pos) != cont.end()) + { + sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName()); + return; + } + + // ANTI_GIVE, ANTI_MYSHOP check + LPITEM pkItem = GetItem((pTable + i)->pos); + + if (pkItem) + { + const TItemTable * item_table = pkItem->GetProto(); + + if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭ λ Ǹ ϴ.")); + return; + } + + if (pkItem->IsEquipped() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" λ Ǹ ϴ.")); + return; + } + + if (true == pkItem->isLocked()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" λ Ǹ ϴ.")); + return; + } + + // MYSHOP_PRICE_LIST + itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount(); + // END_OF_MYSHOP_PRICE_LIST + } + + cont.insert((pTable + i)->pos); + } + + // MYSHOP_PRICE_LIST + // ҽŲ. + if (CountSpecifyItem(71049)) { // ʰ Ѵ. + + // + // ϱ Ŷ DB ijÿ . + // + 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); + } + + CXTrapManager::instance().DestroyClientSession(this); + + 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 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(BYTE level) +{ + m_points.level = level; + + if (IsPC()) + { + if (level < PK_PROTECT_LEVEL) + SetPKMode(PK_MODE_PROTECT); + else if (GetGMLevel() != GM_PLAYER) + SetPKMode(PK_MODE_PROTECT); + else if (m_bPKMode == PK_MODE_PROTECT) + SetPKMode(PK_MODE_PEACE); + } +} + +void CHARACTER::SetEmpire(BYTE bEmpire) +{ + m_bEmpire = bEmpire; +} + +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: ϴ ijͰ 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( event->info ); + if ( info == NULL ) + { + sys_err( "kill_ore_load_even> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + + ch->m_pkMiningEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +void CHARACTER::SetProto(const CMob * pkMob) +{ + if (m_pkMobInst) + M2_DELETE(m_pkMobInst); + + m_pkMobData = pkMob; + m_pkMobInst = M2_NEW CMobInstance; + + m_bPKMode = PK_MODE_FREE; + + const TMobTable * t = &m_pkMobData->m_table; + + m_bCharType = t->bType; + + SetLevel(t->bLevel); + SetEmpire(t->bEmpire); + + SetExp(t->dwExp); + SetRealPoint(POINT_ST, t->bStr); + SetRealPoint(POINT_DX, t->bDex); + SetRealPoint(POINT_HT, t->bCon); + SetRealPoint(POINT_IQ, t->bInt); + + ComputePoints(); + + SetHP(GetMaxHP()); + SetSP(GetMaxSP()); + + //////////////////// + m_pointsInstant.dwAIFlag = t->dwAIFlag; + SetImmuneFlag(t->dwImmuneFlag); + + AssignTriggers(t); + + ApplyMobAttribute(t); + + if (IsStone()) + { + DetermineDropMetinStone(); + } + + if (IsWarp() || IsGoto()) + { + StartWarpNPCEvent(); + } + + CHARACTER_MANAGER::instance().RegisterRaceNumMap(this); + + // XXX X-mas santa hardcoding + if (GetRaceNum() == xmas::MOB_SANTA_VNUM) + { + SetPoint(POINT_ATT_GRADE_BONUS, 10); + 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(); + + 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(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( event->info ); + if ( info == NULL ) + { + sys_err( "recovery_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->IsPC()) + { + // + // ȸ + // + if (ch->IsAffectFlag(AFF_POISON)) + return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle)); + + 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(); + + 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, " %s = NONE", GetName()); + } + else if (IsOverTime(OT_3HOUR)) + { + amount = (amount / 2); + dev_log(LOG_DEB0, " %s = 3HOUR", GetName()); + } + else if (IsOverTime(OT_5HOUR)) + { + amount = 0; + dev_log(LOG_DEB0, " %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(GetGold()) + static_cast(amount); + + if (GOLD_MAX <= nTotalMoney) + { + sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName()); + LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", ""); + return; + } + + // ûҳ⺸ȣ + if (LC_IsNewCIBN() && amount > 0) + { + if (IsOverTime(OT_NONE)) + { + dev_log(LOG_DEB0, " %s = NONE", GetName()); + } + else if (IsOverTime(OT_3HOUR)) + { + amount = (amount / 2); + dev_log(LOG_DEB0, " %s = 3HOUR", GetName()); + } + else if (IsOverTime(OT_5HOUR)) + { + amount = 0; + dev_log(LOG_DEB0, " %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::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( event->info ); + if ( info == NULL ) + { + sys_err( "save_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + sys_log(1, "SAVE_EVENT: %s", ch->GetName()); + ch->Save(); + ch->FlushDelayedSaveItem(); + return (save_event_second_cycle); +} + +void CHARACTER::StartSaveEvent() +{ + if (m_pkSaveEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle); +} + +void CHARACTER::MonsterLog(const char* format, ...) +{ + if (!test_server) + return; + + if (IsPC()) + return; + + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "%u)", (DWORD)GetVID()); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + va_list args; + + va_start(args, format); + + int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args); + + if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len) + len += (sizeof(chatbuf) - len) - 1; + else + len += len2; + + // \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(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("·ξ"); +} + + +// ij νϽ Ʈ Լ. +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); + 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; ///< Ƽ ij PID + DWORD dwLeaderPID; ///< Ƽ PID + + TPartyJoinEventInfo() + : dwGuestPID( 0 ) + , dwLeaderPID( 0 ) + { + } +} ; + +EVENTFUNC(party_request_event) +{ + TPartyJoinEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "party_request_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID); + + if (ch) + { + sys_log(0, "PartyRequestEvent %s", ch->GetName()); + ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); + ch->SetPartyRequestEvent(NULL); + } + + return 0; +} + +bool CHARACTER::RequestToParty(LPCHARACTER leader) +{ + if (leader->GetParty()) + leader = leader->GetParty()->GetLeaderCharacter(); + + if (!leader) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ƽ ° ƴ϶ û ϴ.")); + return false; + } + + if (m_pkPartyRequestEvent) + return false; + + if (!IsPC() || !leader->IsPC()) + return false; + + if (leader->IsBlockMode(BLOCK_PARTY_REQUEST)) + return false; + + PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this); + + switch (errcode) + { + case PERR_NONE: + break; + + case PERR_SERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return false; + + case PERR_DIFFEMPIRE: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ٸ Ƽ ̷ ϴ.")); + return false; + + case PERR_DUNGEON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); + return false; + + case PERR_OBSERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); + return false; + + case PERR_LVBOUNDARY: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); + return false; + + case PERR_LOWLEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); + return false; + + case PERR_HILEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); + return false; + + case PERR_ALREADYJOIN: + return false; + + case PERR_PARTYISFULL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + return false; + + default: + sys_err("Do not process party join error(%d)", errcode); + return false; + } + + TPartyJoinEventInfo* info = AllocEventInfo(); + + info->dwGuestPID = GetPlayerID(); + info->dwLeaderPID = leader->GetPlayerID(); + + SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10))); + + leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Կ Ƽ û ߽ϴ."), leader->GetName()); + return true; +} + +void CHARACTER::DenyToParty(LPCHARACTER member) +{ + sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent)); + + if (!member->m_pkPartyRequestEvent) + return; + + TPartyJoinEventInfo * info = dynamic_cast(member->m_pkPartyRequestEvent->info); + + if (!info) + { + sys_err( "CHARACTER::DenyToParty> Null pointer" ); + return; + } + + if (info->dwGuestPID != member->GetPlayerID()) + return; + + if (info->dwLeaderPID != GetPlayerID()) + return; + + event_cancel(&member->m_pkPartyRequestEvent); + + member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +void CHARACTER::AcceptToParty(LPCHARACTER member) +{ + sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent)); + + if (!member->m_pkPartyRequestEvent) + return; + + TPartyJoinEventInfo * info = dynamic_cast(member->m_pkPartyRequestEvent->info); + + if (!info) + { + sys_err( "CHARACTER::AcceptToParty> Null pointer" ); + return; + } + + if (info->dwGuestPID != member->GetPlayerID()) + return; + + if (info->dwLeaderPID != GetPlayerID()) + return; + + event_cancel(&member->m_pkPartyRequestEvent); + + if (!GetParty()) + member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ƽ ʽϴ.")); + else + { + if (GetPlayerID() != GetParty()->GetLeaderPID()) + return; + + PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member); + switch (errcode) + { + case PERR_NONE: member->PartyJoin(this); return; + case PERR_SERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); break; + case PERR_DUNGEON: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); break; + case PERR_OBSERVER: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); break; + case PERR_LVBOUNDARY: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); break; + case PERR_LOWLEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); break; + case PERR_HILEVEL: member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); break; + case PERR_ALREADYJOIN: break; + case PERR_PARTYISFULL: { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ο ʰϿ Ƽ ϴ.")); + break; + } + default: sys_err("Do not process party join error(%d)", errcode); + } + } + + member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +/** + * Ƽ ʴ event callback Լ. + * event ߵϸ ʴ óѴ. + */ +EVENTFUNC(party_invite_event) +{ + TPartyJoinEventInfo * pInfo = dynamic_cast( event->info ); + + if ( pInfo == NULL ) + { + sys_err( "party_invite_event> Null pointer" ); + return 0; + } + + LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID); + + if (pchInviter) + { + sys_log(1, "PartyInviteEvent %s", pchInviter->GetName()); + pchInviter->PartyInviteDeny(pInfo->dwGuestPID); + } + + return 0; +} + +void CHARACTER::PartyInvite(LPCHARACTER pchInvitee) +{ + if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ʴ ִ ϴ.")); + return; + } + else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> %s Ƽ ź Դϴ."), pchInvitee->GetName()); + return; + } + + PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee); + + switch (errcode) + { + case PERR_NONE: + break; + + case PERR_SERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return; + + case PERR_DIFFEMPIRE: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ٸ Ƽ ̷ ϴ.")); + return; + + case PERR_DUNGEON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ʴ븦 ϴ.")); + return; + + case PERR_OBSERVER: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> 忡 Ƽ ʴ븦 ϴ.")); + return; + + case PERR_LVBOUNDARY: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> -30 ~ +30 ̳ 游 ʴ ֽϴ.")); + return; + + case PERR_LOWLEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ְ 30 ʴ ϴ.")); + return; + + case PERR_HILEVEL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ 30 ʴ ϴ.")); + return; + + case PERR_ALREADYJOIN: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̹ %s Ƽ ֽϴ."), pchInvitee->GetName()); + return; + + case PERR_PARTYISFULL: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ̻ Ƽ ʴ ϴ.")); + return; + + default: + sys_err("Do not process party join error(%d)", errcode); + return; + } + + if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID())) + return; + + // + // EventMap ̺Ʈ ߰ + // + TPartyJoinEventInfo* info = AllocEventInfo(); + + 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; + 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; iGetVnum(); + p.equips[i].count = item->GetCount(); + + thecore_memcpy(p.equips[i].alSockets, item->GetSockets(), sizeof(p.equips[i].alSockets)); + thecore_memcpy(p.equips[i].aAttr, item->GetAttributes(), sizeof(p.equips[i].aAttr)); + } + else + { + p.equips[i].vnum = 0; + } + } + ch->GetDesc()->Packet(&p, 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( event->info ); + if ( info == NULL ) + { + sys_err( "warp_npc_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->GetSectree()) + { + ch->m_pkWarpNPCEvent = NULL; + return 0; + } + + FuncCheckWarp f(ch); + if (f.Valid()) + ch->GetSectree()->ForEachAround(f); + + return passes_per_sec / 2; +} + + +void CHARACTER::StartWarpNPCEvent() +{ + if (m_pkWarpNPCEvent) + return; + + if (!IsWarp() && !IsGoto()) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2); +} + +void CHARACTER::SyncPacket() +{ + TEMP_BUFFER buf; + + TPacketCGSyncPositionElement elem; + + elem.dwVID = GetVID(); + elem.lX = GetX(); + elem.lY = GetY(); + + TPacketGCSyncPosition pack; + + pack.bHeader = HEADER_GC_SYNC_POSITION; + pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem); + + buf.write(&pack, 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( event->info ); + if ( info == NULL ) + { + sys_err( "check_speedhack_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (NULL == ch || ch->IsNPC()) + return 0; + + if (IS_SPEED_HACK_PLAYER(ch)) + { + // write hack log + LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count); + + if (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(); + + 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( event->info ); + if ( info == NULL ) + { + sys_err( "destroy_when_idle_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + + if (ch->GetVictim()) + { + return PASSES_PER_SEC(300); + } + + sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName()); + + ch->m_pkDestroyWhenIdleEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +void CHARACTER::StartDestroyWhenIdleEvent() +{ + if (m_pkDestroyWhenIdleEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300)); +} + +void CHARACTER::SetComboSequence(BYTE seq) +{ + m_bComboSequence = seq; +} + +BYTE CHARACTER::GetComboSequence() const +{ + return m_bComboSequence; +} + +void CHARACTER::SetLastComboTime(DWORD time) +{ + m_dwLastComboTime = time; +} + +DWORD CHARACTER::GetLastComboTime() const +{ + return m_dwLastComboTime; +} + +void CHARACTER::SetValidComboInterval(int interval) +{ + m_iValidComboInterval = interval; +} + +int CHARACTER::GetValidComboInterval() const +{ + return m_iValidComboInterval; +} + +BYTE CHARACTER::GetComboIndex() const +{ + return m_bComboIndex; +} + +void CHARACTER::IncreaseComboHackCount(int k) +{ + m_iComboHackCount += k; + + if (m_iComboHackCount >= 10) + { + if (GetDesc()) + if (GetDesc()->DelayedDisconnect(number(2, 7))) + { + sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount); + LogManager::instance().HackLog("Combo", this); + } + } +} + +void CHARACTER::ResetComboHackCount() +{ + m_iComboHackCount = 0; +} + +void CHARACTER::SkipComboAttackByTime(int interval) +{ + m_dwSkipComboAttackByTime = get_dword_time() + interval; +} + +DWORD CHARACTER::GetSkipComboAttackByTime() const +{ + return m_dwSkipComboAttackByTime; +} + +void CHARACTER::ResetChatCounter() +{ + m_bChatCounter = 0; +} + +BYTE CHARACTER::IncreaseChatCounter() +{ + return ++m_bChatCounter; +} + +BYTE CHARACTER::GetChatCounter() const +{ + return m_bChatCounter; +} + +// ̳ ٸ Ÿ ֳ? +bool CHARACTER::IsRiding() const +{ + return IsHorseRiding() || GetMountVnum(); +} + +bool CHARACTER::CanWarp() const +{ + const int iPulse = thecore_pulse(); + const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime); + + if ((iPulse - GetSafeboxLoadTime()) < limit_time) + return false; + + if ((iPulse - GetExchangeTime()) < limit_time) + return false; + + if ((iPulse - GetMyShopTime()) < limit_time) + return false; + + if ((iPulse - GetRefineTime()) < limit_time) + return false; + + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + return false; + + return true; +} + +DWORD CHARACTER::GetNextExp() const +{ + if (PLAYER_EXP_TABLE_MAX < GetLevel()) + return 2500000000; + 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); +} \ No newline at end of file diff --git a/game/src/char.h b/game/src/char.h new file mode 100644 index 0000000..c9d8677 --- /dev/null +++ b/game/src/char.h @@ -0,0 +1,2054 @@ +#ifndef __INC_METIN_II_CHAR_H__ +#define __INC_METIN_II_CHAR_H__ + +#include + +#include "../../common/stl.h" +#include "entity.h" +#include "FSM.h" +#include "horse_rider.h" +#include "vid.h" +#include "constants.h" +#include "affect.h" +#include "affect_flag.h" +#include "cube.h" +#include "mining.h" + +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), +}; + +// Dynamically evaluated CHARACTER* equivalent. +// Referring to SCharDeadEventInfo. +struct DynamicCharacterPtr { + DynamicCharacterPtr() : is_pc(false), id(0) {} + DynamicCharacterPtr(const DynamicCharacterPtr& o) + : is_pc(o.is_pc), id(o.id) {} + + // Returns the LPCHARACTER found in CHARACTER_MANAGER. + LPCHARACTER Get() const; + // Clears the current settings. + void Reset() { + is_pc = false; + id = 0; + } + + // Basic assignment operator. + DynamicCharacterPtr& operator=(const DynamicCharacterPtr& rhs) { + is_pc = rhs.is_pc; + id = rhs.id; + return *this; + } + // Supports assignment with LPCHARACTER type. + DynamicCharacterPtr& operator=(LPCHARACTER character); + // Supports type casting to LPCHARACTER. + operator LPCHARACTER() const { + return Get(); + } + + bool is_pc; + uint32_t id; +}; + +/* ϴ */ +typedef struct character_point +{ + long points[POINT_MAX_NUM]; + + BYTE job; + BYTE voice; + + BYTE level; + DWORD exp; + long gold; + + int hp; + int sp; + + int iRandomHP; + int iRandomSP; + + int stamina; + + BYTE skill_group; +} CHARACTER_POINT; + +/* ʴ ij */ +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 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 m_stateMove; + CStateTemplate m_stateBattle; + CStateTemplate m_stateIdle; + + public: + virtual void StateMove(); + virtual void StateBattle(); + virtual void StateIdle(); + virtual void StateFlag(); + virtual void StateFlagBase(); + void StateHorse(); + + protected: + // STATE_IDLE_REFACTORING + void __StateIdle_Monster(); + void __StateIdle_Stone(); + void __StateIdle_NPC(); + // END_OF_STATE_IDLE_REFACTORING + + public: + DWORD GetAIFlag() const { return m_pointsInstant.dwAIFlag; } + + void SetAggressive(); + bool IsAggressive() const; + + void SetCoward(); + bool IsCoward() const; + void CowardEscape(); + + void SetNoAttackShinsu(); + bool IsNoAttackShinsu() const; + + void SetNoAttackChunjo(); + bool IsNoAttackChunjo() const; + + void SetNoAttackJinno(); + bool IsNoAttackJinno() const; + + void SetAttackMob(); + bool IsAttackMob() const; + + virtual void BeginStateEmpty(); + virtual void EndStateEmpty() {} + + void RestartAtSamePos(); + + protected: + DWORD m_dwStateDuration; + ////////////////////////////////////////////////////////////////////////////////// + + public: + CHARACTER(); + virtual ~CHARACTER(); + + void Create(const char * c_pszName, DWORD vid, bool isPC); + void Destroy(); + + void Disconnect(const char * c_pszReason); + + protected: + void Initialize(); + + ////////////////////////////////////////////////////////////////////////////////// + // Basic Points + public: + DWORD GetPlayerID() const { return m_dwPlayerID; } + + void SetPlayerProto(const TPlayerTable * table); + void CreatePlayerProto(TPlayerTable & tab); // + + void SetProto(const CMob * c_pkMob); + 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(BYTE level); + + BYTE GetGMLevel() const; + BOOL IsGM() const; + void SetGMLevel(); + + DWORD GetExp() const { return m_points.exp; } + void SetExp(DWORD exp) { m_points.exp = exp; } + DWORD GetNextExp() const; + LPCHARACTER DistributeExp(); // Ѵ. + void DistributeHP(LPCHARACTER pkKiller); + void DistributeSP(LPCHARACTER pkKiller, int iMethod=0); + + 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(); // ijͶ ش. + + 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 & GetAffectContainer() const { return m_list_pkAffect; } + bool RemoveAffect(CAffect * pkAff); + + protected: + bool m_bIsLoadedAffect; + TAffectFlag m_afAffectFlag; + std::list m_list_pkAffect; + + public: + // PARTY_JOIN_BUG_FIX + void SetParty(LPPARTY pkParty); + LPPARTY GetParty() const { return m_pkParty; } + + bool RequestToParty(LPCHARACTER leader); + void DenyToParty(LPCHARACTER member); + void AcceptToParty(LPCHARACTER member); + + /// ڽ Ƽ ٸ character ʴѴ. + /** + * @param pchInvitee ʴ character. Ƽ ̾ Ѵ. + * + * character ° Ƽ ʴϰ ʴ ִ ° ƴ϶ ʴϴ ijͿ شϴ ä ޼ Ѵ. + */ + 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, ///< ijͰ + PERR_OBSERVER, ///< + PERR_LVBOUNDARY, ///< ijͿ ̰ + PERR_LOWLEVEL, ///< Ƽ ְ 30 + PERR_HILEVEL, ///< Ƽ 30 + PERR_ALREADYJOIN, ///< Ƽ ijͰ ̹ Ƽ + PERR_PARTYISFULL, ///< Ƽο ʰ + PERR_SEPARATOR, ///< Error type separator. + PERR_DIFFEMPIRE, ///< ijͿ ٸ + 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: ʴ ij PID + * value: event pointer + * + * ʴ ij͵鿡 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 ij÷ ޾ƿ Ʈ ϰ óѴ. + /** + * @param [in] p Ʈ Ŷ + * + * ó UseSilkBotary DB ij÷ Ʈ ûϰ + * Լ ܺ óѴ. + */ + void UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p); + // END_OF_MYSHOP_PRICE_LIST + + bool UseItemEx(LPITEM item, TItemPos DestCell); + bool UseItem(TItemPos Cell, TItemPos DestCell = NPOS); + + // ADD_REFINE_BUILDING + bool IsRefineThroughGuild() const; + CGuild * GetRefineGuild() const; + int ComputeRefineFee(int iCost, int iMultiply = 5) const; + void PayRefineFee(int iTotalMoney); + void SetRefineNPC(LPCHARACTER ch); + // END_OF_ADD_REFINE_BUILDING + + bool RefineItem(LPITEM pkItem, LPITEM pkTarget); + bool DropItem(TItemPos Cell, BYTE bCount=0); + bool GiveRecallItem(LPITEM item); + void ProcessRecallItem(LPITEM item); + + // void PotionPacket(int iPotionType); + void EffectPacket(int enumEffectType); + void SpecificEffectPacket(const char filename[128]); + + // ADD_MONSTER_REFINE + bool DoRefine(LPITEM item, bool bMoneyOnly = false); + // END_OF_ADD_MONSTER_REFINE + + bool DoRefineWithScroll(LPITEM item); + bool RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell = -1); + + void SetRefineMode(int iAdditionalCell = -1); + void ClearRefineMode(); + + bool GiveItem(LPCHARACTER victim, TItemPos Cell); + bool CanReceiveItem(LPCHARACTER from, LPITEM item) const; + void ReceiveItem(LPCHARACTER from, LPITEM item); + bool GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector &dwItemVnums, + std::vector &dwItemCounts, std::vector &item_gets, int &count); + + bool MoveItem(TItemPos pos, TItemPos change_pos, BYTE num); + bool PickupItem(DWORD vid); + bool EquipItem(LPITEM item, int iCandidateCell = -1); + bool UnequipItem(LPITEM item); + + // item ִ Ȯϰ, Ұ ϴٸ ijͿ ˷ִ Լ + bool CanEquipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS); + + // item ִ Ȯϰ, Ұ ϴٸ ijͿ ˷ִ Լ + 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& 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 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 m_vec_dwFlyTargets; + TDamageMap m_map_kDamage; //  ijͰ 󸶸ŭ ־°? +// 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 m_SkillDamageBonus; + std::map m_SkillUseInfo; + + //////////////////////////////////////////////////////////////////////////////////////// + // AI related + public: + void AssignTriggers(const TMobTable * table); + LPCHARACTER GetVictim() const; // + void SetVictim(LPCHARACTER pkVictim); + LPCHARACTER GetNearestVictim(LPCHARACTER pkChr); + LPCHARACTER GetProtege() const; // ȣؾ + + bool Follow(LPCHARACTER pkChr, float fMinimumDistance = 150.0f); + bool Return(); + bool IsGuardNPC() const; + bool IsChangeAttackPosition(LPCHARACTER target) const; + void ResetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time() - AI_CHANGE_ATTACK_POISITION_TIME_NEAR;} + void SetChangeAttackPositionTime() { m_dwLastChangeAttackPositionTime = get_dword_time();} + + bool OnIdle(); + + void OnAttack(LPCHARACTER pkChrAttacker); + void OnClick(LPCHARACTER pkChrCauser); + + VID m_kVIDVictim; + + protected: + DWORD m_dwLastChangeAttackPositionTime; + CTrigger m_triggerOnClick; + // End of AI + + //////////////////////////////////////////////////////////////////////////////////////// + // Target + protected: + LPCHARACTER m_pkChrTarget; // Ÿ + CHARACTER_SET m_set_pkChrTargetedBy; // Ÿ ִ + + public: + void SetTarget(LPCHARACTER pkChrTarget); + void BroadcastTargetPacket(); + void ClearTarget(); + void CheckTarget(); + LPCHARACTER GetTarget() const { return m_pkChrTarget; } + + //////////////////////////////////////////////////////////////////////////////////////// + // Safebox + public: + int GetSafeboxSize() const; + void QuerySafeboxSize(); + void SetSafeboxSize(int size); + + CSafebox * GetSafebox() const; + void LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems); + void ChangeSafeboxSize(BYTE bSize); + void CloseSafebox(); + + /// â û + /** + * @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); + + // ij νϽ Ʈ Լ. ̻ ӱ 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 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 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 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: + //ȥ + + // ij affect, quest load DZ 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 ȥ ϳ ٸ, ij ȥ affect, Ȱ ¸ Ѵ. + // + // ClearItem , ijͰ ϰ ִ unequipϴ ٶ, + // ȥ Affect ŵǰ, ᱹ α , ȥ Ȱȭ ʴ´. + // (Unequip α׾ƿ , ƴ .) + // ȥ deactivateŰ ij ȥ Ȱ ´ ǵ帮 ʴ´. + 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; } +}; + +ESex GET_SEX(LPCHARACTER ch); + +#endif diff --git a/game/src/char_affect.cpp b/game/src/char_affect.cpp new file mode 100644 index 0000000..1e94ef1 --- /dev/null +++ b/game/src/char_affect.cpp @@ -0,0 +1,837 @@ + +#include "stdafx.h" + +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "affect.h" +#include "packet.h" +#include "buffer_manager.h" +#include "desc_client.h" +#include "battle.h" +#include "guild.h" +#include "utils.h" +#include "locale_service.h" +#include "lua_incl.h" +#include "arena.h" +#include "horsename_manager.h" +#include "item.h" +#include "DragonSoul.h" + +#define IS_NO_SAVE_AFFECT(type) ((type) == AFFECT_WAR_FLAG || (type) == AFFECT_REVIVE_INVISIBLE || ((type) >= AFFECT_PREMIUM_START && (type) <= AFFECT_PREMIUM_END)) +#define IS_NO_CLEAR_ON_DEATH_AFFECT(type) ((type) == AFFECT_BLOCK_CHAT || ((type) >= 500 && (type) < 600)) + +void SendAffectRemovePacket(LPDESC d, DWORD pid, DWORD type, BYTE point) +{ + TPacketGCAffectRemove ptoc; + ptoc.bHeader = HEADER_GC_AFFECT_REMOVE; + ptoc.dwType = type; + ptoc.bApplyOn = point; + d->Packet(&ptoc, sizeof(TPacketGCAffectRemove)); + + TPacketGDRemoveAffect ptod; + ptod.dwPID = pid; + ptod.dwType = type; + ptod.bApplyOn = point; + db_clientdesc->DBPacket(HEADER_GD_REMOVE_AFFECT, 0, &ptod, sizeof(ptod)); +} + +void SendAffectAddPacket(LPDESC d, CAffect * pkAff) +{ + TPacketGCAffectAdd ptoc; + ptoc.bHeader = HEADER_GC_AFFECT_ADD; + ptoc.elem.dwType = pkAff->dwType; + ptoc.elem.bApplyOn = pkAff->bApplyOn; + ptoc.elem.lApplyValue = pkAff->lApplyValue; + ptoc.elem.dwFlag = pkAff->dwFlag; + ptoc.elem.lDuration = pkAff->lDuration; + ptoc.elem.lSPCost = pkAff->lSPCost; + d->Packet(&ptoc, sizeof(TPacketGCAffectAdd)); +} +//////////////////////////////////////////////////////////////////// +// Affect +CAffect * CHARACTER::FindAffect(DWORD dwType, BYTE bApply) const +{ + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAffect = *it++; + + if (pkAffect->dwType == dwType && (bApply == APPLY_NONE || bApply == pkAffect->bApplyOn)) + return pkAffect; + } + + return NULL; +} + +EVENTFUNC(affect_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "affect_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->UpdateAffect()) + return 0; + else + return passes_per_sec; // 1 +} + +bool CHARACTER::UpdateAffect() +{ + // affect_event ó ƴ, 1¥ ̺Ʈ óϴ + // ̰ ̶ ⼭ ó Ѵ. + if (GetPoint(POINT_HP_RECOVERY) > 0) + { + if (GetMaxHP() <= GetHP()) + { + PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY)); + } + else + { + int iVal = 0; + + if (LC_IsYMIR()) + { + iVal = MIN(GetPoint(POINT_HP_RECOVERY), GetMaxHP() * 9 / 100); + } + else + { + iVal = MIN(GetPoint(POINT_HP_RECOVERY), GetMaxHP() * 7 / 100); + } + + PointChange(POINT_HP, iVal); + PointChange(POINT_HP_RECOVERY, -iVal); + } + } + + if (GetPoint(POINT_SP_RECOVERY) > 0) + { + if (GetMaxSP() <= GetSP()) + PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY)); + else + { + int iVal; + + if (!g_iUseLocale) + iVal = MIN(GetPoint(POINT_SP_RECOVERY), GetMaxSP() * 7 / 100); + else + iVal = MIN(GetPoint(POINT_SP_RECOVERY), GetMaxSP() * 7 / 100); + + PointChange(POINT_SP, iVal); + PointChange(POINT_SP_RECOVERY, -iVal); + } + } + + if (GetPoint(POINT_HP_RECOVER_CONTINUE) > 0) + { + PointChange(POINT_HP, GetPoint(POINT_HP_RECOVER_CONTINUE)); + } + + if (GetPoint(POINT_SP_RECOVER_CONTINUE) > 0) + { + PointChange(POINT_SP, GetPoint(POINT_SP_RECOVER_CONTINUE)); + } + + AutoRecoveryItemProcess( AFFECT_AUTO_HP_RECOVERY ); + AutoRecoveryItemProcess( AFFECT_AUTO_SP_RECOVERY ); + + // ׹̳ ȸ + if (GetMaxStamina() > GetStamina()) + { + int iSec = (get_dword_time() - GetStopTime()) / 3000; + if (iSec) + PointChange(POINT_STAMINA, GetMaxStamina()/1); + } + + + // ProcessAffect affect true Ѵ. + if (ProcessAffect()) + if (GetPoint(POINT_HP_RECOVERY) == 0 && GetPoint(POINT_SP_RECOVERY) == 0 && GetStamina() == GetMaxStamina()) + { + m_pkAffectEvent = NULL; + return false; + } + + return true; +} + +void CHARACTER::StartAffectEvent() +{ + if (m_pkAffectEvent) + return; + + char_event_info* info = AllocEventInfo(); + info->ch = this; + m_pkAffectEvent = event_create(affect_event, info, passes_per_sec); + sys_log(1, "StartAffectEvent %s %p %p", GetName(), this, get_pointer(m_pkAffectEvent)); +} + +void CHARACTER::ClearAffect(bool bSave) +{ + TAffectFlag afOld = m_afAffectFlag; + WORD wMovSpd = GetPoint(POINT_MOV_SPEED); + WORD wAttSpd = GetPoint(POINT_ATT_SPEED); + + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it; + + if (bSave) + { + if ( IS_NO_CLEAR_ON_DEATH_AFFECT(pkAff->dwType) || IS_NO_SAVE_AFFECT(pkAff->dwType) ) + { + ++it; + continue; + } + + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + } + + ComputeAffect(pkAff, false); + + it = m_list_pkAffect.erase(it); + CAffect::Release(pkAff); + } + + if (afOld != m_afAffectFlag || + wMovSpd != GetPoint(POINT_MOV_SPEED) || + wAttSpd != GetPoint(POINT_ATT_SPEED)) + UpdatePacket(); + + CheckMaximumPoints(); + + if (m_list_pkAffect.empty()) + event_cancel(&m_pkAffectEvent); +} + +int CHARACTER::ProcessAffect() +{ + bool bDiff = false; + CAffect *pkAff = NULL; + + // + // ̾ ó + // + for (int i = 0; i <= PREMIUM_MAX_NUM; ++i) + { + int aff_idx = i + AFFECT_PREMIUM_START; + + pkAff = FindAffect(aff_idx); + + if (!pkAff) + continue; + + int remain = GetPremiumRemainSeconds(i); + + if (remain < 0) + { + RemoveAffect(aff_idx); + bDiff = true; + } + else + pkAff->lDuration = remain + 1; + } + + ////////// HAIR_AFFECT + pkAff = FindAffect(AFFECT_HAIR); + if (pkAff) + { + // IF HAIR_LIMIT_TIME() < CURRENT_TIME() + if ( this->GetQuestFlag("hair.limit_time") < get_global_time()) + { + // SET HAIR NORMAL + this->SetPart(PART_HAIR, 0); + // REMOVE HAIR AFFECT + RemoveAffect(AFFECT_HAIR); + } + else + { + // INCREASE AFFECT DURATION + ++(pkAff->lDuration); + } + } + ////////// HAIR_AFFECT + // + + CHorseNameManager::instance().Validate(this); + + TAffectFlag afOld = m_afAffectFlag; + long lMovSpd = GetPoint(POINT_MOV_SPEED); + long lAttSpd = GetPoint(POINT_ATT_SPEED); + + itertype(m_list_pkAffect) it; + + it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + pkAff = *it; + + bool bEnd = false; + + if (pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END) + { + if (!GetGuild() || !GetGuild()->UnderAnyWar()) + bEnd = true; + } + + if (pkAff->lSPCost > 0) + { + if (GetSP() < pkAff->lSPCost) + bEnd = true; + else + PointChange(POINT_SP, -pkAff->lSPCost); + } + + // AFFECT_DURATION_BUG_FIX + // ȿ ۵ ð δ. + // ð ſ ũ ̶ . + if ( --pkAff->lDuration <= 0 ) + { + bEnd = true; + } + // END_AFFECT_DURATION_BUG_FIX + + if (bEnd) + { + it = m_list_pkAffect.erase(it); + ComputeAffect(pkAff, false); + bDiff = true; + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + + CAffect::Release(pkAff); + + continue; + } + + ++it; + } + + if (bDiff) + { + if (afOld != m_afAffectFlag || + lMovSpd != GetPoint(POINT_MOV_SPEED) || + lAttSpd != GetPoint(POINT_ATT_SPEED)) + { + UpdatePacket(); + } + + CheckMaximumPoints(); + } + + if (m_list_pkAffect.empty()) + return true; + + return false; +} + +void CHARACTER::SaveAffect() +{ + TPacketGDAddAffect p; + + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it++; + + if (IS_NO_SAVE_AFFECT(pkAff->dwType)) + continue; + + sys_log(1, "AFFECT_SAVE: %u %u %d %d", pkAff->dwType, pkAff->bApplyOn, pkAff->lApplyValue, pkAff->lDuration); + + p.dwPID = GetPlayerID(); + p.elem.dwType = pkAff->dwType; + p.elem.bApplyOn = pkAff->bApplyOn; + p.elem.lApplyValue = pkAff->lApplyValue; + p.elem.dwFlag = pkAff->dwFlag; + p.elem.lDuration = pkAff->lDuration; + p.elem.lSPCost = pkAff->lSPCost; + db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p)); + } +} + +EVENTINFO(load_affect_login_event_info) +{ + DWORD pid; + DWORD count; + char* data; + + load_affect_login_event_info() + : pid( 0 ) + , count( 0 ) + , data( 0 ) + { + } +}; + +EVENTFUNC(load_affect_login_event) +{ + load_affect_login_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "load_affect_login_event_info> Null pointer" ); + return 0; + } + + DWORD dwPID = info->pid; + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(dwPID); + + if (!ch) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + + LPDESC d = ch->GetDesc(); + + if (!d) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + + if (d->IsPhase(PHASE_HANDSHAKE) || + d->IsPhase(PHASE_LOGIN) || + d->IsPhase(PHASE_SELECT) || + d->IsPhase(PHASE_DEAD) || + d->IsPhase(PHASE_LOADING)) + { + return PASSES_PER_SEC(1); + } + else if (d->IsPhase(PHASE_CLOSE)) + { + M2_DELETE_ARRAY(info->data); + return 0; + } + else if (d->IsPhase(PHASE_GAME)) + { + sys_log(1, "Affect Load by Event"); + ch->LoadAffect(info->count, (TPacketAffectElement*)info->data); + M2_DELETE_ARRAY(info->data); + return 0; + } + else + { + sys_err("input_db.cpp:quest_login_event INVALID PHASE pid %d", ch->GetPlayerID()); + M2_DELETE_ARRAY(info->data); + return 0; + } +} + +void CHARACTER::LoadAffect(DWORD dwCount, TPacketAffectElement * pElements) +{ + m_bIsLoadedAffect = false; + + if (!GetDesc()->IsPhase(PHASE_GAME)) + { + if (test_server) + sys_log(0, "LOAD_AFFECT: Creating Event", GetName(), dwCount); + + load_affect_login_event_info* info = AllocEventInfo(); + + info->pid = GetPlayerID(); + info->count = dwCount; + info->data = M2_NEW char[sizeof(TPacketAffectElement) * dwCount]; + thecore_memcpy(info->data, pElements, sizeof(TPacketAffectElement) * dwCount); + + event_create(load_affect_login_event, info, PASSES_PER_SEC(1)); + + return; + } + + ClearAffect(true); + + if (test_server) + sys_log(0, "LOAD_AFFECT: %s count %d", GetName(), dwCount); + + TAffectFlag afOld = m_afAffectFlag; + + long lMovSpd = GetPoint(POINT_MOV_SPEED); + long lAttSpd = GetPoint(POINT_ATT_SPEED); + + for (DWORD i = 0; i < dwCount; ++i, ++pElements) + { + // εʴ´. + if (pElements->dwType == SKILL_MUYEONG) + continue; + + if (AFFECT_AUTO_HP_RECOVERY == pElements->dwType || AFFECT_AUTO_SP_RECOVERY == pElements->dwType) + { + LPITEM item = FindItemByID( pElements->dwFlag ); + + if (NULL == item) + continue; + + item->Lock(true); + } + + if (pElements->bApplyOn >= POINT_MAX_NUM) + { + sys_err("invalid affect data %s ApplyOn %u ApplyValue %d", + GetName(), pElements->bApplyOn, pElements->lApplyValue); + continue; + } + + if (test_server) + { + sys_log(0, "Load Affect : Affect %s %d %d", GetName(), pElements->dwType, pElements->bApplyOn ); + } + + CAffect* pkAff = CAffect::Acquire(); + m_list_pkAffect.push_back(pkAff); + + pkAff->dwType = pElements->dwType; + pkAff->bApplyOn = pElements->bApplyOn; + pkAff->lApplyValue = pElements->lApplyValue; + pkAff->dwFlag = pElements->dwFlag; + pkAff->lDuration = pElements->lDuration; + pkAff->lSPCost = pElements->lSPCost; + + SendAffectAddPacket(GetDesc(), pkAff); + + ComputeAffect(pkAff, true); + + + } + + if ( CArenaManager::instance().IsArenaMap(GetMapIndex()) == true ) + { + RemoveGoodAffect(); + } + + if (afOld != m_afAffectFlag || lMovSpd != GetPoint(POINT_MOV_SPEED) || lAttSpd != GetPoint(POINT_ATT_SPEED)) + { + UpdatePacket(); + } + + StartAffectEvent(); + + m_bIsLoadedAffect = true; + + // ȥ ε ʱȭ + DragonSoul_Initialize(); +} + +bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, long lApplyValue, DWORD dwFlag, long lDuration, long lSPCost, bool bOverride, bool IsCube ) +{ + // CHAT_BLOCK + if (dwType == AFFECT_BLOCK_CHAT && lDuration > 1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ä Ǿϴ.")); + } + // END_OF_CHAT_BLOCK + + if (lDuration == 0) + { + sys_err("Character::AddAffect lDuration == 0 type %d", lDuration, dwType); + lDuration = 1; + } + + CAffect * pkAff = NULL; + + if (IsCube) + pkAff = FindAffect(dwType,bApplyOn); + else + pkAff = FindAffect(dwType); + + if (dwFlag == AFF_STUN) + { + if (m_posDest.x != GetX() || m_posDest.y != GetY()) + { + m_posDest.x = m_posStart.x = GetX(); + m_posDest.y = m_posStart.y = GetY(); + battle_end(this); + + SyncPacket(); + } + } + + // ̹ ִ ȿ ó + if (pkAff && bOverride) + { + ComputeAffect(pkAff, false); // ϴ ȿ ϰ + + if (GetDesc()) + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + else + { + // + // 带 ߰ + // + // NOTE: type ε Ʈ ִ. + // + pkAff = CAffect::Acquire(); + m_list_pkAffect.push_back(pkAff); + + } + + sys_log(1, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration); + sys_log(0, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration); + + pkAff->dwType = dwType; + pkAff->bApplyOn = bApplyOn; + pkAff->lApplyValue = lApplyValue; + pkAff->dwFlag = dwFlag; + pkAff->lDuration = lDuration; + pkAff->lSPCost = lSPCost; + + WORD wMovSpd = GetPoint(POINT_MOV_SPEED); + WORD wAttSpd = GetPoint(POINT_ATT_SPEED); + + ComputeAffect(pkAff, true); + + if (pkAff->dwFlag || wMovSpd != GetPoint(POINT_MOV_SPEED) || wAttSpd != GetPoint(POINT_ATT_SPEED)) + UpdatePacket(); + + StartAffectEvent(); + + if (IsPC()) + { + SendAffectAddPacket(GetDesc(), pkAff); + + if (IS_NO_SAVE_AFFECT(pkAff->dwType)) + return true; + + TPacketGDAddAffect p; + p.dwPID = GetPlayerID(); + p.elem.dwType = pkAff->dwType; + p.elem.bApplyOn = pkAff->bApplyOn; + p.elem.lApplyValue = pkAff->lApplyValue; + p.elem.dwFlag = pkAff->dwFlag; + p.elem.lDuration = pkAff->lDuration; + p.elem.lSPCost = pkAff->lSPCost; + db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p)); + } + + return true; +} + +void CHARACTER::RefreshAffect() +{ + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + { + CAffect * pkAff = *it++; + ComputeAffect(pkAff, true); + } +} + +void CHARACTER::ComputeAffect(CAffect * pkAff, bool bAdd) +{ + if (bAdd && pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END) + { + if (!GetGuild()) + return; + + if (!GetGuild()->UnderAnyWar()) + return; + } + + if (pkAff->dwFlag) + { + if (!bAdd) + m_afAffectFlag.Reset(pkAff->dwFlag); + else + m_afAffectFlag.Set(pkAff->dwFlag); + } + + if (bAdd) + PointChange(pkAff->bApplyOn, pkAff->lApplyValue); + else + PointChange(pkAff->bApplyOn, -pkAff->lApplyValue); + + if (pkAff->dwType == SKILL_MUYEONG) + { + if (bAdd) + StartMuyeongEvent(); + else + StopMuyeongEvent(); + } +} + +bool CHARACTER::RemoveAffect(CAffect * pkAff) +{ + if (!pkAff) + return false; + + // AFFECT_BUF_FIX + m_list_pkAffect.remove(pkAff); + // END_OF_AFFECT_BUF_FIX + + ComputeAffect(pkAff, false); + + // . + // ״ ų ->а-> (AFFECT_REVIVE_INVISIBLE) ٷ 쿡 ߻Ѵ. + // а ϴ , ų ȿ ϰ а ȿ ǰ Ǿִµ, + // ٷ ϸ RemoveAffect Ҹ ǰ, ComputePointsϸ鼭 а ȿ + ų ȿ ȴ. + // ComputePoints а ¸ ų ȿ ϸ DZ ϴµ, + // ComputePoints ϰ ǰ ־ ū ȭ ִ .( side effect ߻ ˱ .) + // AFFECT_REVIVE_INVISIBLE RemoveAffect Ǵ 츸 Ѵ. + // ð Ǿ ȿ Ǯ װ ߻ Ƿ ׿ Ȱ . + // (ProcessAffect ð Ǿ Affect Ǵ , ComputePoints θ ʴ´.) + if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType) + { + ComputePoints(); + } + CheckMaximumPoints(); + + if (test_server) + sys_log(0, "AFFECT_REMOVE: %s (flag %u apply: %u)", GetName(), pkAff->dwFlag, pkAff->bApplyOn); + + if (IsPC()) + { + SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn); + } + + CAffect::Release(pkAff); + return true; +} + +bool CHARACTER::RemoveAffect(DWORD dwType) +{ + // CHAT_BLOCK + if (dwType == AFFECT_BLOCK_CHAT) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ä ǮȽϴ.")); + } + // END_OF_CHAT_BLOCK + + bool flag = false; + + CAffect * pkAff; + + while ((pkAff = FindAffect(dwType))) + { + RemoveAffect(pkAff); + flag = true; + } + + return flag; +} + +bool CHARACTER::IsAffectFlag(DWORD dwAff) const +{ + return m_afAffectFlag.IsSet(dwAff); +} + +void CHARACTER::RemoveGoodAffect() +{ + RemoveAffect(AFFECT_MOV_SPEED); + RemoveAffect(AFFECT_ATT_SPEED); + RemoveAffect(AFFECT_STR); + RemoveAffect(AFFECT_DEX); + RemoveAffect(AFFECT_INT); + RemoveAffect(AFFECT_CON); + RemoveAffect(AFFECT_CHINA_FIREWORK); + + RemoveAffect(SKILL_JEONGWI); + RemoveAffect(SKILL_GEOMKYUNG); + RemoveAffect(SKILL_CHUNKEON); + RemoveAffect(SKILL_EUNHYUNG); + RemoveAffect(SKILL_GYEONGGONG); + RemoveAffect(SKILL_GWIGEOM); + RemoveAffect(SKILL_TERROR); + RemoveAffect(SKILL_JUMAGAP); + RemoveAffect(SKILL_MANASHILED); + RemoveAffect(SKILL_HOSIN); + RemoveAffect(SKILL_REFLECT); + RemoveAffect(SKILL_KWAESOK); + RemoveAffect(SKILL_JEUNGRYEOK); + RemoveAffect(SKILL_GICHEON); +} + +bool CHARACTER::IsGoodAffect(BYTE bAffectType) const +{ + switch (bAffectType) + { + case (AFFECT_MOV_SPEED): + case (AFFECT_ATT_SPEED): + case (AFFECT_STR): + case (AFFECT_DEX): + case (AFFECT_INT): + case (AFFECT_CON): + case (AFFECT_CHINA_FIREWORK): + + case (SKILL_JEONGWI): + case (SKILL_GEOMKYUNG): + case (SKILL_CHUNKEON): + case (SKILL_EUNHYUNG): + case (SKILL_GYEONGGONG): + case (SKILL_GWIGEOM): + case (SKILL_TERROR): + case (SKILL_JUMAGAP): + case (SKILL_MANASHILED): + case (SKILL_HOSIN): + case (SKILL_REFLECT): + case (SKILL_KWAESOK): + case (SKILL_JEUNGRYEOK): + case (SKILL_GICHEON): + return true; + } + return false; +} + +void CHARACTER::RemoveBadAffect() +{ + sys_log(0, "RemoveBadAffect %s", GetName()); + // + RemovePoison(); + RemoveFire(); + + // : Value% 5ʰ Ӹ ư. ( 1/2 Ȯ Ǯ) AFF_STUN + RemoveAffect(AFFECT_STUN); + + // ο : Value% /̼ . õ ޶ 쿡 AFF_SLOW + RemoveAffect(AFFECT_SLOW); + + // Ӹ + RemoveAffect(SKILL_TUSOK); + + // + //RemoveAffect(SKILL_CURSE); + + // Ĺ + //RemoveAffect(SKILL_PABUP); + + // : Value% Ų. 2 AFF_FAINT + //RemoveAffect(AFFECT_FAINT); + + // ٸ : Value% ̵ӵ Ʈ. 5ʰ -40 AFF_WEB + //RemoveAffect(AFFECT_WEB); + + // : Value% 10ʰ . ( Ǯ) AFF_SLEEP + //RemoveAffect(AFFECT_SLEEP); + + // : Value% / Ʈ. õ ޶ 쿡 AFF_CURSE + //RemoveAffect(AFFECT_CURSE); + + // : Value% 4ʰ Ų. AFF_PARA + //RemoveAffect(AFFECT_PARALYZE); + + // εں : + //RemoveAffect(SKILL_BUDONG); +} + diff --git a/game/src/char_battle.cpp b/game/src/char_battle.cpp new file mode 100644 index 0000000..0076bb9 --- /dev/null +++ b/game/src/char_battle.cpp @@ -0,0 +1,3648 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc.h" +#include "desc_manager.h" +#include "char_manager.h" +#include "item.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "battle.h" +#include "pvp.h" +#include "skill.h" +#include "start_position.h" +#include "profiler.h" +#include "cmd.h" +#include "dungeon.h" +#include "log.h" +#include "unique_item.h" +#include "priv_manager.h" +#include "db.h" +#include "vector.h" +#include "marriage.h" +#include "arena.h" +#include "regen.h" +#include "monarch.h" +#include "exchange.h" +#include "shop_manager.h" +#include "castle.h" +#include "dev_log.h" +#include "ani.h" +#include "BattleArena.h" +#include "packet.h" +#include "party.h" +#include "affect.h" +#include "guild.h" +#include "guild_manager.h" +#include "questmanager.h" +#include "questlua.h" +#include "threeway_war.h" +#include "BlueDragon.h" +#include "DragonLair.h" + +DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp) +{ + if (PLAYER_EXP_TABLE_MAX < ch->GetLevel()) + { + double ret = 0.95; + double factor = 0.1; + + for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i) + { + if ( (i%10) == 0) + factor /= 2.0; + + ret *= 1.0 - factor; + } + + ret = ret * static_cast(exp); + + if (ret < 1.0) + return 1; + + return static_cast(ret); + } + + return exp; +} + +bool CHARACTER::CanBeginFight() const +{ + if (!CanMove()) + return false; + + return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun(); +} + +void CHARACTER::BeginFight(LPCHARACTER pkVictim) +{ + SetVictim(pkVictim); + SetPosition(POS_FIGHTING); + SetNextStatePulse(1); +} + +bool CHARACTER::CanFight() const +{ + return m_pointsInstant.position >= POS_FIGHTING ? true : false; +} + +void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim) +{ + TPacketGCCreateFly packFly; + + packFly.bHeader = HEADER_GC_CREATE_FLY; + packFly.bType = bType; + packFly.dwStartVID = GetVID(); + packFly.dwEndVID = pkVictim->GetVID(); + + PacketAround(&packFly, sizeof(TPacketGCCreateFly)); +} + +void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod) +{ + if (pkKiller->GetSP() >= pkKiller->GetMaxSP()) + return; + + bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000; + bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000; + + if (iMethod == 1) + { + int num = number(0, 3); + + if (!num) + { + int iLvDelta = GetLevel() - pkKiller->GetLevel(); + int iAmount = 0; + + if (iLvDelta >= 5) + iAmount = 10; + else if (iLvDelta >= 0) + iAmount = 6; + else if (iLvDelta >= -3) + iAmount = 2; + + if (iAmount != 0) + { + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + + if (iAmount >= 11) + CreateFly(FLY_SP_BIG, pkKiller); + else if (iAmount >= 7) + CreateFly(FLY_SP_MEDIUM, pkKiller); + else + CreateFly(FLY_SP_SMALL, pkKiller); + + pkKiller->PointChange(POINT_SP, iAmount); + } + } + } + else + { + if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2)) + { + int iAmount; + + if (bAttacking) + iAmount = 2 + GetMaxSP() / 100; + else if (bMoving) + iAmount = 3 + GetMaxSP() * 2 / 100; + else + iAmount = 10 + GetMaxSP() * 3 / 100; // + + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + pkKiller->PointChange(POINT_SP, iAmount); + } + else + { + int iAmount; + + if (bAttacking) + iAmount = 2 + pkKiller->GetMaxSP() / 200; + else if (bMoving) + iAmount = 2 + pkKiller->GetMaxSP() / 100; + else + { + // + if (pkKiller->GetHP() < pkKiller->GetMaxHP()) + iAmount = 2 + (pkKiller->GetMaxSP() / 100); // á + else + iAmount = 9 + (pkKiller->GetMaxSP() / 100); // ⺻ + } + + iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; + pkKiller->PointChange(POINT_SP, iAmount); + } + } +} + + +bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType) +{ + if (test_server) + sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange()); + //PROF_UNIT puAttack("Attack"); + if (!CanMove()) + return false; + + // CASTLE + if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim)) + return false; + // CASTLE + + DWORD dwCurrentTime = get_dword_time(); + + if (IsPC()) + { + if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime)) + return false; + + if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime()) + return false; + } + else + { + MonsterChat(MONSTER_CHAT_ATTACK); + } + + pkVictim->SetSyncOwner(this); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(this); + + int iRet; + + if (bType == 0) + { + // + // Ϲ + // + switch (GetMobBattleType()) + { + case BATTLE_TYPE_MELEE: + case BATTLE_TYPE_POWER: + case BATTLE_TYPE_TANKER: + case BATTLE_TYPE_SUPER_POWER: + case BATTLE_TYPE_SUPER_TANKER: + iRet = battle_melee_attack(this, pkVictim); + break; + + case BATTLE_TYPE_RANGE: + FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); + iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE; + break; + + case BATTLE_TYPE_MAGIC: + FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); + iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE; + break; + + default: + sys_err("Unhandled battle type %d", GetMobBattleType()); + iRet = BATTLE_NONE; + break; + } + } + else + { + if (IsPC() == true) + { + if (dwCurrentTime - m_dwLastSkillTime > 1500) + { + sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)", + GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime)); + return false; + } + } + + sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():""); + iRet = ComputeSkill(bType, pkVictim); + } + + //if (test_server && IsPC()) + // sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet); + if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD) + { + OnMove(true); + pkVictim->OnMove(); + + // only pc sets victim null. For npc, state machine will reset this. + if (BATTLE_DEAD == iRet && IsPC()) + SetVictim(NULL); + + return true; + } + + return false; +} + +void CHARACTER::DeathPenalty(BYTE bTown) +{ + sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown); + + Cube_close(this); + + if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) + { + return; + } + + if (GetLevel() < 10) + { + sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + return; + } + + if (number(0, 2)) + { + sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + return; + } + + if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY)) + { + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + + // NO_DEATH_PENALTY_BUG_FIX + if (LC_IsYMIR()) // õ ȣ üũѴ. + { + if (FindAffect(AFFECT_NO_DEATH_PENALTY)) + { + sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + RemoveAffect(AFFECT_NO_DEATH_PENALTY); + return; + } + } + else if (!bTown) // ڸ Ȱø ȣ Ѵ. ( ͽô ġ гƼ ) + { + if (FindAffect(AFFECT_NO_DEATH_PENALTY)) + { + sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ġ ʾҽϴ.")); + RemoveAffect(AFFECT_NO_DEATH_PENALTY); + return; + } + } + // END_OF_NO_DEATH_PENALTY_BUG_FIX + + int iLoss = ((GetNextExp() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100); + + if (true == LC_IsYMIR()) + { + if (PLAYER_EXP_TABLE_MAX < GetLevel()) + { + iLoss = MIN(500000, iLoss); + } + else + { + iLoss = MIN(200000, iLoss); + } + } + else if (true == LC_IsEurope()) + { + iLoss = MIN(800000, iLoss); + } + + if (bTown) + { + if (g_iUseLocale) + { + iLoss = 0; + } + else + { + iLoss -= iLoss / 3; + } + } + + if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS)) + iLoss /= 2; + + sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, GetLevel())]); + + PointChange(POINT_EXP, -iLoss, true); + } +} + +bool CHARACTER::IsStun() const +{ + if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN)) + return true; + + return false; +} + +EVENTFUNC(StunEvent) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "StunEvent> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + ch->m_pkStunEvent = NULL; + ch->Dead(); + return 0; +} + +void CHARACTER::Stun() +{ + if (IsStun()) + return; + + if (IsDead()) + return; + + if (!IsPC() && m_pkParty) + { + m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0); + } + + sys_log(1, "%s: Stun %p", GetName(), this); + + PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY)); + PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY)); + + CloseMyShop(); + + event_cancel(&m_pkRecoveryEvent); // ȸ ̺Ʈ δ. + + TPacketGCStun pack; + pack.header = HEADER_GC_STUN; + pack.vid = m_vid; + PacketAround(&pack, sizeof(pack)); + + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); + + if (m_pkStunEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + + m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3)); +} + +EVENTINFO(SCharDeadEventInfo) +{ + bool isPC; + uint32_t dwID; + + SCharDeadEventInfo() + : isPC(0) + , dwID(0) + { + } +}; + +EVENTFUNC(dead_event) +{ + const SCharDeadEventInfo* info = dynamic_cast(event->info); + + if ( info == NULL ) + { + sys_err( "dead_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = NULL; + + if (true == info->isPC) + { + ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID ); + } + else + { + ch = CHARACTER_MANAGER::instance().Find( info->dwID ); + } + + if (NULL == ch) + { + sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID ); + return 0; + } + + ch->m_pkDeadEvent = NULL; + + if (ch->GetDesc()) + { + ch->GetDesc()->SetPhase(PHASE_GAME); + + ch->SetPosition(POS_STANDING); + + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + ch->WarpSet(pos.x, pos.y); + else + { + sys_err("cannot find spawn position (name %s)", ch->GetName()); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + + ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true); + + ch->DeathPenalty(0); + + ch->StartRecoveryEvent(); + + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + } + else + { + if (ch->IsMonster() == true) + { + if (ch->IsRevive() == false && ch->HasReviverInParty() == true) + { + ch->SetPosition(POS_STANDING); + ch->SetHP(ch->GetMaxHP()); + + ch->ViewReencode(); + + ch->SetAggressive(); + ch->SetRevive(true); + + return 0; + } + } + + M2_DESTROY_CHARACTER(ch); + } + + return 0; +} + +bool CHARACTER::IsDead() const +{ + if (m_pointsInstant.position == POS_DEAD) + return true; + + return false; +} + +#define GetGoldMultipler() (distribution_test_server ? 3 : 1) + +void CHARACTER::RewardGold(LPCHARACTER pkAttacker) +{ + // ADD_PREMIUM + bool isAutoLoot = + (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 || + pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT)) + ? true : false; // 3 + // END_OF_ADD_PREMIUM + + PIXEL_POSITION pos; + + if (!isAutoLoot) + if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) + return; + + int iTotalGold = 0; + // + // --------- Ȯ ---------- + // + int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent; + + if (pkAttacker->IsPC()) + iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100; + + if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS)) + iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100); + + iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100; + + // ADD_PREMIUM + if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 || + pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD)) + iGoldPercent += iGoldPercent; + // END_OF_ADD_PREMIUM + + if (iGoldPercent > 100) + iGoldPercent = 100; + + int iPercent; + + if (GetMobRank() >= MOB_RANK_BOSS) + iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100); + else + iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100); + //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent); + + if (number(1, 100) > iPercent) + return; + + int iGoldMultipler = GetGoldMultipler(); + + if (1 == number(1, 50000)) // 1/50000 Ȯ 10 + iGoldMultipler *= 10; + else if (1 == number(1, 10000)) // 1/10000 Ȯ 5 + iGoldMultipler *= 5; + + // + if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) + if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) + iGoldMultipler *= 2; + + // + // --------- ---------- + // + if (test_server) + pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker)); + + // + // --------- ó ------------- + // + LPITEM item; + + int iGold10DropPct = 100; + iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP)); + + // MOB_RANK BOSS ź + if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0) + { + if (1 == number(1, iGold10DropPct)) + iGoldMultipler *= 10; // 1% Ȯ 10 + + int iSplitCount = number(25, 35); + + for (int i = 0; i < iSplitCount; ++i) + { + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount; + if (test_server) + sys_log(0, "iGold %d", iGold); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + if (iGold == 0) + { + continue ; + } + + if (test_server) + { + sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler); + sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax); + } + + // NOTE: ź 3 ó + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) + { + pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23); + pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + iTotalGold += iGold; // Total gold + } + } + } + // 1% Ȯ 10 ߸. (10 ) + else if (1 == number(1, iGold10DropPct)) + { + // + // ź + // + for (int i = 0; i < 10; ++i) + { + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + if (iGold == 0) + { + continue; + } + + // NOTE: ź 3 ó + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) + { + pos.x = GetX() + (number(-7, 7) * 20); + pos.y = GetY() + (number(-7, 7) * 20); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + iTotalGold += iGold; // Total gold + } + } + } + else + { + // + // Ϲ + // + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; + iGold *= iGoldMultipler; + + int iSplitCount; + + if (iGold >= 3 && !LC_IsYMIR()) + iSplitCount = number(1, 3); + else if (GetMobRank() >= MOB_RANK_BOSS) + { + iSplitCount = number(3, 10); + + if ((iGold / iSplitCount) == 0) + iSplitCount = 1; + } + else + iSplitCount = 1; + + if (iGold != 0) + { + iTotalGold += iGold; // Total gold + + for (int i = 0; i < iSplitCount; ++i) + { + if (isAutoLoot) + { + pkAttacker->GiveGold(iGold / iSplitCount); + } + else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) + { + pos.x = GetX() + (number(-7, 7) * 20); + pos.y = GetY() + (number(-7, 7) * 20); + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + } + } + + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold); +} + +void CHARACTER::Reward(bool bItemDrop) +{ + if (GetRaceNum() == 5001) // ֱ + { + PIXEL_POSITION pos; + + if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) + return; + + LPITEM item; + int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); + iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100; + iGold *= GetGoldMultipler(); + int iSplitCount = number(25, 35); + + sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount); + + for (int i = 1; i <= iSplitCount; ++i) + { + if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) + { + if (i != 0) + { + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + + pos.x += GetX(); + pos.y += GetY(); + } + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + return; + } + + //PROF_UNIT puReward("Reward"); + LPCHARACTER pkAttacker = DistributeExp(); + + if (!pkAttacker) + return; + + //PROF_UNIT pu1("r1"); + if (pkAttacker->IsPC()) + { + if (GetLevel() - pkAttacker->GetLevel() >= -10) + if (pkAttacker->GetRealAlignment() < 0) + { + if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL)) + pkAttacker->UpdateAlignment(14); + else + pkAttacker->UpdateAlignment(7); + } + else + pkAttacker->UpdateAlignment(2); + + pkAttacker->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum()); + CHARACTER_MANAGER::instance().KillLog(GetRaceNum()); + + if (!number(0, 9)) + { + if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY)) + { + int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100; + pkAttacker->PointChange(POINT_HP, iHP); + CreateFly(FLY_HP_SMALL, pkAttacker); + } + + if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER)) + { + int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100; + pkAttacker->PointChange(POINT_SP, iSP); + CreateFly(FLY_SP_SMALL, pkAttacker); + } + } + } + //pu1.Pop(); + + if (!bItemDrop) + return; + + PIXEL_POSITION pos = GetXYZ(); + + if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos)) + return; + + // + // + // + //PROF_UNIT pu2("r2"); + if (test_server) + sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName()); + RewardGold(pkAttacker); + //pu2.Pop(); + + // + // + // + //PROF_UNIT pu3("r3"); + LPITEM item; + + static std::vector s_vec_item; + s_vec_item.clear(); + + if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item)) + { + if (s_vec_item.size() == 0); + else if (s_vec_item.size() == 1) + { + item = s_vec_item[0]; + item->AddToGround(GetMapIndex(), pos); + + if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false) + { + item->SetOwnership(pkAttacker); + } + + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); + } + else + { + int iItemIdx = s_vec_item.size() - 1; + + std::priority_queue > pq; + + int total_dam = 0; + + for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) + { + int iDamage = it->second.iTotalDamage; + if (iDamage > 0) + { + LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); + + if (ch) + { + pq.push(std::make_pair(iDamage, ch)); + total_dam += iDamage; + } + } + } + + std::vector v; + + while (!pq.empty() && pq.top().first * 10 >= total_dam) + { + v.push_back(pq.top().second); + pq.pop(); + } + + if (v.empty()) + { + // Ư + while (iItemIdx >= 0) + { + item = s_vec_item[iItemIdx--]; + + if (!item) + { + sys_err("item null in vector idx %d", iItemIdx + 1); + continue; + } + + item->AddToGround(GetMapIndex(), pos); + // 10% Ǿ + //item->SetOwnership(pkAttacker); + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); + } + } + else + { + // + std::vector::iterator it = v.begin(); + + while (iItemIdx >= 0) + { + item = s_vec_item[iItemIdx--]; + + if (!item) + { + sys_err("item null in vector idx %d", iItemIdx + 1); + continue; + } + + item->AddToGround(GetMapIndex(), pos); + + LPCHARACTER ch = *it; + + if (ch->GetParty()) + ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY()); + + ++it; + + if (it == v.end()) + it = v.begin(); + + if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false) + { + item->SetOwnership(ch); + } + + item->StartDestroyEvent(); + + pos.x = number(-7, 7) * 20; + pos.y = number(-7, 7) * 20; + pos.x += GetX(); + pos.y += GetY(); + + sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); + } + } + } + } + + m_map_kDamage.clear(); +} + +struct TItemDropPenalty +{ + int iInventoryPct; // Range: 1 ~ 1000 + int iInventoryQty; // Range: -- + int iEquipmentPct; // Range: 1 ~ 100 + int iEquipmentQty; // Range: -- +}; + +TItemDropPenalty aItemDropPenalty_kor[9] = +{ + { 0, 0, 0, 0 }, // + { 0, 0, 0, 0 }, // + { 0, 0, 0, 0 }, // + { 0, 0, 0, 0 }, // + { 0, 0, 0, 0 }, // + { 25, 1, 5, 1 }, // + { 50, 2, 10, 1 }, // + { 75, 4, 15, 1 }, // + { 100, 8, 20, 1 }, // п +}; + +void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller) +{ + // λ ¿ ʴ´. + if (GetMyShop()) + return; + + if (false == LC_IsYMIR()) + { + if (GetLevel() < 50) + return; + } + + if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) + { + return; + } + + struct TItemDropPenalty * table = &aItemDropPenalty_kor[0]; + + if (GetLevel() < 10) + return; + + int iAlignIndex; + + if (GetRealAlignment() >= 120000) + iAlignIndex = 0; + else if (GetRealAlignment() >= 80000) + iAlignIndex = 1; + else if (GetRealAlignment() >= 40000) + iAlignIndex = 2; + else if (GetRealAlignment() >= 10000) + iAlignIndex = 3; + else if (GetRealAlignment() >= 0) + iAlignIndex = 4; + else if (GetRealAlignment() > -40000) + iAlignIndex = 5; + else if (GetRealAlignment() > -80000) + iAlignIndex = 6; + else if (GetRealAlignment() > -120000) + iAlignIndex = 7; + else + iAlignIndex = 8; + + std::vector > vec_item; + LPITEM pkItem; + int i; + bool isDropAllEquipments = false; + + TItemDropPenalty & r = table[iAlignIndex]; + sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct); + + bool bDropInventory = r.iInventoryPct >= number(1, 1000); + bool bDropEquipment = r.iEquipmentPct >= number(1, 100); + bool bDropAntiDropUniqueItem = false; + + if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)) + { + bDropInventory = false; + bDropEquipment = false; + bDropAntiDropUniqueItem = true; + } + + if (bDropInventory) // Drop Inventory + { + std::vector vec_bSlots; + + for (i = 0; i < INVENTORY_MAX_NUM; ++i) + if (GetInventoryItem(i)) + vec_bSlots.push_back(i); + + if (!vec_bSlots.empty()) + { + random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); + + int iQty = MIN(vec_bSlots.size(), r.iInventoryQty); + + if (iQty) + iQty = number(1, iQty); + + for (i = 0; i < iQty; ++i) + { + pkItem = GetInventoryItem(vec_bSlots[i]); + + if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) + continue; + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); + vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY)); + } + } + else if (iAlignIndex == 8) + isDropAllEquipments = true; + } + + if (bDropEquipment) // Drop Equipment + { + std::vector vec_bSlots; + + for (i = 0; i < WEAR_MAX_NUM; ++i) + if (GetWear(i)) + vec_bSlots.push_back(i); + + if (!vec_bSlots.empty()) + { + random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); + int iQty; + + if (isDropAllEquipments) + iQty = vec_bSlots.size(); + else + iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty)); + + if (iQty) + iQty = number(1, iQty); + + for (i = 0; i < iQty; ++i) + { + pkItem = GetWear(vec_bSlots[i]); + + if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) + continue; + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); + vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); + } + } + } + + if (bDropAntiDropUniqueItem) + { + LPITEM pkItem; + + pkItem = GetWear(WEAR_UNIQUE1); + + if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255); + vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); + } + + pkItem = GetWear(WEAR_UNIQUE2); + + if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255); + vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); + } + } + + { + PIXEL_POSITION pos; + pos.x = GetX(); + pos.y = GetY(); + + unsigned int i; + + for (i = 0; i < vec_item.size(); ++i) + { + LPITEM item = vec_item[i].first; + int window = vec_item[i].second; + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + + sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); + LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : "")); + + pos.x = GetX() + number(-7, 7) * 20; + pos.y = GetY() + number(-7, 7) * 20; + } + } +} + +class FPartyAlignmentCompute +{ + public: + FPartyAlignmentCompute(int iAmount, int x, int y) + { + m_iAmount = iAmount; + m_iCount = 0; + m_iStep = 0; + m_iKillerX = x; + m_iKillerY = y; + } + + void operator () (LPCHARACTER pkChr) + { + if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE) + { + if (m_iStep == 0) + { + ++m_iCount; + } + else + { + pkChr->UpdateAlignment(m_iAmount / m_iCount); + } + } + } + + int m_iAmount; + int m_iCount; + int m_iStep; + + int m_iKillerX; + int m_iKillerY; +}; + + + +void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) +{ + if (IsDead()) + return; + + { + if (IsHorseRiding()) + { + StopRiding(); + } + else if (GetMountVnum()) + { + RemoveAffect(AFFECT_MOUNT_BONUS); + m_dwMountVnum = 0; + UnEquipSpecialRideUniqueItem(); + + UpdatePacket(); + } + } + + if (!pkKiller && m_dwKillerPID) + pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID); + + m_dwKillerPID = 0; // ݵ ʱȭ ؾ DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE + + bool isAgreedPVP = false; + bool isUnderGuildWar = false; + bool isDuel = false; + bool isForked = false; + + if (pkKiller && pkKiller->IsPC()) + { + if (pkKiller->m_pkChrTarget == this) + pkKiller->SetTarget(NULL); + + if (!IsPC() && pkKiller->GetDungeon()) + pkKiller->GetDungeon()->IncKillCount(pkKiller, this); + + isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID()); + isDuel = CArenaManager::instance().OnDead(pkKiller, this); + + if (IsPC()) + { + CGuild * g1 = GetGuild(); + CGuild * g2 = pkKiller->GetGuild(); + + if (g1 && g2) + if (g1->UnderWar(g2->GetID())) + isUnderGuildWar = true; + + pkKiller->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC); + CGuildManager::instance().Kill(pkKiller, this); + } + } + + //CHECK_FORKEDROAD_WAR + if (IsPC()) + { + if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex())) + isForked = true; + } + //END_CHECK_FORKEDROAD_WAR + + if (pkKiller && + !isAgreedPVP && + !isUnderGuildWar && + IsPC() && + !isDuel && + !isForked && + !IS_CASTLE_MAP(GetMapIndex())) + { + if (GetGMLevel() == GM_PLAYER || test_server) + { + ItemDropPenalty(pkKiller); + } + } + + // CASTLE_SIEGE + if (IS_CASTLE_MAP(GetMapIndex())) + { + if (CASTLE_FROG_VNUM == GetRaceNum()) + castle_frog_die(this, pkKiller); + else if (castle_is_guard_vnum(GetRaceNum())) + castle_guard_die(this, pkKiller); + else if (castle_is_tower_vnum(GetRaceNum())) + castle_tower_die(this, pkKiller); + } + // CASTLE_SIEGE + + if (true == isForked) + { + CThreeWayWar::instance().onDead( this, pkKiller ); + } + + SetPosition(POS_DEAD); + ClearAffect(true); + + if (pkKiller && IsPC()) + { + if (!pkKiller->IsPC()) + { + if (!isForked) + { + sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this); + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName()); + } + } + else + { + sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller)); + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + + if (GetEmpire() != pkKiller->GetEmpire()) + { + int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT)); + + PointChange(POINT_EMPIRE_POINT, -(iEP / 10)); + pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5); + + if (GetPoint(POINT_EMPIRE_POINT) < 10) + { + // TODO : Ա ڵ带 ־ Ѵ. + } + + char buf[256]; + snprintf(buf, sizeof(buf), + "%d %d %d %s %d %d %d %s", + GetEmpire(), GetAlignment(), GetPKMode(), GetName(), + pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); + + LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); + } + else + { + if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked) + { + int iNoPenaltyProb = 0; + + if (g_iUseLocale) + { + if (pkKiller->GetAlignment() >= 0) // 1/3 percent down + iNoPenaltyProb = 33; + else // 4/5 percent down + iNoPenaltyProb = 20; + } + + if (number(1, 100) < iNoPenaltyProb) + pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȣ ʾҽϴ.")); + else + { + if (g_iUseLocale && pkKiller->GetParty()) + { + FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY()); + pkKiller->GetParty()->ForEachOnlineMember(f); + + if (f.m_iCount == 0) + pkKiller->UpdateAlignment(-20000); + else + { + sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount); + + f.m_iStep = 1; + pkKiller->GetParty()->ForEachOnlineMember(f); + } + } + else + pkKiller->UpdateAlignment(-20000); + } + } + + char buf[256]; + snprintf(buf, sizeof(buf), + "%d %d %d %s %d %d %d %s", + GetEmpire(), GetAlignment(), GetPKMode(), GetName(), + pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); + + LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); + } + } + } + else + { + sys_log(1, "DEAD: %s %p", GetName(), this); + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); + } + + ClearSync(); + + //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID()); + event_cancel(&m_pkStunEvent); // ̺Ʈ δ. + + if (IsPC()) + { + m_dwLastDeadTime = get_dword_time(); + SetKillerMode(false); + GetDesc()->SetPhase(PHASE_DEAD); + } + else + { + // 忡 ݹ ʹ Ѵ. + if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD)) + { + if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD))) + { + // Ȱϴ ʹ ʴ´. + if (GetMobTable().dwResurrectionVnum) + { + // DUNGEON_MONSTER_REBIRTH_BUG_FIX + LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation()); + if (GetDungeon() && chResurrect) + { + chResurrect->SetDungeon(GetDungeon()); + } + // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX + + Reward(false); + } + else if (IsRevive() == true) + { + Reward(false); + } + else + { + Reward(true); // Drops gold, item, etc.. + } + } + else + { + if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time()) + { + pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000; + pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ߿ ɿ ϴ.")); + } + } + } + } + + // BOSS_KILL_LOG + if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC()) + { + char buf[51]; + snprintf(buf, sizeof(buf), "%d %ld", g_bChannel, pkKiller->GetMapIndex()); + if (IsStone()) + LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf); + else + LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf); + } + // END_OF_BOSS_KILL_LOG + + TPacketGCDead pack; + pack.header = HEADER_GC_DEAD; + pack.vid = m_vid; + PacketAround(&pack, sizeof(pack)); + + REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); + + // ÷̾ ij̸ + if (GetDesc() != NULL) { + // + // Ŭ̾Ʈ Ʈ Ŷ ٽ . + // + itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); + + while (it != m_list_pkAffect.end()) + SendAffectAddPacket(GetDesc(), *it++); + } + + // + // Dead ̺Ʈ , + // + // Dead ̺Ʈ Ŀ Destroy ǵ ָ, + // PC 3 ִٰ ش. 3 κ + // , ⼭ ޴´. + if (isDuel == false) + { + if (m_pkDeadEvent) + { + sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); + event_cancel(&m_pkDeadEvent); + } + + if (IsStone()) + ClearStone(); + + if (GetDungeon()) + { + GetDungeon()->DeadCharacter(this); + } + + SCharDeadEventInfo* pEventInfo = AllocEventInfo(); + + if (IsPC()) + { + pEventInfo->isPC = true; + pEventInfo->dwID = this->GetPlayerID(); + + m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180)); + } + else + { + pEventInfo->isPC = false; + pEventInfo->dwID = this->GetVID(); + + if (IsRevive() == false && HasReviverInParty() == true) + { + m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3)); + } + else + { + m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10)); + } + } + + sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); + } + + if (m_pkExchange != NULL) + { + m_pkExchange->Cancel(); + } + + if (IsCubeOpen() == true) + { + Cube_close(this); + } + + CShopManager::instance().StopShopping(this); + CloseMyShop(); + CloseSafebox(); + + if (true == IsMonster() && 2493 == GetMobTable().dwVnum) + { + if (NULL != pkKiller && NULL != pkKiller->GetGuild()) + { + CDragonLairManager::instance().OnDragonDead( this, pkKiller->GetGuild()->GetID() ); + } + else + { + sys_err("DragonLair: Dragon killed by nobody"); + } + } +} + +struct FuncSetLastAttacked +{ + FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime) + { + } + + void operator () (LPCHARACTER ch) + { + ch->SetLastAttacked(m_dwTime); + } + + DWORD m_dwTime; +}; + +void CHARACTER::SetLastAttacked(DWORD dwTime) +{ + assert(m_pkMobInst != NULL); + + m_pkMobInst->m_dwLastAttackedTime = dwTime; + m_pkMobInst->m_posLastAttacked = GetXYZ(); +} + +void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag) +{ + if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this)) + { + TPacketGCDamageInfo damageInfo; + memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo)); + + damageInfo.header = HEADER_GC_DAMAGE_INFO; + damageInfo.dwVID = (DWORD)GetVID(); + damageInfo.flag = DamageFlag; + damageInfo.damage = Damage; + + if (GetDesc() != NULL) + { + GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); + } + + if (pAttacker->GetDesc() != NULL) + { + pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); + } + /* + if (GetArenaObserverMode() == false && GetArena() != NULL) + { + GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo)); + } + */ + } +} + +// +// CHARACTER::Damage ޼ҵ this ԰ Ѵ. +// +// Arguments +// pAttacker : +// dam : +// EDamageType :  ΰ? +// +// Return value +// true : dead +// false : not dead yet +// +bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead +{ + if (DAMAGE_TYPE_MAGIC == type) + { + dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f); + } + if (GetRaceNum() == 5001) + { + bool bDropMoney = false; + int iPercent = (GetHP() * 100) / GetMaxHP(); + + if (iPercent <= 10 && GetMaxSP() < 5) + { + SetMaxSP(5); + bDropMoney = true; + } + else if (iPercent <= 20 && GetMaxSP() < 4) + { + SetMaxSP(4); + bDropMoney = true; + } + else if (iPercent <= 40 && GetMaxSP() < 3) + { + SetMaxSP(3); + bDropMoney = true; + } + else if (iPercent <= 60 && GetMaxSP() < 2) + { + SetMaxSP(2); + bDropMoney = true; + } + else if (iPercent <= 80 && GetMaxSP() < 1) + { + SetMaxSP(1); + bDropMoney = true; + } + + if (bDropMoney) + { + DWORD dwGold = 1000; + int iSplitCount = number(10, 13); + + sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP()); + + for (int i = 1; i <= iSplitCount; ++i) + { + PIXEL_POSITION pos; + LPITEM item; + + if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount))) + { + if (i != 0) + { + pos.x = (number(-14, 14) + number(-14, 14)) * 20; + pos.y = (number(-14, 14) + number(-14, 14)) * 20; + + pos.x += GetX(); + pos.y += GetY(); + } + + item->AddToGround(GetMapIndex(), pos); + item->StartDestroyEvent(); + } + } + } + } + + // Ÿ ƴ ó + if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE) + { + if (IsAffectFlag(AFF_TERROR)) + { + int pct = GetSkillPower(SKILL_TERROR) / 400; + + if (number(1, 100) <= pct) + return false; + } + } + + int iCurHP = GetHP(); + int iCurSP = GetSP(); + + bool IsCritical = false; + bool IsPenetrate = false; + bool IsDeathBlow = false; + + enum DamageFlag + { + DAMAGE_NORMAL = (1 << 0), + DAMAGE_POISON = (1 << 1), + DAMAGE_DODGE = (1 << 2), + DAMAGE_BLOCK = (1 << 3), + DAMAGE_PENETRATE= (1 << 4), + DAMAGE_CRITICAL = (1 << 5), + }; + + //PROF_UNIT puAttr("Attr"); + + // + // ų, ų(ڰ) ũƼð, Ѵ. + // ʾƾ ϴµ Nerf(ٿ뷱)ġ  ũƼð + // ʰ, /2 ̻Ͽ Ѵ. + // + // ̾߱Ⱑ Ƽ и ų ߰ + // + // 20091109 : 簡 û г, 70% + // + if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC) + { + if (pAttacker) + { + // ũƼ + int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); + + if (!IsPC()) + iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); + + if (iCriticalPct) + { + if (iCriticalPct >= 10) // 10 ũ 5% + (4 1% ), ġ 50̸ 20% + iCriticalPct = 5 + (iCriticalPct - 10) / 4; + else // 10 ܼ , 10 = 5% + iCriticalPct /= 2; + + //ũƼ . + iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); + + if (number(1, 100) <= iCriticalPct) + { + IsCritical = true; + dam *= 2; + EffectPacket(SE_CRITICAL); + + if (IsAffectFlag(AFF_MANASHIELD)) + { + RemoveAffect(AFF_MANASHIELD); + } + } + } + + // + int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); + + if (!IsPC()) + iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); + + + if (iPenetratePct) + { + { + CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); + + if (NULL != pkSk) + { + pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); + + iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); + } + } + + if (iPenetratePct >= 10) + { + // 10 ũ 5% + (4 1% ), ġ 50̸ 20% + iPenetratePct = 5 + (iPenetratePct - 10) / 4; + } + else + { + // 10 ܼ , 10 = 5% + iPenetratePct /= 2; + } + + //Ÿ . + iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); + + if (number(1, 100) <= iPenetratePct) + { + IsPenetrate = true; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); + + dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; + + if (IsAffectFlag(AFF_MANASHIELD)) + { + RemoveAffect(AFF_MANASHIELD); + } + } + } + } + } + // + // ޺ , Ȱ , Ÿ Ӽ Ѵ. + // + else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE) + { + if (type == DAMAGE_TYPE_NORMAL) + { + // Ÿ + if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK)) + { + if (test_server) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); + } + + SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK); + return false; + } + } + else if (type == DAMAGE_TYPE_NORMAL_RANGE) + { + // Ÿ Ÿ + if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE)) + { + if (test_server) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); + } + + SendDamagePacket(pAttacker, 0, DAMAGE_DODGE); + return false; + } + } + + if (IsAffectFlag(AFF_JEONGWIHON)) + dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100); + + if (IsAffectFlag(AFF_TERROR)) + dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100); + + if (IsAffectFlag(AFF_HOSIN)) + dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100; + + // + // Ӽ + // + if (pAttacker) + { + if (type == DAMAGE_TYPE_NORMAL) + { + // ݻ + if (GetPoint(POINT_REFLECT_MELEE)) + { + int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100; + + // NOTE: ڰ IMMUNE_REFLECT Ӽ ִٸ ݻ縦 ϴ + // ƴ϶ 1/3 ؼ  ȹ û. + if (pAttacker->IsImmune(IMMUNE_REFLECT)) + reflectDamage = int(reflectDamage / 3.0f + 0.5f); + + pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL); + } + } + + // ũƼ + int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); + + if (!IsPC()) + iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); + + if (iCriticalPct) + { + //ũƼ . + iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); + + if (number(1, 100) <= iCriticalPct) + { + IsCritical = true; + dam *= 2; + EffectPacket(SE_CRITICAL); + } + } + + // + int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); + + if (!IsPC()) + iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); + + { + CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); + + if (NULL != pkSk) + { + pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); + + iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); + } + } + + + if (iPenetratePct) + { + + //Ÿ . + iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); + + if (number(1, 100) <= iPenetratePct) + { + IsPenetrate = true; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); + dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; + } + } + + // HP ƿ + if (pAttacker->GetPoint(POINT_STEAL_HP)) + { + int pct = 1; + + if (number(1, 10) <= pct) + { + int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100; + + if (iHP > 0 && GetHP() >= iHP) + { + CreateFly(FLY_HP_SMALL, pAttacker); + pAttacker->PointChange(POINT_HP, iHP); + PointChange(POINT_HP, -iHP); + } + } + } + + // SP ƿ + if (pAttacker->GetPoint(POINT_STEAL_SP)) + { + int pct = 1; + + if (number(1, 10) <= pct) + { + int iCur; + + if (IsPC()) + iCur = iCurSP; + else + iCur = iCurHP; + + int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100; + + if (iSP > 0 && iCur >= iSP) + { + CreateFly(FLY_SP_SMALL, pAttacker); + pAttacker->PointChange(POINT_SP, iSP); + + if (IsPC()) + PointChange(POINT_SP, -iSP); + } + } + } + + // ƿ + if (pAttacker->GetPoint(POINT_STEAL_GOLD)) + { + if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD)) + { + int iAmount = number(1, GetLevel()); + pAttacker->PointChange(POINT_GOLD, iAmount); + DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount); + } + } + + // ĥ HPȸ + if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% Ȯ + { + int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; + + if (i) + { + CreateFly(FLY_HP_SMALL, pAttacker); + pAttacker->PointChange(POINT_HP, i); + } + } + + // ĥ SPȸ + if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% Ȯ + { + int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; + + if (i) + { + CreateFly(FLY_SP_SMALL, pAttacker); + pAttacker->PointChange(POINT_SP, i); + } + } + + // ش. + if (pAttacker->GetPoint(POINT_MANA_BURN_PCT)) + { + if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT)) + PointChange(POINT_SP, -50); + } + } + } + + // + // Ÿ Ǵ ų ʽ / + // + switch (type) + { + case DAMAGE_TYPE_NORMAL: + case DAMAGE_TYPE_NORMAL_RANGE: + if (pAttacker) + if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) + dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100; + + dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100; + break; + + case DAMAGE_TYPE_MELEE: + case DAMAGE_TYPE_RANGE: + case DAMAGE_TYPE_FIRE: + case DAMAGE_TYPE_ICE: + case DAMAGE_TYPE_ELEC: + case DAMAGE_TYPE_MAGIC: + if (pAttacker) + if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) + dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100; + + dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100; + break; + + default: + break; + } + + // + // (żȣ) + // + if (IsAffectFlag(AFF_MANASHIELD)) + { + // POINT_MANASHIELD ۾ + int iDamageSPPart = dam / 3; + int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100; + int iSP = GetSP(); + + // SP + if (iDamageToSP <= iSP) + { + PointChange(POINT_SP, -iDamageToSP); + dam -= iDamageSPPart; + } + else + { + // ŷ ڶ ǰ ←ҋ + PointChange(POINT_SP, -GetSP()); + dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1); + } + } + + // + // ü ( ) + // + if (GetPoint(POINT_MALL_DEFBONUS) > 0) + { + int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100); + dam -= dec_dam; + } + + if (pAttacker) + { + // + // ü ݷ ( ) + // + if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0) + { + int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100); + dam += add_dam; + } + + // + // ʽ (ѱ õ ) + // + int iEmpire = GetEmpire(); + long lMapIndex = GetMapIndex(); + int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); + + if (LC_IsYMIR() == true) + { + if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) + { + dam += (dam * 30) / 100; + } + } + + if (pAttacker->IsPC()) + { + iEmpire = pAttacker->GetEmpire(); + lMapIndex = pAttacker->GetMapIndex(); + iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); + + // ٸ 10% + if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) + { + int percent = 10; + + if (184 <= lMapIndex && lMapIndex <= 189) + { + if (LC_IsYMIR() == true) + percent = 7; + else + percent = 9; + } + else + { + if (LC_IsYMIR() == true) + percent = 8; + else + percent = 9; + } + + dam = dam * percent / 10; + } + + if (!IsPC() && GetMonsterDrainSPPoint()) + { + int iDrain = GetMonsterDrainSPPoint(); + + if (iDrain <= pAttacker->GetSP()) + pAttacker->PointChange(POINT_SP, -iDrain); + else + { + int iSP = pAttacker->GetSP(); + pAttacker->PointChange(POINT_SP, -iSP); + } + } + + } + else if (pAttacker->IsGuardNPC()) + { + SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD); + Stun(); + return true; + } + + // + // ݰ & + // + if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire())) + { + // 10% + dam += dam / 10; + } + + if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire())) + { + // 10% + dam -= dam / 10; + } + } + //puAttr.Pop(); + + if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK)) + return false; + + if (!IsPC()) + { + if (m_pkParty && m_pkParty->GetLeader()) + m_pkParty->GetLeader()->SetLastAttacked(get_dword_time()); + else + SetLastAttacked(get_dword_time()); + + // : + MonsterChat(MONSTER_CHAT_ATTACKED); + } + + if (IsStun()) + { + Dead(pAttacker); + return true; + } + + if (IsDead()) + return true; + + // ʵ . + if (type == DAMAGE_TYPE_POISON) + { + if (GetHP() - dam <= 0) + { + dam = GetHP() - 1; + } + } + + // ------------------------ + // ̾ + // ----------------------- + if (LC_IsGermany() && pAttacker && pAttacker->IsPC()) + { + int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker); + dam = dam * iDmgPct / 100; + } + + // STONE SKIN : + if (IsMonster() && IsStoneSkinner()) + { + if (GetHPPct() < GetMobTable().bStoneSkinPoint) + dam /= 2; + } + + //PROF_UNIT puRest1("Rest1"); + if (pAttacker) + { + // DEATH BLOW : Ȯ 4 (!? ̺Ʈ ͸ ) + if (pAttacker->IsMonster() && pAttacker->IsDeathBlower()) + { + if (pAttacker->IsDeathBlow()) + { + if (number(1, 4) == GetJob()) + { + IsDeathBlow = true; + dam = dam * 4; + } + } + } + + dam = BlueDragon_Damage(this, pAttacker, dam); + + BYTE damageFlag = 0; + + if (type == DAMAGE_TYPE_POISON) + damageFlag = DAMAGE_POISON; + else + damageFlag = DAMAGE_NORMAL; + + if (IsCritical == true) + damageFlag |= DAMAGE_CRITICAL; + + if (IsPenetrate == true) + damageFlag |= DAMAGE_PENETRATE; + + + // + float damMul = this->GetDamMul(); + float tempDam = dam; + dam = tempDam * damMul + 0.5f; + + + if (pAttacker) + SendDamagePacket(pAttacker, dam, damageFlag); + + if (test_server) + { + if(pAttacker) + { + pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s", + GetName(), + dam, + GetHP(), + (GetHP() * 100) / GetMaxHP(), + IsCritical ? "crit " : "", + IsPenetrate ? "pene " : "", + IsDeathBlow ? "deathblow " : ""); + } + + ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s", + pAttacker ? pAttacker->GetName() : 0, + dam, + GetHP(), + (GetHP() * 100) / GetMaxHP(), + IsCritical ? "crit " : "", + IsPenetrate ? "pene " : "", + IsDeathBlow ? "deathblow " : ""); + } + + if (m_bDetailLog) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d] ġ: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY()); + } + } + + // + // !!!!!!!!! HP ̴ κ !!!!!!!!! + // + if (!cannot_dead) + { + PointChange(POINT_HP, -dam, false); + } + + //puRest1.Pop(); + + //PROF_UNIT puRest2("Rest2"); + if (pAttacker && dam > 0 && IsNPC()) + { + //PROF_UNIT puRest20("Rest20"); + TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); + + if (it == m_map_kDamage.end()) + { + m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0))); + it = m_map_kDamage.find(pAttacker->GetVID()); + } + else + { + it->second.iTotalDamage += dam; + } + //puRest20.Pop(); + + //PROF_UNIT puRest21("Rest21"); + StartRecoveryEvent(); // ʹ ȸ Ѵ. + //puRest21.Pop(); + + //PROF_UNIT puRest22("Rest22"); + UpdateAggrPointEx(pAttacker, type, dam, it->second); + //puRest22.Pop(); + } + //puRest2.Pop(); + + //PROF_UNIT puRest3("Rest3"); + if (GetHP() <= 0) + { + Stun(); + + if (pAttacker && !pAttacker->IsNPC()) + m_dwKillerPID = pAttacker->GetPlayerID(); + else + m_dwKillerPID = 0; + } + + return false; +} + +void CHARACTER::DistributeHP(LPCHARACTER pkKiller) +{ + if (pkKiller->GetDungeon()) // ΰʴ´ + return; +} + +static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) +{ + // ġ + iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp); + + // ܺ ׽Ʈ ġ 3 ʽ + if (distribution_test_server) + iExp *= 3; + + int iBaseExp = iExp; + + // , ȸ ġ ̺Ʈ + iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100; + + // ӳ ⺻ Ǵ ġ ʽ + { + // 뵿 ޴ + if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL)) + iExp += iExp * 20 /100; + + // Ÿ ġ ʽ + if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) + iExp += iExp * 20 / 100; // 1.2 (20%) + + // ġ ι Ӽ + if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) + iExp += iExp * 30 / 100; // 1.3 (30%) + + // (2ð¥) + if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP)) + iExp += iExp * 50 / 100; + + switch (to->GetMountVnum()) + { + case 20110: + case 20111: + case 20112: + case 20113: + if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) || + to->IsEquipUniqueItem(71121) ) + { + iExp += iExp * 10 / 100; + } + break; + + case 20114: + case 20120: + case 20121: + case 20122: + case 20123: + case 20124: + case 20125: + // ġ ʽ + iExp += iExp * 30 / 100; + break; + } + } + + // Ǹ ġ ʽ + if (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada()) + { + // : ġ + if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + { + iExp += (iExp * 50 / 100); + } + + if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) + { + iExp += (iExp * 50 / 100); + } + + // PC ġ ʽ + if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) + { + if (to->IsPCBang() == true) + iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); + } + + // ȥ ʽ + iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; + } + else if (/*LC_IsNewCIBN() || */LC_IsBrazil()) + { + // : ġ + if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + { + iExp += iExp; + } + + if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) + { + iExp += iExp; + } + + // PC ġ ʽ + if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) + { + if (to->IsPCBang() == true) + iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); + } + + // ȥ ʽ + iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; + } + else + { + // : ġ + if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + { + iExp += (iExp * 20 / 100); + } + + if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) + { + iExp += (iExp * 20 / 100); + } + + // PC ġ ʽ + if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) + { + if (to->IsPCBang() == true) + iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); + } + + // ȥ ʽ + iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; + } + + iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100); + iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100); + iExp += (iExp * to->GetPoint(POINT_EXP)/100); + +/* if (speed_server) + { + iExp += iExp * CSpeedServerManager::ExpBonus(); + + } +*/ + if (test_server) + { + sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d", + to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP), + to->GetPoint(POINT_MALL_EXPBONUS), + to->GetPoint(POINT_EXP) + ); + } + + // ȹ 2005.04.21 85% + iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100; + + // ġ ѹ ȹ淮 + iExp = MIN(to->GetNextExp() / 10, iExp); + + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0) + to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp); + } + + iExp = AdjustExpByLevel(to, iExp); + + to->PointChange(POINT_EXP, iExp, true); + from->CreateFly(FLY_EXP, to); + + { + LPCHARACTER you = to->GetMarryPartner(); + // κΰ Ƽ̸ ݽ + if (you) + { + // 1 100% + DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3; + + if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || + you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0) + dwUpdatePoint = (DWORD)(dwUpdatePoint * 3); + + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID()); + + // DIVORCE_NULL_BUG_FIX + if (pMarriage && pMarriage->IsNear()) + pMarriage->Update(dwUpdatePoint); + // END_OF_DIVORCE_NULL_BUG_FIX + } + } +} + +namespace NPartyExpDistribute +{ + struct FPartyTotaler + { + int total; + int member_count; + int x, y; + + FPartyTotaler(LPCHARACTER center) + : total(0), member_count(0), x(center->GetX()), y(center->GetY()) + {}; + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + if (LC_IsYMIR()) + total += ch->GetLevel(); + else + total += party_exp_distribute_table[ch->GetLevel()]; + + ++member_count; + } + } + }; + + struct FPartyDistributor + { + int total; + LPCHARACTER c; + int x, y; + DWORD _iExp; + int m_iMode; + int m_iMemberCount; + + FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode) + : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count) + { + if (m_iMemberCount == 0) + m_iMemberCount = 1; + }; + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + DWORD iExp2 = 0; + + switch (m_iMode) + { + case PARTY_EXP_DISTRIBUTION_NON_PARITY: + if (LC_IsYMIR()) + iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total); + else + iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[ch->GetLevel()] / total); + break; + + case PARTY_EXP_DISTRIBUTION_PARITY: + iExp2 = _iExp / m_iMemberCount; + break; + + default: + sys_err("Unknown party exp distribution mode %d", m_iMode); + return; + } + + GiveExp(c, ch, iExp2); + } + } + }; +} + +typedef struct SDamageInfo +{ + int iDam; + LPCHARACTER pAttacker; + LPPARTY pParty; + + void Clear() + { + pAttacker = NULL; + pParty = NULL; + } + + inline void Distribute(LPCHARACTER ch, int iExp) + { + if (pAttacker) + GiveExp(ch, pAttacker, iExp); + else if (pParty) + { + NPartyExpDistribute::FPartyTotaler f(ch); + pParty->ForEachOnlineMember(f); + + if (pParty->IsPositionNearLeader(ch)) + iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100; + + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent()) + pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent()); + } + + // ġ ֱ (Ƽ ȹ ġ 5% ) + if (pParty->GetExpCentralizeCharacter()) + { + LPCHARACTER tch = pParty->GetExpCentralizeCharacter(); + + if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE) + { + int iExpCenteralize = (int) (iExp * 0.05f); + iExp -= iExpCenteralize; + + GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize); + } + } + + NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode()); + pParty->ForEachOnlineMember(fDist); + } + } +} TDamageInfo; + +LPCHARACTER CHARACTER::DistributeExp() +{ + int iExpToDistribute = GetExp(); + + if (iExpToDistribute <= 0) + return NULL; + + int iTotalDam = 0; + LPCHARACTER pkChrMostAttacked = NULL; + int iMostDam = 0; + + typedef std::vector TDamageInfoTable; + TDamageInfoTable damage_info_table; + std::map map_party_damage; + + damage_info_table.reserve(m_map_kDamage.size()); + + TDamageMap::iterator it = m_map_kDamage.begin(); + + // ϴ ɷ . (50m) + while (it != m_map_kDamage.end()) + { + const VID & c_VID = it->first; + int iDam = it->second.iTotalDamage; + + ++it; + + LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); + + // NPC ⵵ ϳ? -.-; + if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000) + continue; + + iTotalDam += iDam; + if (!pkChrMostAttacked || iDam > iMostDam) + { + pkChrMostAttacked = pAttacker; + iMostDam = iDam; + } + + if (pAttacker->GetParty()) + { + std::map::iterator it = map_party_damage.find(pAttacker->GetParty()); + if (it == map_party_damage.end()) + { + TDamageInfo di; + di.iDam = iDam; + di.pAttacker = NULL; + di.pParty = pAttacker->GetParty(); + map_party_damage.insert(std::make_pair(di.pParty, di)); + } + else + { + it->second.iDam += iDam; + } + } + else + { + TDamageInfo di; + + di.iDam = iDam; + di.pAttacker = pAttacker; + di.pParty = NULL; + + //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam); + //pq_damage.push(di); + damage_info_table.push_back(di); + } + } + + for (std::map::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it) + { + damage_info_table.push_back(it->second); + //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam); + } + + SetExp(0); + //m_map_kDamage.clear(); + + if (iTotalDam == 0) // ذ 0̸ + return NULL; + + if (m_pkChrStone) // ġ ѱ. + { + //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1); + int iExp = iExpToDistribute >> 1; + m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp); + iExpToDistribute -= iExp; + } + + sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d", + GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam); + //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d", + //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam); + + if (damage_info_table.empty()) + return NULL; + + // HP ȸ Ѵ. + DistributeHP(pkChrMostAttacked); // ý + + { + // ̳ Ƽ ġ 20% + ڱⰡ ŭ ġ Դ´. + TDamageInfoTable::iterator di = damage_info_table.begin(); + { + TDamageInfoTable::iterator it; + + for (it = damage_info_table.begin(); it != damage_info_table.end();++it) + { + if (it->iDam > di->iDam) + di = it; + } + } + + int iExp = iExpToDistribute / 5; + iExpToDistribute -= iExp; + + float fPercent = (float) di->iDam / iTotalDam; + + if (fPercent > 1.0f) + { + sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName()); + fPercent = 1.0f; + } + + iExp += (int) (iExpToDistribute * fPercent); + + //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam); + + di->Distribute(this, iExp); + + // 100% Ծ Ѵ. + if (fPercent == 1.0f) + return pkChrMostAttacked; + + di->Clear(); + } + + { + // 80% ġ йѴ. + TDamageInfoTable::iterator it; + + for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it) + { + TDamageInfo & di = *it; + + float fPercent = (float) di.iDam / iTotalDam; + + if (fPercent > 1.0f) + { + sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName()); + fPercent = 1.0f; + } + + //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam); + di.Distribute(this, (int) (iExpToDistribute * fPercent)); + } + } + + return pkChrMostAttacked; +} + +// ȭ +int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */) +{ + LPITEM pkBow; + + if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW) + { + return 0; + } + + LPITEM pkArrow; + + if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON || + pkArrow->GetProto()->bSubType != WEAPON_ARROW) + { + return 0; + } + + iArrowCount = MIN(iArrowCount, pkArrow->GetCount()); + + *ppkBow = pkBow; + *ppkArrow = pkArrow; + + return iArrowCount; +} + +void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount) +{ + int iCount = pkArrow->GetCount(); + DWORD dwVnum = pkArrow->GetVnum(); + iCount = iCount - MIN(iCount, dwArrowCount); + pkArrow->SetCount(iCount); + + if (iCount == 0) + { + LPITEM pkNewArrow = FindSpecifyItem(dwVnum); + + sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow)); + + if (pkNewArrow) + EquipItem(pkNewArrow); + } +} + +class CFuncShoot +{ + public: + LPCHARACTER m_me; + BYTE m_bType; + bool m_bSucceed; + + CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(FALSE) + { + } + + void operator () (DWORD dwTargetVID) + { + if (m_bType > 1) + { + if (g_bSkillDisable) + return; + + m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID); + /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK) + m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/ + } + + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); + + if (!pkVictim) + return; + + // Ұ + if (!battle_is_attackable(m_me, pkVictim)) + return; + + if (m_me->IsNPC()) + { + if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000) + return; + } + + LPITEM pkBow, pkArrow; + + switch (m_bType) + { + case 0: // ϹȰ + { + int iDam = 0; + + if (m_me->IsPC()) + { + if (m_me->GetJob() != JOB_ASSASSIN) + return; + + if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow)) + return; + + if (m_me->GetSkillGroup() != 0) + if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2) + { + if (m_me->GetSP() < 5) + return; + + m_me->PointChange(POINT_SP, -5); + } + + iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow); + m_me->UseArrow(pkArrow, 1); + + // check speed hack + DWORD dwCurrentTime = get_dword_time(); + if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime)) + iDam = 0; + } + else + iDam = CalcMeleeDamage(m_me, pkVictim); + + NormalAttackAffect(m_me, pkVictim); + + // + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100; + + //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); + + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE); + // Ÿġ + } + break; + + case 1: // Ϲ + { + int iDam; + + if (m_me->IsPC()) + return; + + iDam = CalcMagicDamage(m_me, pkVictim); + + NormalAttackAffect(m_me, pkVictim); + + // + iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100; + + //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); + + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC); + // Ÿġ + } + break; + + case SKILL_YEONSA: // + { + //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100); + int iUseArrow = 1; + + // Ż ϴ° + { + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + + if (pkVictim->IsDead()) + break; + + } + else + break; + } + } + break; + + + case SKILL_KWANKYEOK: + { + int iUseArrow = 1; + + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + break; + + case SKILL_GIGUNG: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + case SKILL_HWAJO: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + + case SKILL_HORSE_WILDATTACK_RANGE: + { + int iUseArrow = 1; + if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + m_me->UseArrow(pkArrow, iUseArrow); + } + } + + break; + + case SKILL_MARYUNG: + //case SKILL_GUMHWAN: + case SKILL_TUSOK: + case SKILL_BIPABU: + case SKILL_NOEJEON: + case SKILL_GEOMPUNG: + case SKILL_SANGONG: + case SKILL_MAHWAN: + case SKILL_PABEOB: + //case SKILL_CURSE: + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + } + break; + + case SKILL_CHAIN: + { + m_me->OnMove(true); + pkVictim->OnMove(); + + if (pkVictim->CanBeginFight()) + pkVictim->BeginFight(m_me); + + sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); + m_me->ComputeSkill(m_bType, pkVictim); + + // TODO ϱ + } + break; + + case SKILL_YONGBI: + { + m_me->OnMove(true); + } + break; + + /*case SKILL_BUDONG: + { + m_me->OnMove(true); + pkVictim->OnMove(); + + DWORD * pdw; + DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw); + pdw[0] = m_me->GetVID(); + pdw[1] = pkVictim->GetVID(); + + event_create(budong_event_func, dwEI, PASSES_PER_SEC(1)); + } + break;*/ + + default: + sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType); + break; + } + + m_bSucceed = TRUE; + } +}; + +bool CHARACTER::Shoot(BYTE bType) +{ + sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size()); + + if (!CanMove()) + { + return false; + } + + CFuncShoot f(this, bType); + + if (m_dwFlyTargetID != 0) + { + f(m_dwFlyTargetID); + m_dwFlyTargetID = 0; + } + + f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f); + m_vec_dwFlyTargets.clear(); + + return f.m_bSucceed; +} + +void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader) +{ + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); + TPacketGCFlyTargeting pack; + + //pack.bHeader = HEADER_GC_FLY_TARGETING; + pack.bHeader = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING; + pack.dwShooterVID = GetVID(); + + if (pkVictim) + { + pack.dwTargetVID = pkVictim->GetVID(); + pack.x = pkVictim->GetX(); + pack.y = pkVictim->GetY(); + + if (bHeader == HEADER_CG_FLY_TARGETING) + m_dwFlyTargetID = dwTargetVID; + else + m_vec_dwFlyTargets.push_back(dwTargetVID); + } + else + { + pack.dwTargetVID = 0; + pack.x = x; + pack.y = y; + } + + sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y); + PacketAround(&pack, sizeof(pack), this); +} + +LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr) +{ + if (NULL == pkChr) + pkChr = this; + + float fMinDist = 99999.0f; + LPCHARACTER pkVictim = NULL; + + TDamageMap::iterator it = m_map_kDamage.begin(); + + // ϴ ɷ . + while (it != m_map_kDamage.end()) + { + const VID & c_VID = it->first; + ++it; + + LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); + + if (!pAttacker) + continue; + + if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || + pAttacker->IsAffectFlag(AFF_INVISIBILITY) || + pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE)) + continue; + + float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY()); + + if (fDist < fMinDist) + { + pkVictim = pAttacker; + fMinDist = fDist; + } + } + + return pkVictim; +} + +void CHARACTER::SetVictim(LPCHARACTER pkVictim) +{ + if (!pkVictim) + { + if (0 != (DWORD)m_kVIDVictim) + MonsterLog(" "); + + m_kVIDVictim.Reset(); + battle_end(this); + } + else + { + if (m_kVIDVictim != pkVictim->GetVID()) + MonsterLog(" : %s", pkVictim->GetName()); + + m_kVIDVictim = pkVictim->GetVID(); + m_dwLastVictimSetTime = get_dword_time(); + } +} + +LPCHARACTER CHARACTER::GetVictim() const +{ + return CHARACTER_MANAGER::instance().Find(m_kVIDVictim); +} + +LPCHARACTER CHARACTER::GetProtege() const // ȣؾ +{ + if (m_pkChrStone) + return m_pkChrStone; + + if (m_pkParty) + return m_pkParty->GetLeader(); + + return NULL; +} + +int CHARACTER::GetAlignment() const +{ + return m_iAlignment; +} + +int CHARACTER::GetRealAlignment() const +{ + return m_iRealAlignment; +} + +void CHARACTER::ShowAlignment(bool bShow) +{ + if (bShow) + { + if (m_iAlignment != m_iRealAlignment) + { + m_iAlignment = m_iRealAlignment; + UpdatePacket(); + } + } + else + { + if (m_iAlignment != 0) + { + m_iAlignment = 0; + UpdatePacket(); + } + } +} + +void CHARACTER::UpdateAlignment(int iAmount) +{ + bool bShow = false; + + if (m_iAlignment == m_iRealAlignment) + bShow = true; + + int i = m_iAlignment / 10; + + m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000); + + if (bShow) + { + m_iAlignment = m_iRealAlignment; + + if (i != m_iAlignment / 10) + UpdatePacket(); + } +} + +void CHARACTER::SetKillerMode(bool isOn) +{ + if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER)) + return; + + if (isOn) + SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); + else + REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); + + m_iKillerModePulse = thecore_pulse(); + UpdatePacket(); + sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID()); +} + +bool CHARACTER::IsKillerMode() const +{ + return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); +} + +void CHARACTER::UpdateKillerMode() +{ + if (!IsKillerMode()) + return; + + int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60; + + if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds)) + SetKillerMode(false); +} + +void CHARACTER::SetPKMode(BYTE bPKMode) +{ + if (bPKMode >= PK_MODE_MAX_NUM) + return; + + if (m_bPKMode == bPKMode) + return; + + if (bPKMode == PK_MODE_GUILD && !GetGuild()) + bPKMode = PK_MODE_FREE; + + m_bPKMode = bPKMode; + UpdatePacket(); + + sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode); +} + +BYTE CHARACTER::GetPKMode() const +{ + return m_bPKMode; +} + +struct FuncForgetMyAttacker +{ + LPCHARACTER m_ch; + FuncForgetMyAttacker(LPCHARACTER ch) + { + m_ch = ch; + } + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (ch->m_kVIDVictim == m_ch->GetVID()) + ch->SetVictim(NULL); + } + } +}; + +struct FuncAggregateMonster +{ + LPCHARACTER m_ch; + FuncAggregateMonster(LPCHARACTER ch) + { + m_ch = ch; + } + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + if (ch->GetVictim()) + return; + + if (number(1, 100) <= 50) // ӽ÷ 50% Ȯ ´ + if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000) + if (ch->CanBeginFight()) + ch->BeginFight(m_ch); + } + } +}; + +struct FuncAttractRanger +{ + LPCHARACTER m_ch; + FuncAttractRanger(LPCHARACTER ch) + { + m_ch = ch; + } + + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + if (ch->GetVictim() && ch->GetVictim() != m_ch) + return; + if (ch->GetMobAttackRange() > 150) + { + int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2); + if (iNewRange < 150) + iNewRange = 150; + + ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false); + } + } + } +}; + +struct FuncPullMonster +{ + LPCHARACTER m_ch; + int m_iLength; + FuncPullMonster(LPCHARACTER ch, int iLength = 300) + { + m_ch = ch; + m_iLength = iLength; + } + + void operator()(LPENTITY ent) + { + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER ch = (LPCHARACTER) ent; + if (ch->IsPC()) + return; + if (!ch->IsMonster()) + return; + //if (ch->GetVictim() && ch->GetVictim() != m_ch) + //return; + float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY()); + if (fDist > 3000 || fDist < 100) + return; + + float fNewDist = fDist - m_iLength; + if (fNewDist < 100) + fNewDist = 100; + + float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY()); + float fx; + float fy; + + GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy); + long tx = (long)(ch->GetX() + fx); + long ty = (long)(ch->GetY() + fy); + + ch->Sync(tx, ty); + ch->Goto(tx, ty); + ch->CalculateMoveDuration(); + + ch->SyncPacket(); + } + } +}; + +void CHARACTER::ForgetMyAttacker() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncForgetMyAttacker f(this); + pSec->ForEachAround(f); + } + ReviveInvisible(5); +} + +void CHARACTER::AggregateMonster() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncAggregateMonster f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::AttractRanger() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncAttractRanger f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::PullMonster() +{ + LPSECTREE pSec = GetSectree(); + if (pSec) + { + FuncPullMonster f(this); + pSec->ForEachAround(f); + } +} + +void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info) +{ + // Ư ŸԿ ö󰣴 + switch (type) + { + case DAMAGE_TYPE_NORMAL_RANGE: + dam = (int) (dam*1.2f); + break; + + case DAMAGE_TYPE_RANGE: + dam = (int) (dam*1.5f); + break; + + case DAMAGE_TYPE_MAGIC: + dam = (int) (dam*1.2f); + break; + + default: + break; + } + + // ڰ ʽ ش. + if (pAttacker == GetVictim()) + dam = (int) (dam * 1.2f); + + info.iAggro += dam; + + if (info.iAggro < 0) + info.iAggro = 0; + + //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total); + if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL) + { + LPPARTY pParty = GetParty(); + + // ϴ + int iPartyAggroDist = dam; + + if (pParty->GetLeaderPID() == GetVID()) + iPartyAggroDist /= 2; + else + iPartyAggroDist /= 3; + + pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID()); + } + + ChangeVictimByAggro(info.iAggro, pAttacker); +} + +void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam) +{ + if (IsDead() || IsStun()) + return; + + TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); + + if (it == m_map_kDamage.end()) + { + m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(0, dam))); + it = m_map_kDamage.find(pAttacker->GetVID()); + } + + UpdateAggrPointEx(pAttacker, type, dam, it->second); +} + +void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim) +{ + if (get_dword_time() - m_dwLastVictimSetTime < 3000) // 3ʴ ٷѴ + return; + + if (pNewVictim == GetVictim()) + { + if (m_iMaxAggro < iNewAggro) + { + m_iMaxAggro = iNewAggro; + return; + } + + // Aggro + TDamageMap::iterator it; + TDamageMap::iterator itFind = m_map_kDamage.end(); + + for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) + { + if (it->second.iAggro > iNewAggro) + { + LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); + + if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000) + { + itFind = it; + iNewAggro = it->second.iAggro; + } + } + } + + if (itFind != m_map_kDamage.end()) + { + m_iMaxAggro = iNewAggro; + SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first)); + m_dwStateDuration = 1; + } + } + else + { + if (m_iMaxAggro < iNewAggro) + { + m_iMaxAggro = iNewAggro; + SetVictim(pNewVictim); + m_dwStateDuration = 1; + } + } +} + diff --git a/game/src/char_change_empire.cpp b/game/src/char_change_empire.cpp new file mode 100644 index 0000000..69ced20 --- /dev/null +++ b/game/src/char_change_empire.cpp @@ -0,0 +1,210 @@ + +#include "stdafx.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "db.h" +#include "guild_manager.h" +#include "marriage.h" + +/* + Return Value + 0 : or + 1 : ٲٷ + 2 : ijͰ + 3 : ȥ ijͰ + + 999 : ̵ +*/ +int CHARACTER::ChangeEmpire(BYTE empire) +{ + if (GetEmpire() == empire) + return 1; + + char szQuery[1024+1]; + DWORD dwAID; + DWORD dwPID[4]; + memset(dwPID, 0, sizeof(dwPID)); + + { + // 1. pid ´ + snprintf(szQuery, sizeof(szQuery), + "SELECT id, pid1, pid2, pid3, pid4 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", + get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + + std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); + + if (msg->Get()->uiNumRows == 0) + { + return 0; + } + + MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); + + str_to_number(dwAID, row[0]); + str_to_number(dwPID[0], row[1]); + str_to_number(dwPID[1], row[2]); + str_to_number(dwPID[2], row[3]); + str_to_number(dwPID[3], row[4]); + } + + const int loop = 4; + + { + // 2. ij ´. + // ijͶ 忡 Ǿ ִٸ, ̵ . + DWORD dwGuildID[4]; + CGuild * pGuild[4]; + SQLMsg * pMsg = NULL; + + for (int i = 0; i < loop; ++i) + { + snprintf(szQuery, sizeof(szQuery), "SELECT guild_id FROM guild_member%s WHERE pid=%u", get_table_postfix(), dwPID[i]); + + pMsg = DBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows > 0) + { + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + str_to_number(dwGuildID[i], row[0]); + + pGuild[i] = CGuildManager::instance().FindGuild(dwGuildID[i]); + + if (pGuild[i] != NULL) + { + M2_DELETE(pMsg); + return 2; + } + } + else + { + dwGuildID[i] = 0; + pGuild[i] = NULL; + } + + M2_DELETE(pMsg); + } + } + } + + { + // 3. ij ȥ ´. + // ijͶ ȥ ¶ ̵ . + for (int i = 0; i < loop; ++i) + { + if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true) + return 3; + } + } + + { + // 4. db Ʈ Ѵ. + snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", + get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + + std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); + + if (msg->Get()->uiAffectedRows > 0) + { + // 5. ̷ ߰Ѵ. + SetChangeEmpireCount(); + + return 999; + } + } + + return 0; +} + +int CHARACTER::GetChangeEmpireCount() const +{ + char szQuery[1024+1]; + DWORD dwAID = GetAID(); + + if (dwAID == 0) + return 0; + + snprintf(szQuery, sizeof(szQuery), "SELECT change_count FROM change_empire WHERE account_id = %u", dwAID); + + SQLMsg * pMsg = DBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows == 0) + { + M2_DELETE(pMsg); + return 0; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + DWORD count = 0; + str_to_number(count, row[0]); + + M2_DELETE(pMsg); + + return count; + } + + return 0; +} + +void CHARACTER::SetChangeEmpireCount() +{ + char szQuery[1024+1]; + + DWORD dwAID = GetAID(); + + if (dwAID == 0) return; + + int count = GetChangeEmpireCount(); + + if (count == 0) + { + count++; + snprintf(szQuery, sizeof(szQuery), "INSERT INTO change_empire VALUES(%u, %d, NOW())", dwAID, count); + } + else + { + count++; + snprintf(szQuery, sizeof(szQuery), "UPDATE change_empire SET change_count=%d WHERE account_id=%u", count, dwAID); + } + + std::auto_ptr pmsg(DBManager::instance().DirectQuery(szQuery)); +} + +DWORD CHARACTER::GetAID() const +{ + char szQuery[1024+1]; + DWORD dwAID = 0; + + snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", + get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); + + SQLMsg* pMsg = DBManager::instance().DirectQuery(szQuery); + + if (pMsg != NULL) + { + if (pMsg->Get()->uiNumRows == 0) + { + M2_DELETE(pMsg); + return 0; + } + + MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); + + str_to_number(dwAID, row[0]); + + M2_DELETE(pMsg); + + return dwAID; + } + else + { + return 0; + } +} + diff --git a/game/src/char_dragonsoul.cpp b/game/src/char_dragonsoul.cpp new file mode 100644 index 0000000..308ccaa --- /dev/null +++ b/game/src/char_dragonsoul.cpp @@ -0,0 +1,143 @@ +#include "stdafx.h" +#include "char.h" +#include "item.h" +#include "desc.h" +#include "DragonSoul.h" +#include "log.h" + +// ȥ ʱȭ +// ȥ on/off Affect DZ , +// ȥ Affect ִٸ ִ ȥ activateؾѴ. +// ȥ ڰ QuestFlag ұ , +// Ʈ Flag ȥ ڰ о´. + +// ij affect, quest load DZ DragonSoul_Initialize ȣϸ ȵȴ. +// affect εǾ LoadAffect ȣ. +void CHARACTER::DragonSoul_Initialize() +{ + for (int i = INVENTORY_MAX_NUM + WEAR_MAX_NUM; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + LPITEM pItem = GetItem(TItemPos(INVENTORY, i)); + if (NULL != pItem) + pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 0); + } + + if (FindAffect(AFFECT_DRAGON_SOUL_DECK_0)) + { + DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_0); + } + else if (FindAffect(AFFECT_DRAGON_SOUL_DECK_1)) + { + DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_1); + } +} + +int CHARACTER::DragonSoul_GetActiveDeck() const +{ + return m_pointsInstant.iDragonSoulActiveDeck; +} + +bool CHARACTER::DragonSoul_IsDeckActivated() const +{ + return m_pointsInstant.iDragonSoulActiveDeck >= 0; +} + +bool CHARACTER::DragonSoul_IsQualified() const +{ + return FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED) != NULL; +} + +void CHARACTER::DragonSoul_GiveQualification() +{ + if(NULL == FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED)) + { + LogManager::instance().CharLog(this, 0, "DS_QUALIFIED", ""); + } + AddAffect(AFFECT_DRAGON_SOUL_QUALIFIED, APPLY_NONE, 0, AFF_NONE, INFINITE_AFFECT_DURATION, 0, false, false); + //SetQuestFlag("dragon_soul.is_qualified", 1); + //// ڰִٸ POINT_DRAGON_SOUL_IS_QUALIFIED 1 + //PointChange(POINT_DRAGON_SOUL_IS_QUALIFIED, 1 - GetPoint(POINT_DRAGON_SOUL_IS_QUALIFIED)); +} + +bool CHARACTER::DragonSoul_ActivateDeck(int deck_idx) +{ + if (deck_idx < DRAGON_SOUL_DECK_0 || deck_idx >= DRAGON_SOUL_DECK_MAX_NUM) + { + return false; + } + + if (DragonSoul_GetActiveDeck() == deck_idx) + return true; + + DragonSoul_DeactivateAll(); + + if (!DragonSoul_IsQualified()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ڰ Ȱȭ ʾҽϴ.")); + return false; + } + + AddAffect(AFFECT_DRAGON_SOUL_DECK_0 + deck_idx, APPLY_NONE, 0, 0, INFINITE_AFFECT_DURATION, 0, false); + + m_pointsInstant.iDragonSoulActiveDeck = deck_idx; + + for (int i = DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * deck_idx; + i < DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * (deck_idx + 1); i++) + { + LPITEM pItem = GetInventoryItem(i); + if (NULL != pItem) + DSManager::instance().ActivateDragonSoul(pItem); + } +} + +void CHARACTER::DragonSoul_DeactivateAll() +{ + for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true); + } + m_pointsInstant.iDragonSoulActiveDeck = -1; + RemoveAffect(AFFECT_DRAGON_SOUL_DECK_0); + RemoveAffect(AFFECT_DRAGON_SOUL_DECK_1); +} + +void CHARACTER::DragonSoul_CleanUp() +{ + for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++) + { + DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true); + } +} + +bool CHARACTER::DragonSoul_RefineWindow_Open(LPENTITY pEntity) +{ + if (NULL == m_pointsInstant.m_pDragonSoulRefineWindowOpener) + { + m_pointsInstant.m_pDragonSoulRefineWindowOpener = pEntity; + } + + TPacketGCDragonSoulRefine PDS; + PDS.header = HEADER_GC_DRAGON_SOUL_REFINE; + PDS.bSubType = DS_SUB_HEADER_OPEN; + LPDESC d = GetDesc(); + + if (NULL == d) + { + sys_err ("User(%s)'s DESC is NULL POINT.", GetName()); + return false; + } + + d->Packet(&PDS, sizeof(PDS)); + return true; +} + +bool CHARACTER::DragonSoul_RefineWindow_Close() +{ + m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL; + return true; +} + +bool CHARACTER::DragonSoul_RefineWindow_CanRefine() +{ + return NULL != m_pointsInstant.m_pDragonSoulRefineWindowOpener; +} \ No newline at end of file diff --git a/game/src/char_hackshield.cpp b/game/src/char_hackshield.cpp new file mode 100644 index 0000000..fb800ce --- /dev/null +++ b/game/src/char_hackshield.cpp @@ -0,0 +1,93 @@ + +#include "stdafx.h" + +#include "char.h" + +#include "config.h" +#include "event.h" +#include "HackShield.h" +#include "log.h" +#include "desc.h" +#include "packet.h" + +EVENTINFO(hackshield_event_info) +{ + DynamicCharacterPtr CharPtr; +}; + +EVENTFUNC(hackshield_event) +{ + hackshield_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "hackshield_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->CharPtr; + + if (NULL == ch) + { + sys_err("HShield: character pointer is null"); + return 0; + } + + if (NULL == ch->GetDesc()) + { + sys_err("HShield: character has no descriptor"); + return 0; + } + + if (false == ch->GetHackShieldCheckMode()) + { + if (false == CHackShieldManager::instance().SendCheckPacket(ch)) + { + return 0; + } + else + { + ch->SetHackShieldCheckMode(true); + + return HackShield_CheckCycleTime; + } + } + + sys_log(0, "HShield: no response from Player(%u)", ch->GetPlayerID()); + + LogManager::instance().HackShieldLog(0, ch); + + ch->m_HackShieldCheckEvent = NULL; + + ch->GetDesc()->SetPhase(PHASE_CLOSE); + + return 0; +} + +void CHARACTER::StartHackShieldCheckCycle(int seconds) +{ + StopHackShieldCheckCycle(); + + if (false == isHackShieldEnable) + return; + + hackshield_event_info* info = AllocEventInfo(); + + info->CharPtr = this; + + m_HackShieldCheckEvent = event_create(hackshield_event, info, seconds); + + sys_log(0, "HShield: StartHackShieldCheckCycle %d", seconds); +} + +void CHARACTER::StopHackShieldCheckCycle() +{ + if (NULL != m_HackShieldCheckEvent) + { + event_cancel(&m_HackShieldCheckEvent); + m_HackShieldCheckEvent = NULL; + + sys_log(0, "HShield: StopHackShieldCheckCycle"); + } +} + diff --git a/game/src/char_horse.cpp b/game/src/char_horse.cpp new file mode 100644 index 0000000..6617c75 --- /dev/null +++ b/game/src/char_horse.cpp @@ -0,0 +1,387 @@ +#include "stdafx.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "packet.h" +#include "guild.h" +#include "vector.h" +#include "questmanager.h" +#include "item.h" +#include "horsename_manager.h" +#include "locale_service.h" +#include "arena.h" + +#include "../../common/VnumHelper.h" + +bool CHARACTER::StartRiding() +{ + if (IsDead() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + // νõ Ÿ + LPITEM armor = GetWear(WEAR_BODY); + + if (armor && (armor->GetVnum() >= 11901 && armor->GetVnum() <= 11904)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ Ż ϴ.")); + return false; + } + + if (LC_IsCanada() == true) + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + return false; + } + + + DWORD dwMountVnum = m_chHorse ? m_chHorse->GetRaceNum() : GetMyHorseVnum(); + + if (false == CHorseRider::StartRiding()) + { + if (GetHorseLevel() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϰ ʽϴ.")); + else if (GetHorseHealth() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׾ִ Դϴ.")); + else if (GetHorseStamina() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׹̳ʰ Ͽ Ż ϴ.")); + + return false; + } + + // ȯ ְ + HorseSummon(false); + + MountVnum(dwMountVnum); + + if(test_server) + sys_log(0, "Ride Horse : %s ", GetName()); + + return true; +} + +bool CHARACTER::StopRiding() +{ + if (CHorseRider::StopRiding()) + { + quest::CQuestManager::instance().Unmount(GetPlayerID()); + + if (!IsDead() && !IsStun()) + { + DWORD dwOldVnum = GetMountVnum(); + MountVnum(0); + + // [NOTE] ڱⰡ ȯϵ + HorseSummon(true, false, dwOldVnum); + } + else + { + m_dwMountVnum = 0; + ComputePoints(); + UpdatePacket(); + } + + PointChange(POINT_ST, 0); + PointChange(POINT_DX, 0); + PointChange(POINT_HT, 0); + PointChange(POINT_IQ, 0); + + return true; + } + + return false; +} + +EVENTFUNC(horse_dead_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "horse_dead_event> Null pointer" ); + return 0; + } + + // + LPCHARACTER ch = info->ch; + if (ch == NULL) { + return 0; + } + ch->HorseSummon(false); + return 0; +} + +void CHARACTER::SetRider(LPCHARACTER ch) +{ + if (m_chRider) + m_chRider->ClearHorseInfo(); + + m_chRider = ch; + + if (m_chRider) + m_chRider->SendHorseInfo(); +} + +LPCHARACTER CHARACTER::GetRider() const +{ + return m_chRider; +} + + +void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const char* pPetName) +{ + if ( bSummon ) + { + //NOTE : summonߴµ ̹ horse ƹ͵ Ѵ. + if( m_chHorse != NULL ) + return; + + if (GetHorseLevel() <= 0) + return; + + // 𰡸 Ÿ ִٸ + if (IsRiding()) + return; + + sys_log(0, "HorseSummon : %s lv:%d bSummon:%d fromFar:%d", GetName(), GetLevel(), bSummon, bFromFar); + + long x = GetX(); + long y = GetY(); + + if (GetHorseHealth() <= 0) + bFromFar = false; + + if (bFromFar) + { + x += (number(0, 1) * 2 - 1) * number(2000, 2500); + y += (number(0, 1) * 2 - 1) * number(2000, 2500); + } + else + { + x += number(-100, 100); + y += number(-100, 100); + } + + m_chHorse = CHARACTER_MANAGER::instance().SpawnMob( + (0 == dwVnum) ? GetMyHorseVnum() : dwVnum, + GetMapIndex(), + x, y, + GetZ(), false, (int)(GetRotation()+180), false); + + if (!m_chHorse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯ Ͽϴ.")); + return; + } + + if (GetHorseHealth() <= 0) + { + // ó ְ ϴ ó + m_chHorse->SetPosition(POS_DEAD); + + // ðִ . + char_event_info* info = AllocEventInfo(); + info->ch = this; + m_chHorse->m_pkDeadEvent = event_create(horse_dead_event, info, PASSES_PER_SEC(60)); + } + + m_chHorse->SetLevel(GetHorseLevel()); + + const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID()); + + if ( pHorseName != NULL && strlen(pHorseName) != 0 ) + { + m_chHorse->m_stName = pHorseName; + } + else + { + m_chHorse->m_stName = GetName(); + m_chHorse->m_stName += LC_TEXT(" "); + } + + if (!m_chHorse->Show(GetMapIndex(), x, y, GetZ())) + { + M2_DESTROY_CHARACTER(m_chHorse); + sys_err("cannot show monster"); + m_chHorse = NULL; + return; + } + + if ((GetHorseHealth() <= 0)) + { + TPacketGCDead pack; + pack.header = HEADER_GC_DEAD; + pack.vid = m_chHorse->GetVID(); + PacketAround(&pack, sizeof(pack)); + } + + m_chHorse->SetRider(this); + } + else + { + if (!m_chHorse) + return; + + LPCHARACTER chHorse = m_chHorse; + + chHorse->SetRider(NULL); // m_chHorse assign to NULL + + if (!bFromFar) + { + M2_DESTROY_CHARACTER(chHorse); + } + else + { + // ־鼭 ó ϱ + chHorse->SetNowWalking(false); + float fx, fy; + chHorse->SetRotation(GetDegreeFromPositionXY(chHorse->GetX(), chHorse->GetY(), GetX(), GetY())+180); + GetDeltaByDegree(chHorse->GetRotation(), 3500, &fx, &fy); + chHorse->Goto((long)(chHorse->GetX()+fx), (long) (chHorse->GetY()+fy)); + chHorse->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + + m_chHorse = NULL; + } +} + +DWORD CHARACTER::GetMyHorseVnum() const +{ + int delta = 0; + + if (GetGuild()) + { + ++delta; + + if (GetGuild()->GetMasterPID() == GetPlayerID()) + ++delta; + } + + return c_aHorseStat[GetHorseLevel()].iNPCRace + delta; +} + +void CHARACTER::HorseDie() +{ + CHorseRider::HorseDie(); + HorseSummon(false); +} + +bool CHARACTER::ReviveHorse() +{ + if (CHorseRider::ReviveHorse()) + { + HorseSummon(false); + HorseSummon(true); + return true; + } + return false; +} + +void CHARACTER::ClearHorseInfo() +{ + if (!IsHorseRiding()) + { + ChatPacket(CHAT_TYPE_COMMAND, "hide_horse_state"); + + m_bSendHorseLevel = 0; + m_bSendHorseHealthGrade = 0; + m_bSendHorseStaminaGrade = 0; + } + + m_chHorse = NULL; + +} + +void CHARACTER::SendHorseInfo() +{ + if (m_chHorse || IsHorseRiding()) + { + int iHealthGrade; + int iStaminaGrade; + /* + HP +3: 70% < ~ <= 100% +2: 30% < ~ <= 70% +1: 0% < ~ <= 30% +0: + +STM + +3: 71% < ~ <= 100% +2: 31% < ~ <= 70% +1: 10% < ~ <= 30% +0: ~ <= 10% + */ + if (GetHorseHealth() == 0) + iHealthGrade = 0; + else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 3) + iHealthGrade = 1; + else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 7) + iHealthGrade = 2; + else + iHealthGrade = 3; + + if (GetHorseStamina() * 10 <= GetHorseMaxStamina()) + iStaminaGrade = 0; + else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 3) + iStaminaGrade = 1; + else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 7) + iStaminaGrade = 2; + else + iStaminaGrade = 3; + + if (m_bSendHorseLevel != GetHorseLevel() || + m_bSendHorseHealthGrade != iHealthGrade || + m_bSendHorseStaminaGrade != iStaminaGrade) + { + ChatPacket(CHAT_TYPE_COMMAND, "horse_state %d %d %d", GetHorseLevel(), iHealthGrade, iStaminaGrade); + + // FIX : Ŭ̾Ʈ " " ǥ Լ Կ returnν Ʒ ڵ带 Ѵٸ + // Ѵ ȯϴ ù װ .. Ȯ ľ غ . + m_bSendHorseLevel = GetHorseLevel(); + m_bSendHorseHealthGrade = iHealthGrade; + m_bSendHorseStaminaGrade = iStaminaGrade; + } + } +} + +bool CHARACTER::CanUseHorseSkill() +{ + if(IsRiding()) + { + if (GetHorseGrade() == 3) + return true; + else + return false; + + if(GetMountVnum()) + { + if (GetMountVnum() >= 20209 && GetMountVnum() <= 20212) + return true; + + //󸶴 渶 + if (CMobVnumHelper::IsRamadanBlackHorse(GetMountVnum())) + return true; + } + else + return false; + + } + + return false; +} + +void CHARACTER::SetHorseLevel(int iLevel) +{ + CHorseRider::SetHorseLevel(iLevel); + SetSkillLevel(SKILL_HORSE, GetHorseLevel()); +} + diff --git a/game/src/char_item.cpp b/game/src/char_item.cpp new file mode 100644 index 0000000..cfd9f65 --- /dev/null +++ b/game/src/char_item.cpp @@ -0,0 +1,7541 @@ +#include "stdafx.h" + +#include + +#include "utils.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "item_manager.h" +#include "desc.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "packet.h" +#include "affect.h" +#include "skill.h" +#include "start_position.h" +#include "mob_manager.h" +#include "db.h" +#include "log.h" +#include "vector.h" +#include "buffer_manager.h" +#include "questmanager.h" +#include "fishing.h" +#include "party.h" +#include "dungeon.h" +#include "refine.h" +#include "unique_item.h" +#include "war_map.h" +#include "xmas_event.h" +#include "marriage.h" +#include "monarch.h" +#include "polymorph.h" +#include "blend_item.h" +#include "castle.h" +#include "BattleArena.h" +#include "arena.h" +#include "dev_log.h" +#include "pcbang.h" +#include "threeway_war.h" + +#include "safebox.h" +#include "shop.h" + +#include "../../common/VnumHelper.h" +#include "DragonSoul.h" +#include "buff_on_attributes.h" +#include "belt_inventory_helper.h" + +//auction_temp +#ifdef __AUCTION__ +#include "auction_manager.h" +#endif +const int ITEM_BROKEN_METIN_VNUM = 28960; + +// CHANGE_ITEM_ATTRIBUTES +const DWORD CHARACTER::msc_dwDefaultChangeItemAttrCycle = 10; +const char CHARACTER::msc_szLastChangeItemAttrFlag[] = "Item.LastChangeItemAttr"; +const char CHARACTER::msc_szChangeItemAttrCycleFlag[] = "change_itemattr_cycle"; +// END_OF_CHANGE_ITEM_ATTRIBUTES +const BYTE g_aBuffOnAttrPoints[] = { POINT_ENERGY, POINT_COSTUME_ATTR_BONUS }; + +struct FFindStone +{ + std::map m_mapStone; + + void operator()(LPENTITY pEnt) + { + if (pEnt->IsType(ENTITY_CHARACTER) == true) + { + LPCHARACTER pChar = (LPCHARACTER)pEnt; + + if (pChar->IsStone() == true) + { + m_mapStone[(DWORD)pChar->GetVID()] = pChar; + } + } + } +}; + + +//ȯ, ȯ, ȥ +static bool IS_SUMMON_ITEM(int vnum) +{ + switch (vnum) + { + case 22000: + case 22010: + case 22011: + case 22020: + case ITEM_MARRIAGE_RING: + return true; + } + + return false; +} + +static bool IS_MONKEY_DUNGEON(int map_index) +{ + switch (map_index) + { + case 5: + case 25: + case 45: + case 108: + case 109: + return true;; + } + + return false; +} + +bool IS_SUMMONABLE_ZONE(int map_index) +{ + // Ű + if (IS_MONKEY_DUNGEON(map_index)) + return false; + // + if (IS_CASTLE_MAP(map_index)) + return false; + + switch (map_index) + { + case 66 : // Ÿ + case 71 : // Ź 2 + case 72 : // õ + case 73 : // õ 2 + case 193 : // Ź 2-1 +#if 0 + case 184 : // õ (ż) + case 185 : // õ 2(ż) + case 186 : // õ (õ) + case 187 : // õ 2(õ) + case 188 : // õ () + case 189 : // õ 2() +#endif +// case 206 : // Ʊ͵ + case 216 : // Ʊ͵ + case 217 : // Ź 3 + case 208 : // õ () + return false; + } + + if (CBattleArena::IsBattleArenaMap(map_index)) return false; + + // private Ұ + if (map_index > 10000) return false; + + return true; +} + +bool IS_BOTARYABLE_ZONE(int nMapIndex) +{ + if (LC_IsYMIR() == false && LC_IsKorea() == false) return true; + + switch (nMapIndex) + { + case 1 : + case 3 : + case 21 : + case 23 : + case 41 : + case 43 : + return true; + } + + return false; +} + +// item socket Ÿ԰ üũ -- by mhh +static bool FN_check_item_socket(LPITEM item) +{ + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item->GetSocket(i) != item->GetProto()->alSockets[i]) + return false; + } + + return true; +} + +// item socket -- by mhh +static void FN_copy_item_socket(LPITEM dest, LPITEM src) +{ + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + dest->SetSocket(i, src->GetSocket(i)); + } +} +static bool FN_check_item_sex(LPCHARACTER ch, LPITEM item) +{ + // + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_MALE)) + { + if (SEX_MALE==GET_SEX(ch)) + return false; + } + // ڱ + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_FEMALE)) + { + if (SEX_FEMALE==GET_SEX(ch)) + return false; + } + + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// ITEM HANDLING +///////////////////////////////////////////////////////////////////////////// +bool CHARACTER::CanHandleItem(bool bSkipCheckRefine, bool bSkipObserver) +{ + if (!bSkipObserver) + if (m_bIsObserver) + return false; + + if (GetMyShop()) + return false; + + if (!bSkipCheckRefine) + if (m_bUnderRefine) + return false; + + if (IsCubeOpen() || NULL != DragonSoul_RefineWindow_GetOpener()) + return false; + + if (IsWarping()) + return false; + + return true; +} + +LPITEM CHARACTER::GetInventoryItem(WORD wCell) const +{ + return GetItem(TItemPos(INVENTORY, wCell)); +} +LPITEM CHARACTER::GetItem(TItemPos Cell) const +{ + if (!IsValidItemPosition(Cell)) + return NULL; + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + switch (window_type) + { + case INVENTORY: + case EQUIPMENT: + if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) + { + sys_err("CHARACTER::GetInventoryItem: invalid item cell %d", wCell); + return NULL; + } + return m_pointsInstant.pItems[wCell]; + case DRAGON_SOUL_INVENTORY: + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + { + sys_err("CHARACTER::GetInventoryItem: invalid DS item cell %d", wCell); + return NULL; + } + return m_pointsInstant.pDSItems[wCell]; + + default: + return NULL; + } + return NULL; +} + +void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) +{ + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + if ((unsigned long)((CItem*)pItem) == 0xff || (unsigned long)((CItem*)pItem) == 0xffffffff) + { + sys_err("!!! FATAL ERROR !!! item == 0xff (char: %s cell: %u)", GetName(), wCell); + core_dump(); + return; + } + + if (pItem && pItem->GetOwner()) + { + assert(!"GetOwner exist"); + return; + } + // ⺻ κ丮 + switch(window_type) + { + case INVENTORY: + case EQUIPMENT: + { + if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) + { + sys_err("CHARACTER::SetItem: invalid item cell %d", wCell); + return; + } + + LPITEM pOld = m_pointsInstant.pItems[wCell]; + + if (pOld) + { + if (wCell < INVENTORY_MAX_NUM) + { + for (int i = 0; i < pOld->GetSize(); ++i) + { + int p = wCell + (i * 5); + + if (p >= INVENTORY_MAX_NUM) + continue; + + if (m_pointsInstant.pItems[p] && m_pointsInstant.pItems[p] != pOld) + continue; + + m_pointsInstant.bItemGrid[p] = 0; + } + } + else + m_pointsInstant.bItemGrid[wCell] = 0; + } + + if (pItem) + { + if (wCell < INVENTORY_MAX_NUM) + { + for (int i = 0; i < pItem->GetSize(); ++i) + { + int p = wCell + (i * 5); + + if (p >= INVENTORY_MAX_NUM) + continue; + + // wCell + 1 ϴ üũ + // óϱ + m_pointsInstant.bItemGrid[p] = wCell + 1; + } + } + else + m_pointsInstant.bItemGrid[wCell] = wCell + 1; + } + + m_pointsInstant.pItems[wCell] = pItem; + } + break; + // ȥ κ丮 + case DRAGON_SOUL_INVENTORY: + { + LPITEM pOld = m_pointsInstant.pDSItems[wCell]; + + if (pOld) + { + if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) + { + for (int i = 0; i < pOld->GetSize(); ++i) + { + int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + continue; + + if (m_pointsInstant.pDSItems[p] && m_pointsInstant.pDSItems[p] != pOld) + continue; + + m_pointsInstant.wDSItemGrid[p] = 0; + } + } + else + m_pointsInstant.wDSItemGrid[wCell] = 0; + } + + if (pItem) + { + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + { + sys_err("CHARACTER::SetItem: invalid DS item cell %d", wCell); + return; + } + + if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) + { + for (int i = 0; i < pItem->GetSize(); ++i) + { + int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + continue; + + // wCell + 1 ϴ üũ + // óϱ + m_pointsInstant.wDSItemGrid[p] = wCell + 1; + } + } + else + m_pointsInstant.wDSItemGrid[wCell] = wCell + 1; + } + + m_pointsInstant.pDSItems[wCell] = pItem; + } + break; + default: + sys_err ("Invalid Inventory type %d", window_type); + return; + } + + if (GetDesc()) + { + // Ȯ : ÷ + if (pItem) + { + TPacketGCItemSet pack; + pack.header = HEADER_GC_ITEM_SET; + pack.Cell = Cell; + + pack.count = pItem->GetCount(); + pack.vnum = pItem->GetVnum(); + pack.flags = pItem->GetFlag(); + pack.anti_flags = pItem->GetAntiFlag(); + pack.highlight = (Cell.window_type == DRAGON_SOUL_INVENTORY); + + + thecore_memcpy(pack.alSockets, pItem->GetSockets(), sizeof(pack.alSockets)); + thecore_memcpy(pack.aAttr, pItem->GetAttributes(), sizeof(pack.aAttr)); + + GetDesc()->Packet(&pack, sizeof(TPacketGCItemSet)); + } + else + { + TPacketGCItemDelDeprecated pack; + pack.header = HEADER_GC_ITEM_DEL; + pack.Cell = Cell; + pack.count = 0; + pack.vnum = 0; + memset(pack.alSockets, 0, sizeof(pack.alSockets)); + memset(pack.aAttr, 0, sizeof(pack.aAttr)); + + GetDesc()->Packet(&pack, sizeof(TPacketGCItemDelDeprecated)); + } + } + + if (pItem) + { + pItem->SetCell(this, wCell); + switch (window_type) + { + case INVENTORY: + case EQUIPMENT: + if ((wCell < INVENTORY_MAX_NUM) || (BELT_INVENTORY_SLOT_START <= wCell && BELT_INVENTORY_SLOT_END > wCell)) + pItem->SetWindow(INVENTORY); + else + pItem->SetWindow(EQUIPMENT); + break; + case DRAGON_SOUL_INVENTORY: + pItem->SetWindow(DRAGON_SOUL_INVENTORY); + break; + } + } +} + +LPITEM CHARACTER::GetWear(BYTE bCell) const +{ + // > WEAR_MAX_NUM : ȥ Ե. + if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX) + { + sys_err("CHARACTER::GetWear: invalid wear cell %d", bCell); + return NULL; + } + + return m_pointsInstant.pItems[INVENTORY_MAX_NUM + bCell]; +} + +void CHARACTER::SetWear(BYTE bCell, LPITEM item) +{ + // > WEAR_MAX_NUM : ȥ Ե. + if (bCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX) + { + sys_err("CHARACTER::SetItem: invalid item cell %d", bCell); + return; + } + + SetItem(TItemPos (INVENTORY, INVENTORY_MAX_NUM + bCell), item); + + if (!item && bCell == WEAR_WEAPON) + { + // Ͱ ̶ ȿ ־ Ѵ. + if (IsAffectFlag(AFF_GWIGUM)) + RemoveAffect(SKILL_GWIGEOM); + + if (IsAffectFlag(AFF_GEOMGYEONG)) + RemoveAffect(SKILL_GEOMKYUNG); + } +} + +void CHARACTER::ClearItem() +{ + int i; + LPITEM item; + + for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + { + if ((item = GetInventoryItem(i))) + { + item->SetSkipSave(true); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + item->RemoveFromCharacter(); + M2_DESTROY_ITEM(item); + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i)))) + { + item->SetSkipSave(true); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + item->RemoveFromCharacter(); + M2_DESTROY_ITEM(item); + } + } +} + +bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, BYTE bSize, int iExceptionCell) const +{ + switch (Cell.window_type) + { + case INVENTORY: + { + BYTE bCell = Cell.cell; + + // bItemCell 0 false Ÿ + 1 ؼ óѴ. + // iExceptionCell 1 Ѵ. + ++iExceptionCell; + + if (Cell.IsBeltInventoryPosition()) + { + LPITEM beltItem = GetWear(WEAR_BELT); + + if (NULL == beltItem) + return false; + + if (false == CBeltInventoryHelper::IsAvailableCell(bCell - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0))) + return false; + + if (m_pointsInstant.bItemGrid[bCell]) + { + if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) + return true; + + return false; + } + + if (bSize == 1) + return true; + + } + else if (bCell >= INVENTORY_MAX_NUM) + return false; + + if (m_pointsInstant.bItemGrid[bCell]) + { + if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) + { + if (bSize == 1) + return true; + + int j = 1; + BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2); + + do + { + BYTE p = bCell + (5 * j); + + if (p >= INVENTORY_MAX_NUM) + return false; + + if (p / (INVENTORY_MAX_NUM / 2) != bPage) + return false; + + if (m_pointsInstant.bItemGrid[p]) + if (m_pointsInstant.bItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + else + return false; + } + + // ũⰡ 1̸ ĭ ϴ ̹Ƿ ׳ + if (1 == bSize) + return true; + else + { + int j = 1; + BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2); + + do + { + BYTE p = bCell + (5 * j); + + if (p >= INVENTORY_MAX_NUM) + return false; + + if (p / (INVENTORY_MAX_NUM / 2) != bPage) + return false; + + if (m_pointsInstant.bItemGrid[p]) + if (m_pointsInstant.bItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + } + break; + case DRAGON_SOUL_INVENTORY: + { + WORD wCell = Cell.cell; + if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + // bItemCell 0 false Ÿ + 1 ؼ óѴ. + // iExceptionCell 1 Ѵ. + iExceptionCell++; + + if (m_pointsInstant.wDSItemGrid[wCell]) + { + if (m_pointsInstant.wDSItemGrid[wCell] == iExceptionCell) + { + if (bSize == 1) + return true; + + int j = 1; + + do + { + BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + if (m_pointsInstant.wDSItemGrid[p]) + if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + else + return false; + } + + // ũⰡ 1̸ ĭ ϴ ̹Ƿ ׳ + if (1 == bSize) + return true; + else + { + int j = 1; + + do + { + BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); + + if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) + return false; + + if (m_pointsInstant.bItemGrid[p]) + if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) + return false; + } + while (++j < bSize); + + return true; + } + } + } +} + +int CHARACTER::GetEmptyInventory(BYTE size) const +{ + // NOTE: Լ , ȹ κ丮 ĭ ã ǰ ִµ, + // Ʈ κ丮 Ư κ丮̹Ƿ ˻ ʵ Ѵ. (⺻ κ丮: INVENTORY_MAX_NUM ˻) + for ( int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (IsEmptyItemGrid(TItemPos (INVENTORY, i), size)) + return i; + return -1; +} + +int CHARACTER::GetEmptyDragonSoulInventory(LPITEM pItem) const +{ + if (NULL == pItem || !pItem->IsDragonSoul()) + return -1; + if (!DragonSoul_IsQualified()) + { + return -1; + } + BYTE bSize = pItem->GetSize(); + WORD wBaseCell = DSManager::instance().GetBasePosition(pItem); + + if (WORD_MAX == wBaseCell) + return -1; + + for (int i = 0; i < DRAGON_SOUL_BOX_SIZE; ++i) + if (IsEmptyItemGrid(TItemPos(DRAGON_SOUL_INVENTORY, i + wBaseCell), bSize)) + return i + wBaseCell; + + return -1; +} + +void CHARACTER::CopyDragonSoulItemGrid(std::vector& vDragonSoulItemGrid) const +{ + vDragonSoulItemGrid.resize(DRAGON_SOUL_INVENTORY_MAX_NUM); + + std::copy(m_pointsInstant.wDSItemGrid, m_pointsInstant.wDSItemGrid + DRAGON_SOUL_INVENTORY_MAX_NUM, vDragonSoulItemGrid.begin()); +} + +int CHARACTER::CountEmptyInventory() const +{ + int count = 0; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (GetInventoryItem(i)) + count += GetInventoryItem(i)->GetSize(); + + return (INVENTORY_MAX_NUM - count); +} + +void TransformRefineItem(LPITEM pkOldItem, LPITEM pkNewItem) +{ + // ACCESSORY_REFINE + if (pkOldItem->IsAccessoryForSocket()) + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + pkNewItem->SetSocket(i, pkOldItem->GetSocket(i)); + } + //pkNewItem->StartAccessorySocketExpireEvent(); + } + // END_OF_ACCESSORY_REFINE + else + { + // ⼭ ڵ û + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (!pkOldItem->GetSocket(i)) + break; + else + pkNewItem->SetSocket(i, 1); + } + + // + int slot = 0; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + long socket = pkOldItem->GetSocket(i); + + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + pkNewItem->SetSocket(slot++, socket); + } + + } + + // + pkOldItem->CopyAttributeTo(pkNewItem); +} + +void NotifyRefineSuccess(LPCHARACTER ch, LPITEM item, const char* way) +{ + if (NULL != ch && item != NULL) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineSuceeded"); + + LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), 1, way); + } +} + +void NotifyRefineFail(LPCHARACTER ch, LPITEM item, const char* way, int success = 0) +{ + if (NULL != ch && NULL != item) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "RefineFailed"); + + LogManager::instance().RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetRefineLevel(), success, way); + } +} + +void CHARACTER::SetRefineNPC(LPCHARACTER ch) +{ + if ( ch != NULL ) + { + m_dwRefineNPCVID = ch->GetVID(); + } + else + { + m_dwRefineNPCVID = 0; + } +} + +bool CHARACTER::DoRefine(LPITEM item, bool bMoneyOnly) +{ + if (!CanHandleItem(true)) + { + ClearRefineMode(); + return false; + } + + // ð : upgrade_refine_scroll.quest 5̳ Ϲ + //Ҽ + if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0) + { + if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5)) + { + sys_log(0, "can't refine %d %s", GetPlayerID(), GetName()); + return false; + } + } + + const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + return false; + + DWORD result_vnum = item->GetRefinedVnum(); + + // REFINE_COST + int cost = ComputeRefineFee(prt->cost); + + int RefineChance = GetQuestFlag("main_quest_lv7.refine_chance"); + + if (RefineChance > 0) + { + if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȸ 20 ⸸ մϴ")); + return false; + } + + cost = 0; + SetQuestFlag("main_quest_lv7.refine_chance", RefineChance - 1); + } + // END_OF_REFINE_COST + + if (result_vnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING) + return false; + + TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum()); + + if (!pProto) + { + sys_err("DoRefine NOT GET ITEM PROTO %d", item->GetRefinedVnum()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + // Check level limit in korea only + if (!g_iUseLocale) + { + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limit = pProto->aLimits[i].lValue; + + switch (pProto->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return false; + } + break; + } + } + } + + // REFINE_COST + if (GetGold() < cost) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + return false; + } + + if (!bMoneyOnly && !RefineChance) + { + for (int i = 0; i < prt->material_count; ++i) + { + if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count) + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count); + } + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ ᰡ մϴ.")); + return false; + } + } + + for (int i = 0; i < prt->material_count; ++i) + RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count); + } + + int prob = number(1, 100); + + if (IsRefineThroughGuild() || bMoneyOnly) + prob -= 10; + + // END_OF_REFINE_COST + + if (prob <= prt->prob) + { + // ! , Ӽ ٸ ȹ + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + // DETAIL_REFINE_LOG + NotifyRefineSuccess(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)"); + // END_OF_DETAIL_REFINE_LOG + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + + sys_log(0, "Refine Success %d", cost); + pkNewItem->AttrLog(); + //PointChange(POINT_GOLD, -cost); + sys_log(0, "PayPee %d", cost); + PayRefineFee(cost); + sys_log(0, "PayPee End %d", cost); + } + else + { + // DETAIL_REFINE_LOG + // -> з + sys_err("cannot create item %u", result_vnum); + NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + // END_OF_DETAIL_REFINE_LOG + } + } + else + { + // ! . + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -cost); + NotifyRefineFail(this, item, IsRefineThroughGuild() ? "GUILD" : "POWER"); + item->AttrLog(); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)"); + + //PointChange(POINT_GOLD, -cost); + PayRefineFee(cost); + } + + return true; +} + +enum enum_RefineScrolls +{ + CHUKBOK_SCROLL = 0, + HYUNIRON_CHN = 1, // ߱ + YONGSIN_SCROLL = 2, + MUSIN_SCROLL = 3, + YAGONG_SCROLL = 4, + MEMO_SCROLL = 5, + BDRAGON_SCROLL = 6, +}; + +bool CHARACTER::DoRefineWithScroll(LPITEM item) +{ + if (!CanHandleItem(true)) + { + ClearRefineMode(); + return false; + } + + ClearRefineMode(); + + // ð : upgrade_refine_scroll.quest 5̳ Ϲ + //Ҽ + if (quest::CQuestManager::instance().GetEventFlag("update_refine_time") != 0) + { + if (get_global_time() < quest::CQuestManager::instance().GetEventFlag("update_refine_time") + (60 * 5)) + { + sys_log(0, "can't refine %d %s", GetPlayerID(), GetName()); + return false; + } + } + + const TRefineTable * prt = CRefineManager::instance().GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + return false; + + LPITEM pkItemScroll; + + // üũ + if (m_iRefineAdditionalCell < 0) + return false; + + pkItemScroll = GetInventoryItem(m_iRefineAdditionalCell); + + if (!pkItemScroll) + return false; + + if (!(pkItemScroll->GetType() == ITEM_USE && pkItemScroll->GetSubType() == USE_TUNING)) + return false; + + if (pkItemScroll->GetVnum() == item->GetVnum()) + return false; + + DWORD result_vnum = item->GetRefinedVnum(); + DWORD result_fail_vnum = item->GetRefineFromVnum(); + + if (result_vnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + // MUSIN_SCROLL + if (pkItemScroll->GetValue(0) == MUSIN_SCROLL) + { + if (item->GetRefineLevel() >= 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + } + // END_OF_MUSIC_SCROLL + + else if (pkItemScroll->GetValue(0) == MEMO_SCROLL) + { + if (item->GetRefineLevel() != pkItemScroll->GetValue(1)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + } + else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL) + { + if (item->GetType() != ITEM_METIN || item->GetRefineLevel() != 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + } + + TItemTable * pProto = ITEM_MANAGER::instance().GetTable(item->GetRefinedVnum()); + + if (!pProto) + { + sys_err("DoRefineWithScroll NOT GET ITEM PROTO %d", item->GetRefinedVnum()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + // Check level limit in korea only + if (!g_iUseLocale) + { + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limit = pProto->aLimits[i].lValue; + + switch (pProto->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return false; + } + break; + } + } + } + + if (GetGold() < prt->cost) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ մϴ.")); + return false; + } + + for (int i = 0; i < prt->material_count; ++i) + { + if (CountSpecifyItem(prt->materials[i].vnum) < prt->materials[i].count) + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "Find %d, count %d, require %d", prt->materials[i].vnum, CountSpecifyItem(prt->materials[i].vnum), prt->materials[i].count); + } + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϱ ᰡ մϴ.")); + return false; + } + } + + for (int i = 0; i < prt->material_count; ++i) + RemoveSpecifyItem(prt->materials[i].vnum, prt->materials[i].count); + + int prob = number(1, 100); + int success_prob = prt->prob; + bool bDestroyWhenFail = false; + + const char* szRefineType = "SCROLL"; + + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN || + pkItemScroll->GetValue(0) == YONGSIN_SCROLL || + pkItemScroll->GetValue(0) == YAGONG_SCROLL) // ö, ູ, ߰ ó + { + const char hyuniron_prob[9] = { 100, 75, 65, 55, 45, 40, 35, 25, 20 }; + const char hyuniron_prob_euckr[9] = { 100, 75, 65, 55, 45, 40, 35, 30, 25 }; + + const char yagong_prob[9] = { 100, 100, 90, 80, 70, 60, 50, 30, 20 }; + const char yagong_prob_euckr[9] = { 100, 100, 90, 80, 70, 60, 50, 40, 30 }; + + if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL) + { + if (LC_IsYMIR() == true || LC_IsKorea() == true) + success_prob = hyuniron_prob_euckr[MINMAX(0, item->GetRefineLevel(), 8)]; + else + success_prob = hyuniron_prob[MINMAX(0, item->GetRefineLevel(), 8)]; + } + else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL) + { + if (LC_IsYMIR() == true || LC_IsKorea() == true) + success_prob = yagong_prob_euckr[MINMAX(0, item->GetRefineLevel(), 8)]; + else + success_prob = yagong_prob[MINMAX(0, item->GetRefineLevel(), 8)]; + } + else + { + sys_err("REFINE : Unknown refine scroll item. Value0: %d", pkItemScroll->GetValue(0)); + } + + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[Only Test] Success_Prob %d, RefineLevel %d ", success_prob, item->GetRefineLevel()); + } + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) // ö μ Ѵ. + bDestroyWhenFail = true; + + // DETAIL_REFINE_LOG + if (pkItemScroll->GetValue(0) == HYUNIRON_CHN) + { + szRefineType = "HYUNIRON"; + } + else if (pkItemScroll->GetValue(0) == YONGSIN_SCROLL) + { + szRefineType = "GOD_SCROLL"; + } + else if (pkItemScroll->GetValue(0) == YAGONG_SCROLL) + { + szRefineType = "YAGONG_SCROLL"; + } + // END_OF_DETAIL_REFINE_LOG + } + + // DETAIL_REFINE_LOG + if (pkItemScroll->GetValue(0) == MUSIN_SCROLL) // ູ 100% (+4) + { + success_prob = 100; + + szRefineType = "MUSIN_SCROLL"; + } + // END_OF_DETAIL_REFINE_LOG + else if (pkItemScroll->GetValue(0) == MEMO_SCROLL) + { + success_prob = 100; + szRefineType = "MEMO_SCROLL"; + } + else if (pkItemScroll->GetValue(0) == BDRAGON_SCROLL) + { + success_prob = 80; + szRefineType = "BDRAGON_SCROLL"; + } + + pkItemScroll->SetCount(pkItemScroll->GetCount() - 1); + + if (prob <= success_prob) + { + // ! , Ӽ ٸ ȹ + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE SUCCESS", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + NotifyRefineSuccess(this, item, szRefineType); + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE SUCCESS)"); + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + pkNewItem->AttrLog(); + //PointChange(POINT_GOLD, -prt->cost); + PayRefineFee(prt->cost); + } + else + { + // -> з + sys_err("cannot create item %u", result_vnum); + NotifyRefineFail(this, item, szRefineType); + } + } + else if (!bDestroyWhenFail && result_fail_vnum) + { + // ! , Ӽ ȹ + LPITEM pkNewItem = ITEM_MANAGER::instance().CreateItem(result_fail_vnum, 1, 0, false); + + if (pkNewItem) + { + ITEM_MANAGER::CopyAllAttrTo(item, pkNewItem); + LogManager::instance().ItemLog(this, pkNewItem, "REFINE FAIL", pkNewItem->GetName()); + + BYTE bCell = item->GetCell(); + + DBManager::instance().SendMoneyLog(MONEY_LOG_REFINE, item->GetVnum(), -prt->cost); + NotifyRefineFail(this, item, szRefineType, -1); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (REFINE FAIL)"); + + pkNewItem->AddToCharacter(this, TItemPos(INVENTORY, bCell)); + ITEM_MANAGER::instance().FlushDelayedSave(pkNewItem); + + pkNewItem->AttrLog(); + + //PointChange(POINT_GOLD, -prt->cost); + PayRefineFee(prt->cost); + } + else + { + // -> з + sys_err("cannot create item %u", result_fail_vnum); + NotifyRefineFail(this, item, szRefineType); + } + } + else + { + NotifyRefineFail(this, item, szRefineType); // + + PayRefineFee(prt->cost); + } + + return true; +} + +bool CHARACTER::RefineInformation(BYTE bCell, BYTE bType, int iAdditionalCell) +{ + if (bCell > INVENTORY_MAX_NUM) + return false; + + LPITEM item = GetInventoryItem(bCell); + + if (!item) + return false; + + // REFINE_COST + if (bType == REFINE_TYPE_MONEY_ONLY && !GetQuestFlag("deviltower_zone.can_refine")) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ÿ Ϸ ѹ 밡մϴ.")); + return false; + } + // END_OF_REFINE_COST + + TPacketGCRefineInformation p; + + p.header = HEADER_GC_REFINE_INFORMATION; + p.pos = bCell; + p.src_vnum = item->GetVnum(); + p.result_vnum = item->GetRefinedVnum(); + p.type = bType; + + if (p.result_vnum == 0) + { + sys_err("RefineInformation p.result_vnum == 0"); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_TUNING) + { + if (bType == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" δ ϴ.")); + return false; + } + else + { + LPITEM itemScroll = GetInventoryItem(iAdditionalCell); + if (!itemScroll || item->GetVnum() == itemScroll->GetVnum()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ĥ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ູ ö ĥ ֽϴ.")); + return false; + } + } + } + + CRefineManager & rm = CRefineManager::instance(); + + const TRefineTable* prt = rm.GetRefineRecipe(item->GetRefineSet()); + + if (!prt) + { + sys_err("RefineInformation NOT GET REFINE SET %d", item->GetRefineSet()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + // REFINE_COST + + //MAIN_QUEST_LV7 + if (GetQuestFlag("main_quest_lv7.refine_chance") > 0) + { + // Ϻ + if (!item->CheckItemUseLevel(20) || item->GetType() != ITEM_WEAPON) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȸ 20 ⸸ մϴ")); + return false; + } + p.cost = 0; + } + else + p.cost = ComputeRefineFee(prt->cost); + + //END_MAIN_QUEST_LV7 + p.prob = prt->prob; + if (bType == REFINE_TYPE_MONEY_ONLY) + { + p.material_count = 0; + memset(p.materials, 0, sizeof(p.materials)); + } + else + { + p.material_count = prt->material_count; + thecore_memcpy(&p.materials, prt->materials, sizeof(prt->materials)); + } + // END_OF_REFINE_COST + + GetDesc()->Packet(&p, sizeof(TPacketGCRefineInformation)); + + SetRefineMode(iAdditionalCell); + return true; +} + +bool CHARACTER::RefineItem(LPITEM pkItem, LPITEM pkTarget) +{ + if (!CanHandleItem()) + return false; + + if (pkItem->GetSubType() == USE_TUNING) + { + // XXX , ϴ... + // XXX ɰ ູ Ǿ! + // MUSIN_SCROLL + if (pkItem->GetValue(0) == MUSIN_SCROLL) + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_MUSIN, pkItem->GetCell()); + // END_OF_MUSIN_SCROLL + else if (pkItem->GetValue(0) == HYUNIRON_CHN) + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_HYUNIRON, pkItem->GetCell()); + else if (pkItem->GetValue(0) == BDRAGON_SCROLL) + { + if (pkTarget->GetRefineSet() != 702) return false; + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_BDRAGON, pkItem->GetCell()); + } + else + { + if (pkTarget->GetRefineSet() == 501) return false; + RefineInformation(pkTarget->GetCell(), REFINE_TYPE_SCROLL, pkItem->GetCell()); + } + } + else if (pkItem->GetSubType() == USE_DETACHMENT && IS_SET(pkTarget->GetFlag(), ITEM_FLAG_REFINEABLE)) + { + LogManager::instance().ItemLog(this, pkTarget, "USE_DETACHMENT", pkTarget->GetName()); + + bool bHasMetinStone = false; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++) + { + long socket = pkTarget->GetSocket(i); + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + { + bHasMetinStone = true; + break; + } + } + + if (bHasMetinStone) + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + long socket = pkTarget->GetSocket(i); + if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM) + { + AutoGiveItem(socket); + //TItemTable* pTable = ITEM_MANAGER::instance().GetTable(pkTarget->GetSocket(i)); + //pkTarget->SetSocket(i, pTable->alValues[2]); + // üش + pkTarget->SetSocket(i, ITEM_BROKEN_METIN_VNUM); + } + } + pkItem->SetCount(pkItem->GetCount() - 1); + return true; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ƾ ϴ.")); + return false; + } + } + + return false; +} + +EVENTFUNC(kill_campfire_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "kill_campfire_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + ch->m_pkMiningEvent = NULL; + M2_DESTROY_CHARACTER(ch); + return 0; +} + +bool CHARACTER::GiveRecallItem(LPITEM item) +{ + int idx = GetMapIndex(); + int iEmpireByMapIndex = -1; + + if (idx < 20) + iEmpireByMapIndex = 1; + else if (idx < 40) + iEmpireByMapIndex = 2; + else if (idx < 60) + iEmpireByMapIndex = 3; + else if (idx < 10000) + iEmpireByMapIndex = 0; + + switch (idx) + { + case 66: + case 216: + iEmpireByMapIndex = -1; + break; + } + + if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ Դϴ.")); + return false; + } + + int pos; + + if (item->GetCount() == 1) // ϳ ׳ . + { + item->SetSocket(0, GetX()); + item->SetSocket(1, GetY()); + } + else if ((pos = GetEmptyInventory(item->GetSize())) != -1) // ׷ ʴٸ ٸ κ丮 ã´. + { + LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), 1); + + if (NULL != item2) + { + item2->SetSocket(0, GetX()); + item2->SetSocket(1, GetY()); + item2->AddToCharacter(this, TItemPos(INVENTORY, pos)); + + item->SetCount(item->GetCount() - 1); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + return false; + } + + return true; +} + +void CHARACTER::ProcessRecallItem(LPITEM item) +{ + int idx; + + if ((idx = SECTREE_MANAGER::instance().GetMapIndex(item->GetSocket(0), item->GetSocket(1))) == 0) + return; + + int iEmpireByMapIndex = -1; + + if (idx < 20) + iEmpireByMapIndex = 1; + else if (idx < 40) + iEmpireByMapIndex = 2; + else if (idx < 60) + iEmpireByMapIndex = 3; + else if (idx < 10000) + iEmpireByMapIndex = 0; + + switch (idx) + { + case 66: + case 216: + iEmpireByMapIndex = -1; + break; + // Ƿ決 ϶ + case 301: + case 302: + case 303: + case 304: + if( GetLevel() < 90 ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return; + } + else + break; + } + + if (iEmpireByMapIndex && GetEmpire() != iEmpireByMapIndex) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ Ÿ ־ ȯ ϴ.")); + item->SetSocket(0, 0); + item->SetSocket(1, 0); + } + else + { + sys_log(1, "Recall: %s %d %d -> %d %d", GetName(), GetX(), GetY(), item->GetSocket(0), item->GetSocket(1)); + WarpSet(item->GetSocket(0), item->GetSocket(1)); + item->SetCount(item->GetCount() - 1); + } +} + +void CHARACTER::__OpenPrivateShop() +{ + unsigned bodyPart = GetPart(PART_MAIN); + switch (bodyPart) + { + case 0: + case 1: + case 2: + ChatPacket(CHAT_TYPE_COMMAND, "OpenPrivateShop"); + break; + default: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֽϴ.")); + break; + } +} + +// MYSHOP_PRICE_LIST +void CHARACTER::SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice) +{ + char szLine[256]; + snprintf(szLine, sizeof(szLine), "MyShopPriceList %u %u", dwItemVnum, dwItemPrice); + ChatPacket(CHAT_TYPE_COMMAND, szLine); + sys_log(0, szLine); +} + +// +// DB ij÷ Ʈ User ϰ Ŀǵ带 . +// +void CHARACTER::UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p) +{ + const TItemPriceInfo* pInfo = (const TItemPriceInfo*)(p + 1); + + if (!p->byCount) + // Ʈ . dummy ͸ Ŀǵ带 ش. + SendMyShopPriceListCmd(1, 0); + else { + for (int idx = 0; idx < p->byCount; idx++) + SendMyShopPriceListCmd(pInfo[ idx ].dwVnum, pInfo[ idx ].dwPrice); + } + + __OpenPrivateShop(); +} + +// +// ̹ ó Open ϴ Ʈ Load ϱ DB ijÿ Ʈ û Ŷ . +// ĺʹ ٷ . +// +void CHARACTER::UseSilkBotary(void) +{ + if (m_bNoOpenedShop) { + DWORD dwPlayerID = GetPlayerID(); + db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_REQ, GetDesc()->GetHandle(), &dwPlayerID, sizeof(DWORD)); + m_bNoOpenedShop = false; + } else { + __OpenPrivateShop(); + } +} +// END_OF_MYSHOP_PRICE_LIST + + +int CalculateConsume(LPCHARACTER ch) +{ + static const int WARP_NEED_LIFE_PERCENT = 30; + static const int WARP_MIN_LIFE_PERCENT = 10; + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + int consumeLife = 0; + { + // CheckNeedLifeForWarp + const int curLife = ch->GetHP(); + const int needPercent = WARP_NEED_LIFE_PERCENT; + const int needLife = ch->GetMaxHP() * needPercent / 100; + if (curLife < needLife) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ڶ ϴ.")); + return -1; + } + + consumeLife = needLife; + + + // CheckMinLifeForWarp: ؼ ȵǹǷ ּҷ ش + const int minPercent = WARP_MIN_LIFE_PERCENT; + const int minLife = ch->GetMaxHP() * minPercent / 100; + if (curLife - needLife < minLife) + consumeLife = curLife - minLife; + + if (consumeLife < 0) + consumeLife = 0; + } + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + return consumeLife; +} + +int CalculateConsumeSP(LPCHARACTER lpChar) +{ + static const int NEED_WARP_SP_PERCENT = 30; + + const int curSP = lpChar->GetSP(); + const int needSP = lpChar->GetMaxSP() * NEED_WARP_SP_PERCENT / 100; + + if (curSP < needSP) + { + lpChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ŷ ڶ ϴ.")); + return -1; + } + + return needSP; +} + +bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell) +{ + int iLimitRealtimeStartFirstUseFlagIndex = -1; + int iLimitTimerBasedOnWearFlagIndex = -1; + + WORD wDestCell = DestCell.cell; + BYTE bDestInven = DestCell.window_type; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limitValue = item->GetProto()->aLimits[i].lValue; + + switch (item->GetProto()->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limitValue) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ѻ ϴ.")); + return false; + } + break; + + case LIMIT_REAL_TIME_START_FIRST_USE: + iLimitRealtimeStartFirstUseFlagIndex = i; + break; + + case LIMIT_TIMER_BASED_ON_WEAR: + iLimitTimerBasedOnWearFlagIndex = i; + break; + } + } + + if (test_server) + { + sys_log(0, "USE_ITEM %s, Inven %d, Cell %d, ItemType %d, SubType %d", item->GetName(), bDestInven, wDestCell, item->GetType(), item->GetSubType()); + } + + if ( CArenaManager::instance().IsLimitedItem( GetMapIndex(), item->GetVnum() ) == true ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + + // ĺʹ ʾƵ ð Ǵ ó. + if (-1 != iLimitRealtimeStartFirstUseFlagIndex) + { + // ̶ δ Socket1 ǴѴ. (Socket1 Ƚ ) + if (0 == item->GetSocket(1)) + { + // 밡ɽð Default Limit Value ϵ, Socket0 ϵ Ѵ. ( ) + long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[iLimitRealtimeStartFirstUseFlagIndex].lValue; + + if (0 == duration) + duration = 60 * 60 * 24 * 7; + + item->SetSocket(0, time(0) + duration); + item->StartRealTimeExpireEvent(); + } + + if (false == item->IsEquipped()) + item->SetSocket(1, item->GetSocket(1) + 1); + } + + switch (item->GetType()) + { + case ITEM_HAIR: + return ItemProcess_Hair(item, wDestCell); + + case ITEM_POLYMORPH: + return ItemProcess_Polymorph(item); + + case ITEM_QUEST: + if (GetArena() != NULL || IsObserverMode() == true) + { + if (item->GetVnum() == 50051 || item->GetVnum() == 50052 || item->GetVnum() == 50053) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + } + + if (!IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_USE | ITEM_FLAG_QUEST_USE_MULTIPLE)) + { + if (item->GetSIGVnum() == 0) + { + quest::CQuestManager::instance().UseItem(GetPlayerID(), item, false); + } + else + { + quest::CQuestManager::instance().SIGUse(GetPlayerID(), item->GetSIGVnum(), item, false); + } + } + break; + + case ITEM_CAMPFIRE: + { + float fx, fy; + GetDeltaByDegree(GetRotation(), 100.0f, &fx, &fy); + + LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy)); + + if (!tree) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ں ǿ Դϴ.")); + return false; + } + + if (tree->IsAttr((long)(GetX()+fx), (long)(GetY()+fy), ATTR_WATER)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ӿ ں ǿ ϴ.")); + return false; + } + + LPCHARACTER campfire = CHARACTER_MANAGER::instance().SpawnMob(fishing::CAMPFIRE_MOB, GetMapIndex(), (long)(GetX()+fx), (long)(GetY()+fy), 0, false, number(0, 359)); + + char_event_info* info = AllocEventInfo(); + + info->ch = campfire; + + campfire->m_pkMiningEvent = event_create(kill_campfire_event, info, PASSES_PER_SEC(40)); + + item->SetCount(item->GetCount() - 1); + } + break; + + case ITEM_UNIQUE: + { + switch (item->GetSubType()) + { + case USE_ABILITY_UP: + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true, true); + break; + + case APPLY_ATT_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true, true); + break; + + case APPLY_STR: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_DEX: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_CON: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_INT: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_CAST_SPEED: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_RESIST_MAGIC: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_RESIST_MAGIC, item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_ATT_GRADE_BONUS: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_ATT_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + + case APPLY_DEF_GRADE_BONUS: + AddAffect(AFFECT_UNIQUE_ABILITY, POINT_DEF_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + default: + { + if (item->GetSubType() == USE_SPECIAL) + { + sys_log(0, "ITEM_UNIQUE: USE_SPECIAL %u", item->GetVnum()); + + switch (item->GetVnum()) + { + case 71049: // ܺ + if (LC_IsYMIR() == true || LC_IsKorea() == true) + { + if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true) + { + UseSilkBotary(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ")); + } + } + else + { + UseSilkBotary(); + } + break; + } + } + else + { + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + } + } + break; + } + } + break; + + case ITEM_COSTUME: + case ITEM_WEAPON: + case ITEM_ARMOR: + case ITEM_ROD: + case ITEM_RING: // ű + case ITEM_BELT: // ű Ʈ + // MINING + case ITEM_PICK: + // END_OF_MINING + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + break; + // ȥ . + // Ŭ, ȥ Ͽ item use Ŷ . + // ȥ item move Ŷ Ѵ. + // ȥ Ѵ. + case ITEM_DS: + { + if (!item->IsEquipped()) + return false; + return DSManager::instance().PullOut(this, NPOS, item); + break; + } + case ITEM_SPECIAL_DS: + if (!item->IsEquipped()) + EquipItem(item); + else + UnequipItem(item); + break; + + case ITEM_FISH: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + + if (item->GetSubType() == FISH_ALIVE) + fishing::UseFish(this, item); + } + break; + + case ITEM_TREASURE_BOX: + { + return false; + //ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ־ ʴ° . 踦 غ.")); + } + break; + + case ITEM_TREASURE_KEY: + { + LPITEM item2; + + if (!GetItem(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging()) + return false; + + if (item2->GetType() != ITEM_TREASURE_BOX) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ƴѰ .")); + return false; + } + + if (item->GetValue(0) == item2->GetValue(0)) + { + //ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ִ κ ȵǾϴ.")); + DWORD dwBoxVnum = item2->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(NULL); + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + ITEM_MANAGER::instance().RemoveItem(item); + ITEM_MANAGER::instance().RemoveItem(item2); + + for (int i = 0; i < count; i++){ + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + + } + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("谡 ʴ .")); + return false; + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("谡 ʴ .")); + return false; + } + } + break; + + case ITEM_GIFTBOX: + { + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(NULL); + int count = 0; + + if (dwBoxVnum == 50033 && LC_IsYMIR()) // ˼ + { + if (GetLevel() < 15) + { + ChatPacket(CHAT_TYPE_INFO, "15 Ͽ ϴ."); + return false; + } + } + + if( (dwBoxVnum > 51500 && dwBoxVnum < 52000) || (dwBoxVnum >= 50255 && dwBoxVnum <= 50260) ) // ȥ + { + if( !(this->DragonSoul_IsQualified()) ) + { + ChatPacket(CHAT_TYPE_INFO,LC_TEXT(" ȥ Ʈ Ϸϼž մϴ.")); + return false; + } + } + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + item->SetCount(item->GetCount()-1); + + for (int i = 0; i < count; i++){ + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + } + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ƹ͵ ϴ.")); + return false; + } + } + break; + + case ITEM_SKILLFORGET: + { + if (!item->GetSocket(0)) + { + ITEM_MANAGER::instance().RemoveItem(item); + return false; + } + + DWORD dwVnum = item->GetSocket(0); + + if (SkillLevelDown(dwVnum)) + { + ITEM_MANAGER::instance().RemoveItem(item); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ų µ Ͽϴ.")); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ų ϴ.")); + } + break; + + case ITEM_SKILLBOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + } + + DWORD dwVnum = 0; + + if (item->GetVnum() == 50300) + { + dwVnum = item->GetSocket(0); + } + else + { + // ο ü value 0 ų ȣ Ƿ װ . + dwVnum = item->GetValue(0); + } + + if (0 == dwVnum) + { + ITEM_MANAGER::instance().RemoveItem(item); + + return false; + } + + if (true == LearnSkillByBook(dwVnum)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + + if (distribution_test_server) + iReadDelay /= 3; + + //ѱ 쿡 ð 24ð + if (LC_IsKorea()) + iReadDelay = 86400; + + SetSkillNextReadTime(dwVnum, get_global_time() + iReadDelay); + } + } + break; + + case ITEM_USE: + { + if (item->GetVnum() > 50800 && item->GetVnum() <= 50820) + { + if (test_server) + sys_log (0, "ADD addtional effect : vnum(%d) subtype(%d)", item->GetOriginalVnum(), item->GetSubType()); + + int affect_type = AFFECT_EXP_BONUS_EURO_FREE; + int apply_type = aApplyInfo[item->GetValue(0)].bPointType; + int apply_value = item->GetValue(2); + int apply_duration = item->GetValue(1); + + switch (item->GetSubType()) + { + case USE_ABILITY_UP: + if (FindAffect(affect_type, apply_type)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + return false; + } + + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(affect_type, apply_type, apply_value, AFF_MOV_SPEED_POTION, apply_duration, 0, true, true); + break; + + case APPLY_ATT_SPEED: + AddAffect(affect_type, apply_type, apply_value, AFF_ATT_SPEED_POTION, apply_duration, 0, true, true); + break; + + case APPLY_STR: + case APPLY_DEX: + case APPLY_CON: + case APPLY_INT: + case APPLY_CAST_SPEED: + case APPLY_RESIST_MAGIC: + case APPLY_ATT_GRADE_BONUS: + case APPLY_DEF_GRADE_BONUS: + AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, true, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_AFFECT : + { + if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + // PC_BANG_ITEM_ADD + if (item->IsPCBangItem() == true) + { + // PC üũؼ ó + if (CPCBangManager::instance().IsPCBangIP(GetDesc()->GetHostName()) == false) + { + // PC ƴ! + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" PC濡 ֽϴ.")); + return false; + } + } + // END_PC_BANG_ITEM_ADD + + AddAffect(AFFECT_EXP_BONUS_EURO_FREE, aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false, true); + item->SetCount(item->GetCount() - 1); + } + } + break; + + case USE_POTION_NODELAY: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 70020 : + case 71018 : + case 71019 : + case 71020 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + break; + } + } + + bool used = false; + + if (item->GetValue(0) != 0) // HP 밪 ȸ + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(1) != 0) // SP 밪 ȸ + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (item->GetValue(3) != 0) // HP % ȸ + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(4) != 0) // SP % ȸ + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (used) + { + if (item->GetVnum() == 50085 || item->GetVnum() == 50086) + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǵ Ͽϴ")); + SetUseSeedOrMoonBottleTime(); + } + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + m_nPotionLimit--; + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + item->SetCount(item->GetCount() - 1); + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + } + } + break; + } + + return true; + } + + + if (item->GetVnum() >= 27863 && item->GetVnum() <= 27883) + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + } + + if (test_server) + { + sys_log (0, "USE_ITEM %s Type %d SubType %d vnum %d", item->GetName(), item->GetType(), item->GetSubType(), item->GetOriginalVnum()); + } + + switch (item->GetSubType()) + { + case USE_TIME_CHARGE_PER: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + // 켱 ȥ ؼ ϵ Ѵ. + if (pDestItem->IsDragonSoul()) + { + int ret; + char buf[128]; + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + ret = pDestItem->GiveMoreTime_Per((float)item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + ret = pDestItem->GiveMoreTime_Per((float)item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + if (ret > 0) + { + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + sprintf(buf, "Inc %ds by item{VN:%d SOC%d:%d}", ret, item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + sprintf(buf, "Inc %ds by item{VN:%d VAL%d:%d}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ŭ Ǿϴ."), ret); + item->SetCount(item->GetCount() - 1); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf); + return true; + } + else + { + if (item->GetVnum() == DRAGON_HEART_VNUM) + { + sprintf(buf, "No change by item{VN:%d SOC%d:%d}", item->GetVnum(), ITEM_SOCKET_CHARGING_AMOUNT_IDX, item->GetSocket(ITEM_SOCKET_CHARGING_AMOUNT_IDX)); + } + else + { + sprintf(buf, "No change by item{VN:%d VAL%d:%d}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf); + return false; + } + } + else + return false; + } + break; + case USE_TIME_CHARGE_FIX: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + // 켱 ȥ ؼ ϵ Ѵ. + if (pDestItem->IsDragonSoul()) + { + int ret = pDestItem->GiveMoreTime_Fix(item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + char buf[128]; + if (ret) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ŭ Ǿϴ."), ret); + sprintf(buf, "Increase %ds by item{VN:%d VAL%d:%d}", ret, item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_SUCCESS", buf); + item->SetCount(item->GetCount() - 1); + return true; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + sprintf(buf, "No change by item{VN:%d VAL%d:%d}", item->GetVnum(), ITEM_VALUE_CHARGING_AMOUNT_IDX, item->GetValue(ITEM_VALUE_CHARGING_AMOUNT_IDX)); + LogManager::instance().ItemLog(this, item, "DS_CHARGING_FAILED", buf); + return false; + } + } + else + return false; + } + break; + case USE_SPECIAL: + + switch (item->GetVnum()) + { + //ũ + case ITEM_NOG_POCKET: + { + /* + ִɷġ : item_proto value ǹ + ̵ӵ value 1 + ݷ value 2 + ġ value 3 + ӽð value 0 ( ) + + */ + if (FindAffect(AFFECT_NOG_ABILITY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + return false; + } + long time = item->GetValue(0); + long moveSpeedPer = item->GetValue(1); + long attPer = item->GetValue(2); + long expPer = item->GetValue(3); + AddAffect(AFFECT_NOG_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true); + AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true); + AddAffect(AFFECT_NOG_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true); + item->SetCount(item->GetCount() - 1); + } + break; + + //󸶴ܿ + case ITEM_RAMADAN_CANDY: + { + /* + ɷġ : item_proto value ǹ + ̵ӵ value 1 + ݷ value 2 + ġ value 3 + ӽð value 0 ( ) + + */ + long time = item->GetValue(0); + long moveSpeedPer = item->GetValue(1); + long attPer = item->GetValue(2); + long expPer = item->GetValue(3); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MOV_SPEED, moveSpeedPer, AFF_MOV_SPEED_POTION, time, 0, true, true); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_ATTBONUS, attPer, AFF_NONE, time, 0, true, true); + AddAffect(AFFECT_RAMADAN_ABILITY, POINT_MALL_EXPBONUS, expPer, AFF_NONE, time, 0, true, true); + item->SetCount(item->GetCount() - 1); + } + break; + case ITEM_MARRIAGE_RING: + { + marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID()); + if (pMarriage) + { + if (pMarriage->ch1 != NULL) + { + if (CArenaManager::instance().IsArenaMap(pMarriage->ch1->GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + break; + } + } + + if (pMarriage->ch2 != NULL) + { + if (CArenaManager::instance().IsArenaMap(pMarriage->ch2->GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + break; + } + } + + int consumeSP = CalculateConsumeSP(this); + + if (consumeSP < 0) + return false; + + PointChange(POINT_SP, -consumeSP, false); + + WarpToPID(pMarriage->GetOther(GetPlayerID())); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ° ƴϸ ȥ ϴ.")); + } + break; + + // + case UNIQUE_ITEM_CAPE_OF_COURAGE: + //󸶴 + case 70057: + case REWARD_BOX_UNIQUE_ITEM_CAPE_OF_COURAGE: + AggregateMonster(); + item->SetCount(item->GetCount()-1); + break; + + case UNIQUE_ITEM_WHITE_FLAG: + ForgetMyAttacker(); + item->SetCount(item->GetCount()-1); + break; + + case UNIQUE_ITEM_TREASURE_BOX: + break; + + case 30093: + case 30094: + case 30095: + case 30096: + // ָӴ + { + const int MAX_BAG_INFO = 26; + static struct LuckyBagInfo + { + DWORD count; + int prob; + DWORD vnum; + } b1[MAX_BAG_INFO] = + { + { 1000, 302, 1 }, + { 10, 150, 27002 }, + { 10, 75, 27003 }, + { 10, 100, 27005 }, + { 10, 50, 27006 }, + { 10, 80, 27001 }, + { 10, 50, 27002 }, + { 10, 80, 27004 }, + { 10, 50, 27005 }, + { 1, 10, 50300 }, + { 1, 6, 92 }, + { 1, 2, 132 }, + { 1, 6, 1052 }, + { 1, 2, 1092 }, + { 1, 6, 2082 }, + { 1, 2, 2122 }, + { 1, 6, 3082 }, + { 1, 2, 3122 }, + { 1, 6, 5052 }, + { 1, 2, 5082 }, + { 1, 6, 7082 }, + { 1, 2, 7122 }, + { 1, 1, 11282 }, + { 1, 1, 11482 }, + { 1, 1, 11682 }, + { 1, 1, 11882 }, + }; + + struct LuckyBagInfo b2[MAX_BAG_INFO] = + { + { 1000, 302, 1 }, + { 10, 150, 27002 }, + { 10, 75, 27002 }, + { 10, 100, 27005 }, + { 10, 50, 27005 }, + { 10, 80, 27001 }, + { 10, 50, 27002 }, + { 10, 80, 27004 }, + { 10, 50, 27005 }, + { 1, 10, 50300 }, + { 1, 6, 92 }, + { 1, 2, 132 }, + { 1, 6, 1052 }, + { 1, 2, 1092 }, + { 1, 6, 2082 }, + { 1, 2, 2122 }, + { 1, 6, 3082 }, + { 1, 2, 3122 }, + { 1, 6, 5052 }, + { 1, 2, 5082 }, + { 1, 6, 7082 }, + { 1, 2, 7122 }, + { 1, 1, 11282 }, + { 1, 1, 11482 }, + { 1, 1, 11682 }, + { 1, 1, 11882 }, + }; + + LuckyBagInfo * bi = NULL; + if (LC_IsHongKong()) + bi = b2; + else + bi = b1; + + int pct = number(1, 1000); + + int i; + for (i=0;i=MAX_BAG_INFO) + return false; + + if (bi[i].vnum == 50300) + { + // ųü Ưϰ ش. + GiveRandomSkillBook(); + } + else if (bi[i].vnum == 1) + { + PointChange(POINT_GOLD, 1000, true); + } + else + { + AutoGiveItem(bi[i].vnum, bi[i].count); + } + ITEM_MANAGER::instance().RemoveItem(item); + } + break; + + case 50004: // ̺Ʈ + { + if (item->GetSocket(0)) + { + item->SetSocket(0, item->GetSocket(0) + 1); + } + else + { + // ó + int iMapIndex = GetMapIndex(); + + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRandomLocation(iMapIndex, pos, 700)) + { + item->SetSocket(0, 1); + item->SetSocket(1, pos.x); + item->SetSocket(2, pos.y); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̺Ʈ Ⱑ ʴ° ϴ.")); + return false; + } + } + + int dist = 0; + float distance = (DISTANCE_SQRT(GetX()-item->GetSocket(1), GetY()-item->GetSocket(2))); + + if (distance < 1000.0f) + { + // ߰! + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̺Ʈ Ⱑ źο ϴ.")); + + // Ƚ ִ ٸ Ѵ. + struct TEventStoneInfo + { + DWORD dwVnum; + int count; + int prob; + }; + const int EVENT_STONE_MAX_INFO = 15; + TEventStoneInfo info_10[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 8 }, + { 27004, 10, 6 }, + { 27002, 10, 12 }, + { 27005, 10, 12 }, + { 27100, 1, 9 }, + { 27103, 1, 9 }, + { 27101, 1, 10 }, + { 27104, 1, 10 }, + { 27999, 1, 12 }, + + { 25040, 1, 4 }, + + { 27410, 1, 0 }, + { 27600, 1, 0 }, + { 25100, 1, 0 }, + + { 50001, 1, 0 }, + { 50003, 1, 1 }, + }; + TEventStoneInfo info_7[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 1 }, + { 27004, 10, 1 }, + { 27004, 10, 9 }, + { 27005, 10, 9 }, + { 27100, 1, 5 }, + { 27103, 1, 5 }, + { 27101, 1, 10 }, + { 27104, 1, 10 }, + { 27999, 1, 14 }, + + { 25040, 1, 5 }, + + { 27410, 1, 5 }, + { 27600, 1, 5 }, + { 25100, 1, 5 }, + + { 50001, 1, 0 }, + { 50003, 1, 5 }, + + }; + TEventStoneInfo info_4[EVENT_STONE_MAX_INFO] = + { + { 27001, 10, 0 }, + { 27004, 10, 0 }, + { 27002, 10, 0 }, + { 27005, 10, 0 }, + { 27100, 1, 0 }, + { 27103, 1, 0 }, + { 27101, 1, 0 }, + { 27104, 1, 0 }, + { 27999, 1, 25 }, + + { 25040, 1, 0 }, + + { 27410, 1, 0 }, + { 27600, 1, 0 }, + { 25100, 1, 15 }, + + { 50001, 1, 10 }, + { 50003, 1, 50 }, + + }; + + { + TEventStoneInfo* info; + if (item->GetSocket(0) <= 4) + info = info_4; + else if (item->GetSocket(0) <= 7) + info = info_7; + else + info = info_10; + + int prob = number(1, 100); + + for (int i = 0; i < EVENT_STONE_MAX_INFO; ++i) + { + if (!info[i].prob) + continue; + + if (prob <= info[i].prob) + { + if (info[i].dwVnum == 50001) + { + DWORD * pdw = M2_NEW DWORD[2]; + + pdw[0] = info[i].dwVnum; + pdw[1] = info[i].count; + + // ÷ Ѵ + DBManager::instance().ReturnQuery(QID_LOTTO, GetPlayerID(), pdw, + "INSERT INTO lotto_list VALUES(0, 'server%s', %u, NOW())", + get_table_postfix(), GetPlayerID()); + } + else + AutoGiveItem(info[i].dwVnum, info[i].count); + + break; + } + prob -= info[i].prob; + } + } + + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "StoneDetect %u 0 0", (DWORD)GetVID()); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; // \0 ڱ + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + pack_chat.bEmpire = GetDesc()->GetEmpire(); + //pack_chat.id = vid; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + PacketAround(buf.read_peek(), buf.size()); + + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 1"); + return true; + } + else if (distance < 20000) + dist = 1; + else if (distance < 70000) + dist = 2; + else + dist = 3; + + // . + const int STONE_DETECT_MAX_TRY = 10; + if (item->GetSocket(0) >= STONE_DETECT_MAX_TRY) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̺Ʈ Ⱑ ϴ.")); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (DETECT_EVENT_STONE) 0"); + AutoGiveItem(27002); + return true; + } + + if (dist) + { + char chatbuf[CHAT_MAX_LEN + 1]; + int len = snprintf(chatbuf, sizeof(chatbuf), + "StoneDetect %u %d %d", + (DWORD)GetVID(), dist, (int)GetDegreeFromPositionXY(GetX(), item->GetSocket(2), item->GetSocket(1), GetY())); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; // \0 ڱ + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + pack_chat.bEmpire = GetDesc()->GetEmpire(); + //pack_chat.id = vid; + + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + PacketAround(buf.read_peek(), buf.size()); + } + + } + break; + + case 27989: // + case 76006: // + { + LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex()); + + if (pMap != NULL) + { + item->SetSocket(0, item->GetSocket(0) + 1); + + FFindStone f; + + // SECTREE::for_each -> SECTREE::for_each_entity + pMap->for_each(f); + + if (f.m_mapStone.size() > 0) + { + std::map::iterator stone = f.m_mapStone.begin(); + + DWORD max = UINT_MAX; + LPCHARACTER pTarget = stone->second; + + while (stone != f.m_mapStone.end()) + { + DWORD dist = (DWORD)DISTANCE_SQRT(GetX()-stone->second->GetX(), GetY()-stone->second->GetY()); + + if (dist != 0 && max > dist) + { + max = dist; + pTarget = stone->second; + } + stone++; + } + + if (pTarget != NULL) + { + int val = 3; + + if (max < 10000) val = 2; + else if (max < 70000) val = 1; + + ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u %d %d", (DWORD)GetVID(), val, + (int)GetDegreeFromPositionXY(GetX(), pTarget->GetY(), pTarget->GetX(), GetY())); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("⸦ ۿϿ Ǵ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("⸦ ۿϿ Ǵ ϴ.")); + } + + if (item->GetSocket(0) >= 6) + { + ChatPacket(CHAT_TYPE_COMMAND, "StoneDetect %u 0 0", (DWORD)GetVID()); + ITEM_MANAGER::instance().RemoveItem(item); + } + } + break; + } + break; + + case 27996: // + item->SetCount(item->GetCount() - 1); + /*if (GetSkillLevel(SKILL_CREATE_POISON)) + AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE, 3, AFF_DRINK_POISON, 15*60, 0, true); + else + { + // ٷⰡ 50% 50% ݷ +2 + if (number(0, 1)) + { + if (GetHP() > 100) + PointChange(POINT_HP, -(GetHP() - 1)); + else + Dead(); + } + else + AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE, 2, AFF_DRINK_POISON, 15*60, 0, true); + }*/ + break; + + case 27987: // + // 50 47990 + // 30 + // 10 47992 + // 7 û 47993 + // 3 47994 + { + item->SetCount(item->GetCount() - 1); + + int r = number(1, 100); + + if (r <= 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Խϴ.")); + AutoGiveItem(27990); + } + else + { + const int prob_table_euckr[] = + { + 80, 90, 97 + }; + + const int prob_table_gb2312[] = + { + 95, 97, 99 + }; + + const int * prob_table = !g_iUseLocale ? prob_table_euckr : prob_table_gb2312; + + if (r <= prob_table[0]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + else if (r <= prob_table[1]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ְ Խϴ.")); + AutoGiveItem(27992); + } + else if (r <= prob_table[2]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ûְ Խϴ.")); + AutoGiveItem(27993); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ְ Խϴ.")); + AutoGiveItem(27994); + } + } + } + break; + + case 71013: // + CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), this); + item->SetCount(item->GetCount() - 1); + break; + + case 50100: // + case 50101: + case 50102: + case 50103: + case 50104: + case 50105: + case 50106: + CreateFly(item->GetVnum() - 50100 + FLY_FIREWORK1, this); + item->SetCount(item->GetCount() - 1); + break; + + case 50200: // + if (LC_IsYMIR() == true || LC_IsKorea() == true) + { + if (IS_BOTARYABLE_ZONE(GetMapIndex()) == true) + { + __OpenPrivateShop(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ")); + } + } + else + { + __OpenPrivateShop(); + } + break; + + case fishing::FISH_MIND_PILL_VNUM: + AddAffect(AFFECT_FISH_MIND_PILL, POINT_NONE, 0, AFF_FISH_MIND, 20*60, 0, true); + item->SetCount(item->GetCount() - 1); + break; + + case 50301: // ַ ü + case 50302: + case 50303: + { + if (IsPolymorphed() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return false; + } + + int lv = GetSkillLevel(SKILL_LEADERSHIP); + + if (lv < item->GetValue(0)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ʹ ϱⰡ ϴ.")); + return false; + } + + if (lv >= item->GetValue(1)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ƹ ʽϴ.")); + return false; + } + + if (LearnSkillByBook(SKILL_LEADERSHIP)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(SKILL_LEADERSHIP, get_global_time() + iReadDelay); + } + } + break; + + case 50304: // ü + case 50305: + case 50306: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + if (GetSkillLevel(SKILL_COMBO) == 0 && GetLevel() < 30) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 30 DZ ʽϴ.")); + return false; + } + + if (GetSkillLevel(SKILL_COMBO) == 1 && GetLevel() < 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 50 DZ ʽϴ.")); + return false; + } + + if (GetSkillLevel(SKILL_COMBO) >= 2) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + int iPct = item->GetValue(0); + + if (LearnSkillByBook(SKILL_COMBO, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(SKILL_COMBO, get_global_time() + iReadDelay); + } + } + break; + case 50311: // ü + case 50312: + case 50313: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = item->GetValue(0); + int iPct = MINMAX(0, item->GetValue(1), 100); + if (GetSkillLevel(dwSkillVnum)>=20 || dwSkillVnum-SKILL_LANGUAGE1+1 == GetEmpire()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ϻϰ ˾Ƶ ִ ̴.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50061 : // Ϻ ȯ ų ü + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = item->GetValue(0); + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum) >= 10) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50314: case 50315: case 50316: // ü + case 50323: case 50324: // ü + case 50325: case 50326: // ö ü + { + if (IsPolymorphed() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return false; + } + + int iSkillLevelLowLimit = item->GetValue(0); + int iSkillLevelHighLimit = item->GetValue(1); + int iPct = MINMAX(0, item->GetValue(2), 100); + int iLevelLimit = item->GetValue(3); + DWORD dwSkillVnum = 0; + + switch (item->GetVnum()) + { + case 50314: case 50315: case 50316: + dwSkillVnum = SKILL_POLYMORPH; + break; + + case 50323: case 50324: + dwSkillVnum = SKILL_ADD_HP; + break; + + case 50325: case 50326: + dwSkillVnum = SKILL_RESIST_PENETRATE; + break; + + default: + return false; + } + + if (0 == dwSkillVnum) + return false; + + if (GetLevel() < iLevelLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ÷ մϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) >= 40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) < iSkillLevelLowLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" å ʹ ϱⰡ ϴ.")); + return false; + } + + if (GetSkillLevel(dwSkillVnum) >= iSkillLevelHighLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" åδ ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + + case 50902: + case 50903: + case 50904: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_CREATE; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum)>=40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Success to learn skill "); + } + } + else + { + if (test_server) + { + ChatPacket(CHAT_TYPE_INFO, "[TEST_SERVER] Failed to learn skill "); + } + } + } + break; + + // MINING + case ITEM_MINING_SKILL_TRAIN_BOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_MINING; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetSkillLevel(dwSkillVnum)>=40) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ϴ.")); + return false; + } + + if (LearnSkillByBook(dwSkillVnum, iPct)) + { + ITEM_MANAGER::instance().RemoveItem(item); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + } + break; + // END_OF_MINING + + case ITEM_HORSE_SKILL_TRAIN_BOOK: + { + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߿ å ϴ.")); + return false; + + } + DWORD dwSkillVnum = SKILL_HORSE; + int iPct = MINMAX(0, item->GetValue(1), 100); + + if (GetLevel() < 50) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¸ ų ִ ƴմϴ.")); + return false; + } + + if (!test_server && get_global_time() < GetSkillNextReadTime(dwSkillVnum)) + { + if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY)) + { + // ־ȼ ߿ ð + RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("־ȼ ȭԸ Խϴ.")); + } + else + { + SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time()); + return false; + } + } + + if (GetPoint(POINT_HORSE_SKILL) >= 20 || + GetSkillLevel(SKILL_HORSE_WILDATTACK) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60 || + GetSkillLevel(SKILL_HORSE_WILDATTACK_RANGE) + GetSkillLevel(SKILL_HORSE_CHARGE) + GetSkillLevel(SKILL_HORSE_ESCAPE) >= 60) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ¸ ü ϴ.")); + return false; + } + + if (number(1, 100) <= iPct) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¸ ü о ¸ ų Ʈ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ʈδ ¸ ų ø ֽϴ.")); + PointChange(POINT_HORSE_SKILL, 1); + + int iReadDelay = number(SKILLBOOK_DELAY_MIN, SKILLBOOK_DELAY_MAX); + if (distribution_test_server) iReadDelay /= 3; + + if (!test_server) + SetSkillNextReadTime(dwSkillVnum, get_global_time() + iReadDelay); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¸ ü ؿ Ͽϴ.")); + } + + ITEM_MANAGER::instance().RemoveItem(item); + } + break; + + case 70102: // + case 70103: // + { + if (GetAlignment() >= 0) + return false; + + int delta = MIN(-GetAlignment(), item->GetValue(0)); + + sys_log(0, "%s ALIGNMENT ITEM %d", GetName(), delta); + + UpdateAlignment(delta); + item->SetCount(item->GetCount() - 1); + + if (delta / 10 > 0) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ±. 𰡰 ̾.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ %d Ͽϴ."), delta/10); + } + } + break; + + case 71107: // õ + { + int val = item->GetValue(0); + int interval = item->GetValue(1); + quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID()); + int last_use_time = pPC->GetFlag("mythical_peach.last_use_time"); + + if (get_global_time() - last_use_time < interval * 60 * 60) + { + if (test_server == false) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׽Ʈ ð ")); + } + } + + if (GetAlignment() == 200000) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ ̻ ø ϴ.")); + return false; + } + + if (200000 - GetAlignment() < val * 10) + { + val = (200000 - GetAlignment()) / 10; + } + + int old_alignment = GetAlignment() / 10; + + UpdateAlignment(val*10); + + item->SetCount(item->GetCount()-1); + pPC->SetFlag("mythical_peach.last_use_time", get_global_time()); + + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ±. 𰡰 ̾.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ %d Ͽϴ."), val); + + char buf[256 + 1]; + snprintf(buf, sizeof(buf), "%d %d", old_alignment, GetAlignment() / 10); + LogManager::instance().CharLog(this, val, "MYTHICAL_PEACH", buf); + } + break; + + case 71109: // Ż + case 72719: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() == true) + return false; + + if (item2->GetSocketCount() == 0) + return false; + + switch( item2->GetType() ) + { + case ITEM_WEAPON: + break; + case ITEM_ARMOR: + switch (item2->GetSubType()) + { + case ARMOR_EAR: + case ARMOR_WRIST: + case ARMOR_NECK: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + return false; + } + break; + + default: + return false; + } + + std::stack socket; + + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + socket.push(item2->GetSocket(i)); + + int idx = ITEM_SOCKET_MAX_NUM - 1; + + while (socket.size() > 0) + { + if (socket.top() > 2 && socket.top() != ITEM_BROKEN_METIN_VNUM) + break; + + idx--; + socket.pop(); + } + + if (socket.size() == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + return false; + } + + LPITEM pItemReward = AutoGiveItem(socket.top()); + + if (pItemReward != NULL) + { + item2->SetSocket(idx, 1); + + char buf[256+1]; + snprintf(buf, sizeof(buf), "%s(%u) %s(%u)", + item2->GetName(), item2->GetID(), pItemReward->GetName(), pItemReward->GetID()); + LogManager::instance().ItemLog(this, item, "USE_DETACHMENT_ONE", buf); + + item->SetCount(item->GetCount() - 1); + } + } + break; + + case 70201: // Ż + case 70202: // () + case 70203: // (ݻ) + case 70204: // () + case 70205: // () + case 70206: // () + { + // NEW_HAIR_STYLE_ADD + if (GetPart(PART_HAIR) >= 1001) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ŸϿ Ż Ұմϴ.")); + } + // END_NEW_HAIR_STYLE_ADD + else + { + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC) + { + int last_dye_level = pPC->GetFlag("dyeing_hair.last_dye_level"); + + if (last_dye_level == 0 || + last_dye_level+3 <= GetLevel() || + item->GetVnum() == 70201) + { + SetPart(PART_HAIR, item->GetVnum() - 70201); + + if (item->GetVnum() == 70201) + pPC->SetFlag("dyeing_hair.last_dye_level", 0); + else + pPC->SetFlag("dyeing_hair.last_dye_level", GetLevel()); + + item->SetCount(item->GetCount() - 1); + UpdatePacket(); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d Ǿ ٽ Ͻ ֽϴ."), last_dye_level+3); + } + } + } + } + break; + + case ITEM_NEW_YEAR_GREETING_VNUM: + { + DWORD dwBoxVnum = ITEM_NEW_YEAR_GREETING_VNUM; + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets; + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + for (int i = 0; i < count; i++) + { + if (dwVnums[i] == CSpecialItemGroup::GOLD) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + } + + item->SetCount(item->GetCount() - 1); + } + } + break; + + case ITEM_VALENTINE_ROSE: + case ITEM_VALENTINE_CHOCOLATE: + { + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(NULL); + int count = 0; + + + if (item->GetVnum() == ITEM_VALENTINE_ROSE && SEX_MALE==GET_SEX(this) || + item->GetVnum() == ITEM_VALENTINE_CHOCOLATE && SEX_FEMALE==GET_SEX(this)) + { + // ʾ . + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + item->SetCount(item->GetCount()-1); + } + break; + + case ITEM_WHITEDAY_CANDY: + case ITEM_WHITEDAY_ROSE: + { + DWORD dwBoxVnum = item->GetVnum(); + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(NULL); + int count = 0; + + + if (item->GetVnum() == ITEM_WHITEDAY_CANDY && SEX_MALE==GET_SEX(this) || + item->GetVnum() == ITEM_WHITEDAY_ROSE && SEX_FEMALE==GET_SEX(this)) + { + // ʾ . + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + item->SetCount(item->GetCount()-1); + } + break; + + case 50011: // + { + DWORD dwBoxVnum = 50011; + std::vector dwVnums; + std::vector dwCounts; + std::vector item_gets(NULL); + int count = 0; + + if (GiveItemFromSpecialItemGroup(dwBoxVnum, dwVnums, dwCounts, item_gets, count)) + { + for (int i = 0; i < count; i++) + { + char buf[50 + 1]; + snprintf(buf, sizeof(buf), "%u %u", dwVnums[i], dwCounts[i]); + LogManager::instance().ItemLog(this, item, "MOONLIGHT_GET", buf); + + //ITEM_MANAGER::instance().RemoveItem(item); + item->SetCount(item->GetCount() - 1); + + switch (dwVnums[i]) + { + case CSpecialItemGroup::GOLD: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), dwCounts[i]); + break; + + case CSpecialItemGroup::EXP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ź ɴϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ġ ȹ߽ϴ."), dwCounts[i]); + break; + + case CSpecialItemGroup::MOB: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + + case CSpecialItemGroup::SLOW: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ̴ ӵ ϴ!")); + break; + + case CSpecialItemGroup::DRAIN_HP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڰ ڱ Ͽϴ! ߽ϴ.")); + break; + + case CSpecialItemGroup::POISON: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ ⸦ ̸ ¸ ϴ!")); + break; + + case CSpecialItemGroup::MOB_GROUP: + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ Ͱ Ÿϴ!")); + break; + + default: + if (item_gets[i]) + { + if (dwCounts[i] > 1) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s %d Խϴ."), item_gets[i]->GetName(), dwCounts[i]); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڿ %s Խϴ."), item_gets[i]->GetName()); + } + break; + } + } + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ƹ͵ ϴ.")); + return false; + } + } + break; + + case ITEM_GIVE_STAT_RESET_COUNT_VNUM: + { + //PointChange(POINT_GOLD, -iCost); + PointChange(POINT_STAT_RESET_COUNT, 1); + item->SetCount(item->GetCount()-1); + } + break; + + case 50107: + { + EffectPacket(SE_CHINA_FIREWORK); + // ÷ش + AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true); + item->SetCount(item->GetCount()-1); + } + break; + + case 50108: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + + EffectPacket(SE_SPIN_TOP); + // ÷ش + AddAffect(AFFECT_CHINA_FIREWORK, POINT_STUN_PCT, 30, AFF_CHINA_FIREWORK, 5*60, 0, true); + item->SetCount(item->GetCount()-1); + } + break; + + case ITEM_WONSO_BEAN_VNUM: + PointChange(POINT_HP, GetMaxHP() - GetHP()); + item->SetCount(item->GetCount()-1); + break; + + case ITEM_WONSO_SUGAR_VNUM: + PointChange(POINT_SP, GetMaxSP() - GetSP()); + item->SetCount(item->GetCount()-1); + break; + + case ITEM_WONSO_FRUIT_VNUM: + PointChange(POINT_STAMINA, GetMaxStamina()-GetStamina()); + item->SetCount(item->GetCount()-1); + break; + + case 90008: // VCARD + case 90009: // VCARD + VCardUse(this, this, item); + break; + + case ITEM_ELK_VNUM: // ٷ + { + int iGold = item->GetSocket(0); + ITEM_MANAGER::instance().RemoveItem(item); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ȹ߽ϴ."), iGold); + PointChange(POINT_GOLD, iGold); + } + break; + + // ǥ + case 70021: + { + int HealPrice = quest::CQuestManager::instance().GetEventFlag("MonarchHealGold"); + if (HealPrice == 0) + HealPrice = 2000000; + + if (CMonarch::instance().HealMyEmpire(this, HealPrice)) + { + char szNotice[256]; + snprintf(szNotice, sizeof(szNotice), LC_TEXT(" ູ %s HP,SP äϴ."), EMPIRE_NAME(GetEmpire())); + SendNoticeMap(szNotice, GetMapIndex(), false); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ູ Ͽϴ.")); + } + } + break; + + case 27995: + { + } + break; + + case 71092 : // ü ӽ + { + if (m_pkChrTarget != NULL) + { + if (m_pkChrTarget->IsPolymorphed()) + { + m_pkChrTarget->SetPolymorph(0); + m_pkChrTarget->RemoveAffect(AFFECT_POLYMORPH); + } + } + else + { + if (IsPolymorphed()) + { + SetPolymorph(0); + RemoveAffect(AFFECT_POLYMORPH); + } + } + } + break; + + case 71051 : // 簡 + { + // , ̰, Ʈ 簡 + if (LC_IsEurope() || LC_IsSingapore() || LC_IsVietnam()) + return false; + + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetInventoryItem(wDestCell))) + return false; + + if (item2->IsExchanging() == true) + return false; + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->AddRareAttribute() == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӽ ߰ Ǿϴ")); + + int iAddedIdx = item2->GetRareAttrCount() + 4; + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_RARE_ATTR", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ Ӽ ߰ ϴ")); + } + } + break; + + case 71052 : // + { + // , ̰, Ʈ 簡 + if (LC_IsEurope() || LC_IsSingapore() || LC_IsVietnam()) + return false; + + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging() == true) + return false; + + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->ChangeRareAttribute() == true) + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CHANGE_RARE_ATTR", buf); + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų Ӽ ϴ")); + } + } + break; + + case ITEM_AUTO_HP_RECOVERY_S: + case ITEM_AUTO_HP_RECOVERY_M: + case ITEM_AUTO_HP_RECOVERY_L: + case ITEM_AUTO_HP_RECOVERY_X: + case ITEM_AUTO_SP_RECOVERY_S: + case ITEM_AUTO_SP_RECOVERY_M: + case ITEM_AUTO_SP_RECOVERY_L: + case ITEM_AUTO_SP_RECOVERY_X: + // ù ϴ ġ ... + // ׷ ׳ ϵ ڵ. ڿ ڵ ۵. + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + EAffectTypes type = AFFECT_NONE; + bool isSpecialPotion = false; + + switch (item->GetVnum()) + { + case ITEM_AUTO_HP_RECOVERY_X: + isSpecialPotion = true; + + case ITEM_AUTO_HP_RECOVERY_S: + case ITEM_AUTO_HP_RECOVERY_M: + case ITEM_AUTO_HP_RECOVERY_L: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S: + type = AFFECT_AUTO_HP_RECOVERY; + break; + + case ITEM_AUTO_SP_RECOVERY_X: + isSpecialPotion = true; + + case ITEM_AUTO_SP_RECOVERY_S: + case ITEM_AUTO_SP_RECOVERY_M: + case ITEM_AUTO_SP_RECOVERY_L: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS: + case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S: + case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S: + type = AFFECT_AUTO_SP_RECOVERY; + break; + } + + if (AFFECT_NONE == type) + break; + + if (item->GetCount() > 1) + { + int pos = GetEmptyInventory(item->GetSize()); + + if (-1 == pos) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ǰ ϴ.")); + break; + } + + item->SetCount( item->GetCount() - 1 ); + + LPITEM item2 = ITEM_MANAGER::instance().CreateItem( item->GetVnum(), 1 ); + item2->AddToCharacter(this, TItemPos(INVENTORY, pos)); + + if (item->GetSocket(1) != 0) + { + item2->SetSocket(1, item->GetSocket(1)); + } + + item = item2; + } + + CAffect* pAffect = FindAffect( type ); + + if (NULL == pAffect) + { + EPointTypes bonus = POINT_NONE; + + if (true == isSpecialPotion) + { + if (type == AFFECT_AUTO_HP_RECOVERY) + { + bonus = POINT_MAX_HP_PCT; + } + else if (type == AFFECT_AUTO_SP_RECOVERY) + { + bonus = POINT_MAX_SP_PCT; + } + } + + AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false); + + item->Lock(true); + item->SetSocket(0, true); + + AutoRecoveryItemProcess( type ); + } + else + { + if (item->GetID() == pAffect->dwFlag) + { + RemoveAffect( pAffect ); + + item->Lock(false); + item->SetSocket(0, false); + } + else + { + LPITEM old = FindItemByID( pAffect->dwFlag ); + + if (NULL != old) + { + old->Lock(false); + old->SetSocket(0, false); + } + + RemoveAffect( pAffect ); + + EPointTypes bonus = POINT_NONE; + + if (true == isSpecialPotion) + { + if (type == AFFECT_AUTO_HP_RECOVERY) + { + bonus = POINT_MAX_HP_PCT; + } + else if (type == AFFECT_AUTO_SP_RECOVERY) + { + bonus = POINT_MAX_SP_PCT; + } + } + + AddAffect( type, bonus, 4, item->GetID(), INFINITE_AFFECT_DURATION, 0, true, false); + + item->Lock(true); + item->SetSocket(0, true); + + AutoRecoveryItemProcess( type ); + } + } + } + break; + } + break; + + case USE_CLEAR: + { + RemoveBadAffect(); + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_INVISIBILITY: + { + if (item->GetVnum() == 70026) + { + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC != NULL) + { + int last_use_time = pPC->GetFlag("mirror_of_disapper.last_use_time"); + + if (get_global_time() - last_use_time < 10*60) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + + pPC->SetFlag("mirror_of_disapper.last_use_time", get_global_time()); + } + } + + AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, 300, 0, true); + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_POTION_NODELAY: + { + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 70020 : + case 71018 : + case 71019 : + case 71020 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + } + + bool used = false; + + if (item->GetValue(0) != 0) // HP 밪 ȸ + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(0) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(1) != 0) // SP 밪 ȸ + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(1) * (100 + GetPoint(POINT_POTION_BONUS)) / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (item->GetValue(3) != 0) // HP % ȸ + { + if (GetHP() < GetMaxHP()) + { + PointChange(POINT_HP, item->GetValue(3) * GetMaxHP() / 100); + EffectPacket(SE_HPUP_RED); + used = TRUE; + } + } + + if (item->GetValue(4) != 0) // SP % ȸ + { + if (GetSP() < GetMaxSP()) + { + PointChange(POINT_SP, item->GetValue(4) * GetMaxSP() / 100); + EffectPacket(SE_SPUP_BLUE); + used = TRUE; + } + } + + if (used) + { + if (item->GetVnum() == 50085 || item->GetVnum() == 50086) + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǵ Ͽϴ")); + SetUseSeedOrMoonBottleTime(); + } + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + m_nPotionLimit--; + + //RESTRICT_USE_SEED_OR_MOONBOTTLE + item->SetCount(item->GetCount() - 1); + //END_RESTRICT_USE_SEED_OR_MOONBOTTLE + } + } + break; + + case USE_POTION: + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit") > 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + + switch (item->GetVnum()) + { + case 27001 : + case 27002 : + case 27003 : + case 27004 : + case 27005 : + case 27006 : + if (quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count") < 10000) + { + if (m_nPotionLimit <= 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ѷ ʰϿϴ.")); + return false; + } + } + break; + + default : + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return false; + } + } + + if (item->GetValue(1) != 0) + { + if (GetPoint(POINT_SP_RECOVERY) + GetSP() >= GetMaxSP()) + { + return false; + } + + PointChange(POINT_SP_RECOVERY, item->GetValue(1) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100); + StartAffectEvent(); + EffectPacket(SE_SPUP_BLUE); + } + + if (item->GetValue(0) != 0) + { + if (GetPoint(POINT_HP_RECOVERY) + GetHP() >= GetMaxHP()) + { + return false; + } + + PointChange(POINT_HP_RECOVERY, item->GetValue(0) * MIN(200, (100 + GetPoint(POINT_POTION_BONUS))) / 100); + StartAffectEvent(); + EffectPacket(SE_HPUP_RED); + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + m_nPotionLimit--; + break; + + case USE_POTION_CONTINUE: + { + if (item->GetValue(0) != 0) + { + AddAffect(AFFECT_HP_RECOVER_CONTINUE, POINT_HP_RECOVER_CONTINUE, item->GetValue(0), 0, item->GetValue(2), 0, true); + } + else if (item->GetValue(1) != 0) + { + AddAffect(AFFECT_SP_RECOVER_CONTINUE, POINT_SP_RECOVER_CONTINUE, item->GetValue(1), 0, item->GetValue(2), 0, true); + } + else + return false; + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_ABILITY_UP: + { + switch (item->GetValue(0)) + { + case APPLY_MOV_SPEED: + AddAffect(AFFECT_MOV_SPEED, POINT_MOV_SPEED, item->GetValue(2), AFF_MOV_SPEED_POTION, item->GetValue(1), 0, true); + break; + + case APPLY_ATT_SPEED: + AddAffect(AFFECT_ATT_SPEED, POINT_ATT_SPEED, item->GetValue(2), AFF_ATT_SPEED_POTION, item->GetValue(1), 0, true); + break; + + case APPLY_STR: + AddAffect(AFFECT_STR, POINT_ST, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_DEX: + AddAffect(AFFECT_DEX, POINT_DX, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_CON: + AddAffect(AFFECT_CON, POINT_HT, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_INT: + AddAffect(AFFECT_INT, POINT_IQ, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_CAST_SPEED: + AddAffect(AFFECT_CAST_SPEED, POINT_CASTING_SPEED, item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_ATT_GRADE_BONUS: + AddAffect(AFFECT_ATT_GRADE, POINT_ATT_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + + case APPLY_DEF_GRADE_BONUS: + AddAffect(AFFECT_DEF_GRADE, POINT_DEF_GRADE_BONUS, + item->GetValue(2), 0, item->GetValue(1), 0, true); + break; + } + } + + if (GetDungeon()) + GetDungeon()->UsePotion(this); + + if (GetWarMap()) + GetWarMap()->UsePotion(this, item); + + item->SetCount(item->GetCount() - 1); + break; + + case USE_TALISMAN: + { + const int TOWN_PORTAL = 1; + const int MEMORY_PORTAL = 2; + + + // gm_guild_build, oxevent ʿ ȯ ȯ ϰ + if (GetMapIndex() == 200 || GetMapIndex() == 113) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ ϴ.")); + return false; + } + + if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̿ ǰԴϴ.")); + return false; + } + + if (m_pkWarpEvent) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̵ غ Ǿ ȯθ Ҽ ϴ")); + return false; + } + + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + int consumeLife = CalculateConsume(this); + + if (consumeLife < 0) + return false; + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + if (item->GetValue(0) == TOWN_PORTAL) // ȯ + { + if (item->GetSocket(0) == 0) + { + if (!GetDungeon()) + if (!GiveRecallItem(item)) + return false; + + PIXEL_POSITION posWarp; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp)) + { + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + PointChange(POINT_HP, -consumeLife, false); + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + WarpSet(posWarp.x, posWarp.y); + } + else + { + sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", GetName(), GetX(), GetY()); + } + } + else + { + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ġ ")); + + ProcessRecallItem(item); + } + } + else if (item->GetValue(0) == MEMORY_PORTAL) // ȯ + { + if (item->GetSocket(0) == 0) + { + if (GetDungeon()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȿ %s%s ϴ."), + item->GetName(), + g_iUseLocale ? "" : (under_han(item->GetName()) ? LC_TEXT("") : LC_TEXT(""))); + return false; + } + + if (!GiveRecallItem(item)) + return false; + } + else + { + // CONSUME_LIFE_WHEN_USE_WARP_ITEM + PointChange(POINT_HP, -consumeLife, false); + // END_OF_CONSUME_LIFE_WHEN_USE_WARP_ITEM + + ProcessRecallItem(item); + } + } + } + break; + + case USE_TUNING: + case USE_DETACHMENT: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging()) + return false; + + if (item2->GetVnum() >= 28330 && item2->GetVnum() <= 28343) // +3 + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("+3 ϴ")); + return false; + } + + if (item2->GetVnum() >= 28430 && item2->GetVnum() <= 28443) // +4 + { + if (item->GetVnum() == 71056) // ûǼ + { + RefineItem(item, item2); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ")); + } + } + else + { + RefineItem(item, item2); + } + } + break; + + // ACCESSORY_REFINE & ADD/CHANGE_ATTRIBUTES + case USE_PUT_INTO_BELT_SOCKET: + case USE_PUT_INTO_RING_SOCKET: + case USE_PUT_INTO_ACCESSORY_SOCKET: + case USE_ADD_ACCESSORY_SOCKET: + case USE_CLEAN_SOCKET: + case USE_CHANGE_ATTRIBUTE: + case USE_CHANGE_ATTRIBUTE2 : + case USE_ADD_ATTRIBUTE: + case USE_ADD_ATTRIBUTE2: + { + LPITEM item2; + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsEquipped()) + { + BuffOnAttr_RemoveBuffsFromItem(item2); + } + + // [NOTE] ڽƬ ۿ Ӽ οϵ, 簡 ƴ޶ û ־. + // ANTI_CHANGE_ATTRIBUTE Flag ߰Ͽ ȹ ϰ Ʈ ֵ ̾ + // ׵ ʿ ġ ش޷ ׳ ⼭ ... -_- + if (ITEM_COSTUME == item2->GetType()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->IsExchanging()) + return false; + + switch (item->GetSubType()) + { + case USE_CLEAN_SOCKET: + { + int i; + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item2->GetSocket(i) == ITEM_BROKEN_METIN_VNUM) + break; + } + + if (i == ITEM_SOCKET_MAX_NUM) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("û ʽϴ.")); + return false; + } + + int j = 0; + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + if (item2->GetSocket(i) != ITEM_BROKEN_METIN_VNUM && item2->GetSocket(i) != 0) + item2->SetSocket(j++, item2->GetSocket(i)); + } + + for (; j < ITEM_SOCKET_MAX_NUM; ++j) + { + if (item2->GetSocket(j) > 0) + item2->SetSocket(j, 1); + } + + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CLEAN_SOCKET", buf); + } + + item->SetCount(item->GetCount() - 1); + + } + break; + + case USE_CHANGE_ATTRIBUTE : + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӽ ϴ.")); + return false; + } + + if (GM_PLAYER == GetGMLevel() && false == test_server) + { + // + // Event Flag Ӽ ð ð 귶 ˻ϰ + // ð 귶ٸ Ӽ濡 ð ش. + // + + DWORD dwChangeItemAttrCycle = quest::CQuestManager::instance().GetEventFlag(msc_szChangeItemAttrCycleFlag); + if (dwChangeItemAttrCycle < msc_dwDefaultChangeItemAttrCycle) + dwChangeItemAttrCycle = msc_dwDefaultChangeItemAttrCycle; + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetPlayerID()); + + if (pPC) + { + DWORD dwNowMin = get_global_time() / 60; + + DWORD dwLastChangeItemAttrMin = pPC->GetFlag(msc_szLastChangeItemAttrFlag); + + if (dwLastChangeItemAttrMin + dwChangeItemAttrCycle > dwNowMin) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ٲ %d ̳ ٽ ϴ.(%d )"), + dwChangeItemAttrCycle, dwChangeItemAttrCycle - (dwNowMin - dwLastChangeItemAttrMin)); + return false; + } + + pPC->SetFlag(msc_szLastChangeItemAttrFlag, dwNowMin); + } + } + + if (item->GetSubType() == USE_CHANGE_ATTRIBUTE2) + { + int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] = + { + 0, 0, 30, 40, 3 + }; + + item2->ChangeAttribute(aiChangeProb); + } + else if (item->GetVnum() == 76014) + { + int aiChangeProb[ITEM_ATTRIBUTE_MAX_LEVEL] = + { + 0, 10, 50, 39, 1 + }; + + item2->ChangeAttribute(aiChangeProb); + } + + else + { + // Ưó + // 簡 ߰ ȵɰŶ Ͽ ϵ ڵ. + if (item->GetVnum() == 71151 || item->GetVnum() == 76023) + { + if ((item2->GetType() == ITEM_WEAPON) + || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY)) + { + bool bCanUse = true; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40) + { + bCanUse = false; + break; + } + } + if (false == bCanUse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ұմϴ.")); + break; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ մϴ.")); + break; + } + } + item2->ChangeAttribute(); + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Ͽϴ.")); + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + LogManager::instance().ItemLog(this, item, "CHANGE_ATTRIBUTE", buf); + } + + item->SetCount(item->GetCount() - 1); + break; + + case USE_ADD_ATTRIBUTE : + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + if (item2->GetAttributeCount() < 4) + { + // 簡 Ưó + // 簡 ߰ ȵɰŶ Ͽ ϵ ڵ. + if (item->GetVnum() == 71152 || item->GetVnum() == 76024) + { + if ((item2->GetType() == ITEM_WEAPON) + || (item2->GetType() == ITEM_ARMOR && item2->GetSubType() == ARMOR_BODY)) + { + bool bCanUse = true; + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + if (item2->GetLimitType(i) == LIMIT_LEVEL && item2->GetLimitValue(i) > 40) + { + bCanUse = false; + break; + } + } + if (false == bCanUse) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ұմϴ.")); + break; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʿ մϴ.")); + break; + } + } + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()]) + { + item2->AddAttribute(); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + + int iAddedIdx = item2->GetAttributeCount() - 1; + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_ATTRIBUTE_SUCCESS", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̻ ̿Ͽ Ӽ ߰ ϴ.")); + } + break; + + case USE_ADD_ATTRIBUTE2 : + // ູ + // 簡񼭸 Ӽ 4 ߰ Ų ۿ ؼ ϳ Ӽ ٿش. + if (item2->GetAttributeSetIndex() == -1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ Դϴ.")); + return false; + } + + // Ӽ ̹ 4 ߰ Ǿ Ӽ ߰ ϴ. + if (item2->GetAttributeCount() == 4) + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (number(1, 100) <= aiItemAttributeAddPercent[item2->GetAttributeCount()]) + { + item2->AddAttribute(); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + + int iAddedIdx = item2->GetAttributeCount() - 1; + LogManager::instance().ItemLog( + GetPlayerID(), + item2->GetAttributeType(iAddedIdx), + item2->GetAttributeValue(iAddedIdx), + item->GetID(), + "ADD_ATTRIBUTE2_SUCCESS", + buf, + GetDesc()->GetHostName(), + item->GetOriginalVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ӽ ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_ATTRIBUTE2_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else if (item2->GetAttributeCount() == 5) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̻ ̿Ͽ Ӽ ߰ ϴ.")); + } + else if (item2->GetAttributeCount() < 4) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 簡񼭸 ̿Ͽ Ӽ ߰ ּ.")); + } + else + { + // wtf ?! + sys_err("ADD_ATTRIBUTE2 : Item has wrong AttributeCount(%d)", item2->GetAttributeCount()); + } + break; + + case USE_ADD_ACCESSORY_SOCKET: + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (item2->IsAccessoryForSocket()) + { + if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM) + { + if (number(1, 100) <= 50) + { + item2->SetAccessorySocketMaxGrade(item2->GetAccessorySocketMaxGrade() + 1); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰Ǿϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_SOCKET_SUCCESS", buf); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "ADD_SOCKET_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ߰ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߰ Դϴ.")); + } + } + break; + + case USE_PUT_INTO_BELT_SOCKET: + case USE_PUT_INTO_ACCESSORY_SOCKET: + if (item2->IsAccessoryForSocket() && item->CanPutInto(item2)) + { + char buf[21]; + snprintf(buf, sizeof(buf), "%u", item2->GetID()); + + if (item2->GetAccessorySocketGrade() < item2->GetAccessorySocketMaxGrade()) + { + if (number(1, 100) <= aiAccessorySocketPutPct[item2->GetAccessorySocketGrade()]) + { + item2->SetAccessorySocketGrade(item2->GetAccessorySocketGrade() + 1); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "PUT_SOCKET_SUCCESS", buf); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͽϴ.")); + LogManager::instance().ItemLog(this, item, "PUT_SOCKET_FAIL", buf); + } + + item->SetCount(item->GetCount() - 1); + } + else + { + if (item2->GetAccessorySocketMaxGrade() == 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̾Ƹ Ǽ ߰ؾմϴ.")); + else if (item2->GetAccessorySocketMaxGrade() < ITEM_ACCESSORY_SOCKET_MAX_NUM) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ϴ.")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̾Ƹ ߰ؾմϴ.")); + } + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ׼ ̻ ϴ.")); + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + } + if (item2->IsEquipped()) + { + BuffOnAttr_AddBuffsFromItem(item2); + } + } + break; + // END_OF_ACCESSORY_REFINE & END_OF_ADD_ATTRIBUTES & END_OF_CHANGE_ATTRIBUTES + + case USE_BAIT: + { + + if (m_pkFishingEvent) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߿ ̳ Ƴ ϴ.")); + return false; + } + + LPITEM weapon = GetWear(WEAR_WEAPON); + + if (!weapon || weapon->GetType() != ITEM_ROD) + return false; + + if (weapon->GetSocket(2)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ִ ̳ %s ϴ."), item->GetName()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ô뿡 %s ̳ ϴ."), item->GetName()); + } + + weapon->SetSocket(2, item->GetValue(0)); + item->SetCount(item->GetCount() - 1); + } + break; + + case USE_MOVE: + case USE_TREASURE_BOX: + case USE_MONEYBAG: + break; + + case USE_AFFECT : + { + if (FindAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + // PC_BANG_ITEM_ADD + if (item->IsPCBangItem() == true) + { + // PC üũؼ ó + if (CPCBangManager::instance().IsPCBangIP(GetDesc()->GetHostName()) == false) + { + // PC ƴ! + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" PC濡 ֽϴ.")); + return false; + } + } + // END_PC_BANG_ITEM_ADD + + AddAffect(item->GetValue(0), aApplyInfo[item->GetValue(1)].bPointType, item->GetValue(2), 0, item->GetValue(3), 0, false); + item->SetCount(item->GetCount() - 1); + } + } + break; + + case USE_CREATE_STONE: + AutoGiveItem(number(28000, 28013)); + item->SetCount(item->GetCount() - 1); + break; + + // ų ó + case USE_RECIPE : + { + LPITEM pSource1 = FindSpecifyItem(item->GetValue(1)); + DWORD dwSourceCount1 = item->GetValue(2); + + LPITEM pSource2 = FindSpecifyItem(item->GetValue(3)); + DWORD dwSourceCount2 = item->GetValue(4); + + if (dwSourceCount1 != 0) + { + if (pSource1 == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ᰡ մϴ.")); + return false; + } + } + + if (dwSourceCount2 != 0) + { + if (pSource2 == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ᰡ մϴ.")); + return false; + } + } + + if (pSource1 != NULL) + { + if (pSource1->GetCount() < dwSourceCount1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) մϴ."), pSource1->GetName()); + return false; + } + + pSource1->SetCount(pSource1->GetCount() - dwSourceCount1); + } + + if (pSource2 != NULL) + { + if (pSource2->GetCount() < dwSourceCount2) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) մϴ."), pSource2->GetName()); + return false; + } + + pSource2->SetCount(pSource2->GetCount() - dwSourceCount2); + } + + LPITEM pBottle = FindSpecifyItem(50901); + + if (!pBottle || pBottle->GetCount() < 1) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ڸϴ.")); + return false; + } + + pBottle->SetCount(pBottle->GetCount() - 1); + + if (number(1, 100) > item->GetValue(5)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ߽ϴ.")); + return false; + } + + AutoGiveItem(item->GetValue(0)); + } + break; + } + } + break; + + case ITEM_METIN: + { + LPITEM item2; + + if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) + return false; + + if (item2->IsExchanging()) + return false; + + if (item2->GetType() == ITEM_PICK) return false; + if (item2->GetType() == ITEM_ROD) return false; + + int i; + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + { + DWORD dwVnum; + + if ((dwVnum = item2->GetSocket(i)) <= 2) + continue; + + TItemTable * p = ITEM_MANAGER::instance().GetTable(dwVnum); + + if (!p) + continue; + + if (item->GetValue(5) == p->alValues[5]) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ϴ.")); + return false; + } + } + + if (item2->GetType() == ITEM_ARMOR) + { + if (!IS_SET(item->GetWearFlag(), WEARABLE_BODY) || !IS_SET(item2->GetWearFlag(), WEARABLE_BODY)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ϴ.")); + return false; + } + } + else if (item2->GetType() == ITEM_WEAPON) + { + if (!IS_SET(item->GetWearFlag(), WEARABLE_WEAPON)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ƾ ⿡ ϴ.")); + return false; + } + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + return false; + } + + for (i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + if (item2->GetSocket(i) >= 1 && item2->GetSocket(i) <= 2 && item2->GetSocket(i) >= item->GetValue(2)) + { + // Ȯ + if (number(1, 100) <= 30) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ƾ Ͽϴ.")); + item2->SetSocket(i, item->GetVnum()); + } + else + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ƾ Ͽϴ.")); + item2->SetSocket(i, ITEM_BROKEN_METIN_VNUM); + } + + LogManager::instance().ItemLog(this, item2, "SOCKET", item->GetName()); + ITEM_MANAGER::instance().RemoveItem(item, "REMOVE (METIN)"); + break; + } + + if (i == ITEM_SOCKET_MAX_NUM) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ִ ϴ.")); + } + break; + + case ITEM_AUTOUSE: + case ITEM_MATERIAL: + case ITEM_SPECIAL: + case ITEM_TOOL: + case ITEM_LOTTERY: + break; + + case ITEM_TOTEM: + { + if (!item->IsEquipped()) + EquipItem(item); + } + break; + + case ITEM_BLEND: + // ο ʵ + sys_log(0,"ITEM_BLEND!!"); + if (Blend_Item_find(item->GetVnum())) + { + int affect_type = AFFECT_BLEND; + if (item->GetSocket(0) >= _countof(aApplyInfo)) + { + sys_err ("INVALID BLEND ITEM(id : %d, vnum : %d). APPLY TYPE IS %d.", item->GetID(), item->GetVnum(), item->GetSocket(0)); + return false; + } + int apply_type = aApplyInfo[item->GetSocket(0)].bPointType; + int apply_value = item->GetSocket(1); + int apply_duration = item->GetSocket(2); + + if (FindAffect(affect_type, apply_type)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + if (FindAffect(AFFECT_EXP_BONUS_EURO_FREE, POINT_RESIST_MAGIC)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ȿ ɷ ֽϴ.")); + } + else + { + AddAffect(affect_type, apply_type, apply_value, 0, apply_duration, 0, false); + item->SetCount(item->GetCount() - 1); + } + } + } + break; + case ITEM_EXTRACT: + { + LPITEM pDestItem = GetItem(DestCell); + if (NULL == pDestItem) + { + return false; + } + switch (item->GetSubType()) + { + case EXTRACT_DRAGON_SOUL: + if (pDestItem->IsDragonSoul()) + { + return DSManager::instance().PullOut(this, NPOS, pDestItem, item); + } + return false; + case EXTRACT_DRAGON_HEART: + if (pDestItem->IsDragonSoul()) + { + return DSManager::instance().ExtractDragonHeart(this, pDestItem, item); + } + return false; + default: + return false; + } + } + break; + + case ITEM_NONE: + sys_err("Item type NONE %s", item->GetName()); + break; + + default: + sys_log(0, "UseItemEx: Unknown type %s %d", item->GetName(), item->GetType()); + return false; + } + + return true; +} + +int g_nPortalLimitTime = 10; + +bool CHARACTER::UseItem(TItemPos Cell, TItemPos DestCell) +{ + WORD wCell = Cell.cell; + BYTE window_type = Cell.window_type; + WORD wDestCell = DestCell.cell; + BYTE bDestInven = DestCell.window_type; + LPITEM item; + + if (!CanHandleItem()) + return false; + + if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) + return false; + + sys_log(0, "%s: USE_ITEM %s (inven %d, cell: %d)", GetName(), item->GetName(), window_type, wCell); + + if (item->IsExchanging()) + return false; + + if (!item->CanUsedBy(this)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + if (IsStun()) + return false; + + if (false == FN_check_item_sex(this, item)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + //PREVENT_TRADE_WINDOW + if (IS_SUMMON_ITEM(item->GetVnum())) + { + if (false == IS_SUMMONABLE_ZONE(GetMapIndex())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ҽ ϴ.")); + return false; + } + + // ȥ SUMMONABLE_ZONE ִ° WarpToPC() üũ + + //Ÿ ʿ ȯθ ƹ. + if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ÿ ߿ ȯ,ȯθ Ҽ ϴ.")); + return false; + } + int iPulse = thecore_pulse(); + + //â üũ + if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("â %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(g_nPortalLimitTime)); + return false; + } + + //ŷ â üũ + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ȯ,ȯ Ҽ ϴ.")); + return false; + } + + //PREVENT_REFINE_HACK + // ðüũ + { + if (iPulse - GetRefineTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + return false; + } + } + //END_PREVENT_REFINE_HACK + + + //PREVENT_ITEM_COPY + { + if (iPulse - GetMyShopTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("λ %d ̳ ȯ,ȯθ ϴ."), g_nPortalLimitTime); + return false; + } + + } + //END_PREVENT_ITEM_COPY + + + //ȯ Ÿüũ + if (item->GetVnum() != 70302) + { + PIXEL_POSITION posWarp; + + int x = 0; + int y = 0; + + double nDist = 0; + const double nDistant = 5000.0; + //ȯ + if (item->GetVnum() == 22010) + { + x = item->GetSocket(0) - GetX(); + y = item->GetSocket(1) - GetY(); + } + //ȯ + else if (item->GetVnum() == 22000) + { + SECTREE_MANAGER::instance().GetRecallPositionByEmpire(GetMapIndex(), GetEmpire(), posWarp); + + if (item->GetSocket(0) == 0) + { + x = posWarp.x - GetX(); + y = posWarp.y - GetY(); + } + else + { + x = item->GetSocket(0) - GetX(); + y = item->GetSocket(1) - GetY(); + } + } + + nDist = sqrt(pow((float)x,2) + pow((float)y,2)); + + if (nDistant > nDist) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̵ Ǿ ġ ʹ ȯθ Ҽ ϴ.")); + if (test_server) + ChatPacket(CHAT_TYPE_INFO, "PossibleDistant %f nNowDist %f", nDistant,nDist); + return false; + } + } + + //PREVENT_PORTAL_AFTER_EXCHANGE + //ȯ ðüũ + if (iPulse - GetExchangeTime() < PASSES_PER_SEC(g_nPortalLimitTime)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷ %d ̳ ȯ,ȯε ϴ."), g_nPortalLimitTime); + return false; + } + //END_PREVENT_PORTAL_AFTER_EXCHANGE + + } + + // ŷâ üũ + if (item->GetVnum() == 50200 | item->GetVnum() == 71049) + { + if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷâ,â ¿ ,ܺ Ҽ ϴ.")); + return false; + } + + } + //END_PREVENT_TRADE_WINDOW + + if (IS_SET(item->GetFlag(), ITEM_FLAG_LOG)) // α׸ ó + { + DWORD vid = item->GetVID(); + DWORD oldCount = item->GetCount(); + DWORD vnum = item->GetVnum(); + + char hint[ITEM_NAME_MAX_LEN + 32 + 1]; + int len = snprintf(hint, sizeof(hint) - 32, "%s", item->GetName()); + + if (len < 0 || len >= (int) sizeof(hint) - 32) + len = (sizeof(hint) - 32) - 1; + + bool ret = UseItemEx(item, DestCell); + + if (NULL == ITEM_MANAGER::instance().FindByVID(vid)) // UseItemEx Ǿ. α׸ + { + LogManager::instance().ItemLog(this, vid, vnum, "REMOVE", hint); + } + else if (oldCount != item->GetCount()) + { + snprintf(hint + len, sizeof(hint) - len, " %u", oldCount - 1); + LogManager::instance().ItemLog(this, vid, vnum, "USE_ITEM", hint); + } + return (ret); + } + else + return UseItemEx(item, DestCell); +} + +bool CHARACTER::DropItem(TItemPos Cell, BYTE bCount) +{ + LPITEM item = NULL; + + if (!CanHandleItem()) + { + if (NULL != DragonSoul_RefineWindow_GetOpener()) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭâ ¿ ű ϴ.")); + return false; + } + + if (IsDead()) + return false; + + if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) + return false; + + if (item->IsExchanging()) + return false; + + if (true == item->isLocked()) + return false; + + if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) + return false; + + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP | ITEM_ANTIFLAG_GIVE)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ.")); + return false; + } + + if (bCount == 0 || bCount > item->GetCount()) + bCount = item->GetCount(); + + SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, 255); // Quickslot + + LPITEM pkItemToDrop; + + if (bCount == item->GetCount()) + { + item->RemoveFromCharacter(); + pkItemToDrop = item; + } + else + { + if (bCount == 0) + { + if (test_server) + sys_log(0, "[DROP_ITEM] drop item count == 0"); + return false; + } + + // check non-split items for china + //if (LC_IsNewCIBN()) + // if (item->GetVnum() == 71095 || item->GetVnum() == 71050 || item->GetVnum() == 70038) + // return false; + + item->SetCount(item->GetCount() - bCount); + ITEM_MANAGER::instance().FlushDelayedSave(item); + + pkItemToDrop = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), bCount); + + // copy item socket -- by mhh + FN_copy_item_socket(pkItemToDrop, item); + + char szBuf[51 + 1]; + snprintf(szBuf, sizeof(szBuf), "%u %u", pkItemToDrop->GetID(), pkItemToDrop->GetCount()); + LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf); + } + + PIXEL_POSITION pxPos = GetXYZ(); + + if (pkItemToDrop->AddToGround(GetMapIndex(), pxPos)) + { + // ѱ ش޶ Ƽ + // ٴڿ Ӽα׸ . + if (LC_IsYMIR()) + item->AttrLog(); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 3 ϴ.")); + pkItemToDrop->StartDestroyEvent(); + + ITEM_MANAGER::instance().FlushDelayedSave(pkItemToDrop); + + char szHint[32 + 1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", pkItemToDrop->GetName(), pkItemToDrop->GetCount(), pkItemToDrop->GetOriginalVnum()); + LogManager::instance().ItemLog(this, pkItemToDrop, "DROP", szHint); + //Motion(MOTION_PICKUP); + } + + return true; +} + +bool CHARACTER::DropGold(int gold) +{ + if (gold <= 0 || gold > GetGold()) + return false; + + if (!CanHandleItem()) + return false; + + if (0 != g_GoldDropTimeLimitValue) + { + if (get_dword_time() < m_dwLastGoldDropTime+g_GoldDropTimeLimitValue) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" 带 ϴ.")); + return false; + } + } + + m_dwLastGoldDropTime = get_dword_time(); + + LPITEM item = ITEM_MANAGER::instance().CreateItem(1, gold); + + if (item) + { + PIXEL_POSITION pos = GetXYZ(); + + if (item->AddToGround(GetMapIndex(), pos)) + { + //Motion(MOTION_PICKUP); + PointChange(POINT_GOLD, -gold, true); + + // ٴ װ ִµ, + // ó ߿ ϳ, + // ũγ, Ἥ 1000 带 0 , + // ٰ ûϴ ִ. + // ׷ 츦 ġ 忡 ؼ α׸ . + if (LC_IsBrazil() == true) + { + if (gold >= 213) + LogManager::instance().CharLog(this, gold, "DROP_GOLD", ""); + } + else + { + if (gold > 1000) // õ ̻ Ѵ. + LogManager::instance().CharLog(this, gold, "DROP_GOLD", ""); + } + + if (false == LC_IsBrazil()) + { + item->StartDestroyEvent(150); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ϴ."), 150/60); + } + else + { + item->StartDestroyEvent(60); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d ϴ."), 1); + } + } + + Save(); + return true; + } + + return false; +} + +bool CHARACTER::MoveItem(TItemPos Cell, TItemPos DestCell, BYTE count) +{ + LPITEM item = NULL; + + if (!IsValidItemPosition(Cell)) + return false; + + if (!(item = GetItem(Cell))) + return false; + + if (item->IsExchanging()) + return false; + + if (item->GetCount() < count) + return false; + + if (INVENTORY == Cell.window_type && Cell.cell >= INVENTORY_MAX_NUM && IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + return false; + + if (true == item->isLocked()) + return false; + + if (!IsValidItemPosition(DestCell)) + { + return false; + } + + if (!CanHandleItem()) + { + if (NULL != DragonSoul_RefineWindow_GetOpener()) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȭâ ¿ ű ϴ.")); + return false; + } + + // ȹ û Ʈ κ丮 Ư Ÿ ۸ ִ. + if (DestCell.IsBeltInventoryPosition() && false == CBeltInventoryHelper::CanMoveIntoBeltInventory(item)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ʈ κ丮 ű ϴ.")); + return false; + } + + // ̹ ٸ ű , 'å ' Ȯϰ ű + if (Cell.IsEquipPosition() && !CanUnequipNow(item)) + return false; + + if (DestCell.IsEquipPosition()) + { + if (GetItem(DestCell)) // ˻ص ȴ. + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ ϰ ֽϴ.")); + + return false; + } + + EquipItem(item, DestCell.cell - INVENTORY_MAX_NUM); + } + else + { + if (item->IsDragonSoul()) + { + if (item->IsEquipped()) + { + return DSManager::instance().PullOut(this, DestCell, item); + } + else + { + if (DestCell.window_type != DRAGON_SOUL_INVENTORY) + { + return false; + } + + if (!DSManager::instance().IsValidCellForThisItem(item, DestCell)) + return false; + } + } + // ȥ ƴ ȥ κ  . + else if (DRAGON_SOUL_INVENTORY == DestCell.window_type) + return false; + + LPITEM item2; + + if ((item2 = GetItem(DestCell)) && item != item2 && item2->IsStackable() && + !IS_SET(item2->GetAntiFlag(), ITEM_ANTIFLAG_STACK) && + item2->GetVnum() == item->GetVnum()) // ĥ ִ + { + for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i) + if (item2->GetSocket(i) != item->GetSocket(i)) + return false; + + if (count == 0) + count = item->GetCount(); + + sys_log(0, "%s: ITEM_STACK %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + count = MIN(200 - item2->GetCount(), count); + + item->SetCount(item->GetCount() - count); + item2->SetCount(item2->GetCount() + count); + return true; + } + + if (!IsEmptyItemGrid(DestCell, item->GetSize(), Cell.cell)) + return false; + + if (count == 0 || count >= item->GetCount() || !item->IsStackable() || IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) + { + sys_log(0, "%s: ITEM_MOVE %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + item->RemoveFromCharacter(); + SetItem(DestCell, item); + + if (INVENTORY == Cell.window_type && INVENTORY == DestCell.window_type) + SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, DestCell.cell); + } + else if (count < item->GetCount()) + { + //check non-split items + //if (LC_IsNewCIBN()) + //{ + // if (item->GetVnum() == 71095 || item->GetVnum() == 71050 || item->GetVnum() == 70038) + // { + // return false; + // } + //} + + sys_log(0, "%s: ITEM_SPLIT %s (window: %d, cell : %d) -> (window:%d, cell %d) count %d", GetName(), item->GetName(), Cell.window_type, Cell.cell, + DestCell.window_type, DestCell.cell, count); + + item->SetCount(item->GetCount() - count); + LPITEM item2 = ITEM_MANAGER::instance().CreateItem(item->GetVnum(), count); + + // copy socket -- by mhh + FN_copy_item_socket(item2, item); + + item2->AddToCharacter(this, DestCell); + + char szBuf[51+1]; + snprintf(szBuf, sizeof(szBuf), "%u %u %u %u ", item2->GetID(), item2->GetCount(), item->GetCount(), item->GetCount() + item2->GetCount()); + LogManager::instance().ItemLog(this, item, "ITEM_SPLIT", szBuf); + } + } + + return true; +} + +namespace NPartyPickupDistribute +{ + struct FFindOwnership + { + LPITEM item; + LPCHARACTER owner; + + FFindOwnership(LPITEM item) + : item(item), owner(NULL) + { + } + + void operator () (LPCHARACTER ch) + { + if (item->IsOwnership(ch)) + owner = ch; + } + }; + + struct FCountNearMember + { + int total; + int x, y; + + FCountNearMember(LPCHARACTER center ) + : total(0), x(center->GetX()), y(center->GetY()) + { + } + + void operator () (LPCHARACTER ch) + { + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + total += 1; + } + }; + + struct FMoneyDistributor + { + int total; + LPCHARACTER c; + int x, y; + int iMoney; + + FMoneyDistributor(LPCHARACTER center, int iMoney) + : total(0), c(center), x(center->GetX()), y(center->GetY()), iMoney(iMoney) + { + } + + void operator ()(LPCHARACTER ch) + { + if (ch!=c) + if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) + { + ch->PointChange(POINT_GOLD, iMoney, true); + + if (iMoney > 1000) // õ ̻ Ѵ. + LogManager::instance().CharLog(ch, iMoney, "GET_GOLD", ""); + } + } + }; +} + +void CHARACTER::GiveGold(int iAmount) +{ + if (iAmount <= 0) + return; + + sys_log(0, "GIVE_GOLD: %s %d", GetName(), iAmount); + + if (GetParty()) + { + LPPARTY pParty = GetParty(); + + // Ƽ ִ . + DWORD dwTotal = iAmount; + DWORD dwMyAmount = dwTotal; + + NPartyPickupDistribute::FCountNearMember funcCountNearMember(this); + pParty->ForEachOnlineMember(funcCountNearMember); + + if (funcCountNearMember.total > 1) + { + DWORD dwShare = dwTotal / funcCountNearMember.total; + dwMyAmount -= dwShare * (funcCountNearMember.total - 1); + + NPartyPickupDistribute::FMoneyDistributor funcMoneyDist(this, dwShare); + + pParty->ForEachOnlineMember(funcMoneyDist); + } + + PointChange(POINT_GOLD, dwMyAmount, true); + + if (dwMyAmount > 1000) // õ ̻ Ѵ. + LogManager::instance().CharLog(this, dwMyAmount, "GET_GOLD", ""); + } + else + { + PointChange(POINT_GOLD, iAmount, true); + + // ٴ װ ִµ, + // ó ߿ ϳ, + // ũγ, Ἥ 1000 带 0 , + // ٰ ûϴ ִ. + // ׷ 츦 ġ 忡 ؼ α׸ . + if (LC_IsBrazil() == true) + { + if (iAmount >= 213) + LogManager::instance().CharLog(this, iAmount, "GET_GOLD", ""); + } + else + { + if (iAmount > 1000) // õ ̻ Ѵ. + LogManager::instance().CharLog(this, iAmount, "GET_GOLD", ""); + } + } +} + +bool CHARACTER::PickupItem(DWORD dwVID) +{ + LPITEM item = ITEM_MANAGER::instance().FindByVID(dwVID); + + if (IsObserverMode()) + return false; + + if (!item || !item->GetSectree()) + return false; + + if (item->DistanceValid(this)) + { + if (item->IsOwnership(this)) + { + // ϴ ũ + if (item->GetType() == ITEM_ELK) + { + GiveGold(item->GetCount()); + item->RemoveFromGround(); + + M2_DESTROY_ITEM(item); + + Save(); + } + // ̶ + else + { + if (item->IsStackable() && !IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) + { + BYTE bCount = item->GetCount(); + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM item2 = GetInventoryItem(i); + + if (!item2) + continue; + + if (item2->GetVnum() == item->GetVnum()) + { + int j; + + for (j = 0; j < ITEM_SOCKET_MAX_NUM; ++j) + if (item2->GetSocket(j) != item->GetSocket(j)) + break; + + if (j != ITEM_SOCKET_MAX_NUM) + continue; + + BYTE bCount2 = MIN(200 - item2->GetCount(), bCount); + bCount -= bCount2; + + item2->SetCount(item2->GetCount() + bCount2); + + if (bCount == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item2->GetName()); + M2_DESTROY_ITEM(item); + if (item2->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem (GetPlayerID(), item2); + return true; + } + } + } + + item->SetCount(bCount); + } + + int iEmptyCell; + if (item->IsDragonSoul()) + { + if ((iEmptyCell = GetEmptyDragonSoulInventory(item)) == -1) + { + sys_log(0, "No empty ds inventory pid %u size %ud itemid %u", GetPlayerID(), item->GetSize(), item->GetID()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + } + else + { + if ((iEmptyCell = GetEmptyInventory(item->GetSize())) == -1) + { + sys_log(0, "No empty inventory pid %u size %ud itemid %u", GetPlayerID(), item->GetSize(), item->GetID()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + } + + item->RemoveFromGround(); + + if (item->IsDragonSoul()) + item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell)); + else + item->AddToCharacter(this, TItemPos(INVENTORY, iEmptyCell)); + + char szHint[32+1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum()); + LogManager::instance().ItemLog(this, item, "GET", szHint); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + + if (item->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem (GetPlayerID(), item); + } + + //Motion(MOTION_PICKUP); + return true; + } + else if (!IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_DROP) && GetParty()) + { + // ٸ Ƽ Ѵٸ + NPartyPickupDistribute::FFindOwnership funcFindOwnership(item); + + GetParty()->ForEachOnlineMember(funcFindOwnership); + + LPCHARACTER owner = funcFindOwnership.owner; + + int iEmptyCell; + + if (item->IsDragonSoul()) + { + if (!(owner && (iEmptyCell = owner->GetEmptyDragonSoulInventory(item)) != -1)) + { + owner = this; + + if ((iEmptyCell = GetEmptyDragonSoulInventory(item)) == -1) + { + owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + } + } + else + { + if (!(owner && (iEmptyCell = owner->GetEmptyInventory(item->GetSize())) != -1)) + { + owner = this; + + if ((iEmptyCell = GetEmptyInventory(item->GetSize())) == -1) + { + owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ϰ ִ ʹ ϴ.")); + return false; + } + } + } + + item->RemoveFromGround(); + + if (item->IsDragonSoul()) + item->AddToCharacter(owner, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell)); + else + item->AddToCharacter(owner, TItemPos(INVENTORY, iEmptyCell)); + + char szHint[32+1]; + snprintf(szHint, sizeof(szHint), "%s %u %u", item->GetName(), item->GetCount(), item->GetOriginalVnum()); + LogManager::instance().ItemLog(owner, item, "GET", szHint); + + if (owner == this) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + else + { + owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s κ %s"), GetName(), item->GetName()); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" : %s Կ %s"), owner->GetName(), item->GetName()); + } + + if (item->GetType() == ITEM_QUEST) + quest::CQuestManager::instance().PickupItem (owner->GetPlayerID(), item); + + return true; + } + } + + return false; +} + +bool CHARACTER::SwapItem(BYTE bCell, BYTE bDestCell) +{ + if (!CanHandleItem()) + return false; + + TItemPos srcCell(INVENTORY, bCell), destCell(INVENTORY, bDestCell); + + // ùٸ Cell ˻ + // ȥ Swap Ƿ, ⼭ ɸ. + //if (bCell >= INVENTORY_MAX_NUM + WEAR_MAX_NUM || bDestCell >= INVENTORY_MAX_NUM + WEAR_MAX_NUM) + if (srcCell.IsDragonSoulEquipPosition() || destCell.IsDragonSoulEquipPosition()) + return false; + + // CELL ˻ + if (bCell == bDestCell) + return false; + + // â ġ Swap . + if (srcCell.IsEquipPosition() && destCell.IsEquipPosition()) + return false; + + LPITEM item1, item2; + + // item2 â ִ ǵ. + if (srcCell.IsEquipPosition()) + { + item1 = GetInventoryItem(bDestCell); + item2 = GetInventoryItem(bCell); + } + else + { + item1 = GetInventoryItem(bCell); + item2 = GetInventoryItem(bDestCell); + } + + if (!item1 || !item2) + return false; + + if (item1 == item2) + { + sys_log(0, "[WARNING][WARNING][HACK USER!] : %s %d %d", m_stName.c_str(), bCell, bDestCell); + return false; + } + + // item2 bCellġ  ִ ȮѴ. + if (!IsEmptyItemGrid(TItemPos (INVENTORY, item1->GetCell()), item2->GetSize(), item1->GetCell())) + return false; + + // ٲ â + if (TItemPos(EQUIPMENT, item2->GetCell()).IsEquipPosition()) + { + BYTE bEquipCell = item2->GetCell() - INVENTORY_MAX_NUM; + BYTE bInvenCell = item1->GetCell(); + + // ְ, ¿߸ + if (false == CanUnequipNow(item2) || false == CanEquipNow(item1)) + return false; + + if (bEquipCell != item1->FindEquipCell(this)) // ġ϶ + return false; + + item2->RemoveFromCharacter(); + + if (item1->EquipTo(this, bEquipCell)) + item2->AddToCharacter(this, TItemPos(INVENTORY, bInvenCell)); + else + sys_err("SwapItem cannot equip %s! item1 %s", item2->GetName(), item1->GetName()); + } + else + { + BYTE bCell1 = item1->GetCell(); + BYTE bCell2 = item2->GetCell(); + + item1->RemoveFromCharacter(); + item2->RemoveFromCharacter(); + + item1->AddToCharacter(this, TItemPos(INVENTORY, bCell2)); + item2->AddToCharacter(this, TItemPos(INVENTORY, bCell1)); + } + + return true; +} + +bool CHARACTER::UnequipItem(LPITEM item) +{ + int pos; + + if (false == CanUnequipNow(item)) + return false; + + if (item->IsDragonSoul()) + pos = GetEmptyDragonSoulInventory(item); + else + pos = GetEmptyInventory(item->GetSize()); + + // HARD CODING + if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE) + ShowAlignment(true); + + item->RemoveFromCharacter(); + if (item->IsDragonSoul()) + { + item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, pos)); + } + else + item->AddToCharacter(this, TItemPos(INVENTORY, pos)); + + CheckMaximumPoints(); + + return true; +} + +// +// @version 05/07/05 Bang2ni - Skill 1.5 ̳ +// +bool CHARACTER::EquipItem(LPITEM item, int iCandidateCell) +{ + if (item->IsExchanging()) + return false; + + if (false == item->IsEquipable()) + return false; + + if (false == CanEquipNow(item)) + return false; + + int iWearCell = item->FindEquipCell(this, iCandidateCell); + + if (iWearCell < 0) + return false; + + // 𰡸 ź ¿ νõ Ա + if (iWearCell == WEAR_BODY && IsRiding() && (item->GetVnum() >= 11901 && item->GetVnum() <= 11904)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ϴ.")); + return false; + } + + if (iWearCell != WEAR_ARROW && IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ϴ.")); + return false; + } + + if (FN_check_item_sex(this, item) == false) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʾ ϴ.")); + return false; + } + + //ű Ż 뿩 üũ + if(item->IsRideItem() && IsRiding()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ż ̿Դϴ.")); + return false; + } + + // ȭ ̿ܿ ð Ǵ ų 1.5 Ŀ ü + DWORD dwCurTime = get_dword_time(); + + if (iWearCell != WEAR_ARROW + && (dwCurTime - GetLastAttackTime() <= 1500 || dwCurTime - m_dwLastSkillTime <= 1500)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֽϴ.")); + return false; + } + + // ȥ Ư ó + if (item->IsDragonSoul()) + { + // Ÿ ȥ ̹  ִٸ . + // ȥ swap ϸ ȵ. + if(GetInventoryItem(INVENTORY_MAX_NUM + iWearCell)) + { + ChatPacket(CHAT_TYPE_INFO, "̹ ȥ ϰ ֽϴ."); + return false; + } + + if (!item->EquipTo(this, iWearCell)) + { + return false; + } + } + // ȥ ƴ. + else + { + // ִٸ, + if (GetWear(iWearCell) && !IS_SET(GetWear(iWearCell)->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + { + // ѹ Ұ. swap Ұ + if (item->GetWearFlag() == WEARABLE_ABILITY) + return false; + + if (false == SwapItem(item->GetCell(), INVENTORY_MAX_NUM + iWearCell)) + { + return false; + } + } + else + { + BYTE bOldCell = item->GetCell(); + + if (item->EquipTo(this, iWearCell)) + { + SyncQuickslot(QUICKSLOT_TYPE_ITEM, bOldCell, iWearCell); + } + } + } + + if (true == item->IsEquipped()) + { + // ĺʹ ʾƵ ð Ǵ ó. + if (-1 != item->GetProto()->cLimitRealTimeFirstUseIndex) + { + // ̶ δ Socket1 ǴѴ. (Socket1 Ƚ ) + if (0 == item->GetSocket(1)) + { + // 밡ɽð Default Limit Value ϵ, Socket0 ϵ Ѵ. ( ) + long duration = (0 != item->GetSocket(0)) ? item->GetSocket(0) : item->GetProto()->aLimits[item->GetProto()->cLimitRealTimeFirstUseIndex].lValue; + + if (0 == duration) + duration = 60 * 60 * 24 * 7; + + item->SetSocket(0, time(0) + duration); + item->StartRealTimeExpireEvent(); + } + + item->SetSocket(1, item->GetSocket(1) + 1); + } + + if (item->GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE) + ShowAlignment(false); + + const DWORD& dwVnum = item->GetVnum(); + + // 󸶴 ̺Ʈ ʽ´ (71135) Ʈ ߵ + if (true == CItemVnumHelper::IsRamadanMoonRing(dwVnum)) + { + this->EffectPacket(SE_EQUIP_RAMADAN_RING); + } + // ҷ (71136) Ʈ ߵ + else if (true == CItemVnumHelper::IsHalloweenCandy(dwVnum)) + { + this->EffectPacket(SE_EQUIP_HALLOWEEN_CANDY); + } + // ູ (71143) Ʈ ߵ + else if (true == CItemVnumHelper::IsHappinessRing(dwVnum)) + { + this->EffectPacket(SE_EQUIP_HAPPINESS_RING); + } + // ҴƮ(71145) Ʈ ߵ + else if (true == CItemVnumHelper::IsLovePendant(dwVnum)) + { + this->EffectPacket(SE_EQUIP_LOVE_PENDANT); + } + // ITEM_UNIQUE , SpecialItemGroup ǵǾ ְ, (item->GetSIGVnum() != NULL) + // + else if (ITEM_UNIQUE == item->GetType() && 0 != item->GetSIGVnum()) + { + const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(item->GetSIGVnum()); + if (NULL != pGroup) + { + const CSpecialAttrGroup* pAttrGroup = ITEM_MANAGER::instance().GetSpecialAttrGroup(pGroup->GetAttrVnum(item->GetVnum())); + if (NULL != pAttrGroup) + { + const std::string& std = pAttrGroup->m_stEffectFileName; + SpecificEffectPacket(std.c_str()); + } + } + } + + if (UNIQUE_SPECIAL_RIDE == item->GetSubType() && IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_USE)) + { + quest::CQuestManager::instance().UseItem(GetPlayerID(), item, false); + } + } + + return true; +} + +void CHARACTER::BuffOnAttr_AddBuffsFromItem(LPITEM pItem) +{ + for (int i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++) + { + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]); + if (it != m_map_buff_on_attrs.end()) + { + it->second->AddBuffFromItem(pItem); + } + } +} + +void CHARACTER::BuffOnAttr_RemoveBuffsFromItem(LPITEM pItem) +{ + for (int i = 0; i < sizeof(g_aBuffOnAttrPoints)/sizeof(g_aBuffOnAttrPoints[0]); i++) + { + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(g_aBuffOnAttrPoints[i]); + if (it != m_map_buff_on_attrs.end()) + { + it->second->RemoveBuffFromItem(pItem); + } + } +} + +void CHARACTER::BuffOnAttr_ClearAll() +{ + for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++) + { + CBuffOnAttributes* pBuff = it->second; + if (pBuff) + { + pBuff->Initialize(); + } + } +} + +void CHARACTER::BuffOnAttr_ValueChange(BYTE bType, BYTE bOldValue, BYTE bNewValue) +{ + TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.find(bType); + + if (0 == bNewValue) + { + if (m_map_buff_on_attrs.end() == it) + return; + else + it->second->Off(); + } + else if(0 == bOldValue) + { + CBuffOnAttributes* pBuff; + if (m_map_buff_on_attrs.end() == it) + { + switch (bType) + { + case POINT_ENERGY: + { + static BYTE abSlot[] = { WEAR_BODY, WEAR_HEAD, WEAR_FOOTS, WEAR_WRIST, WEAR_WEAPON, WEAR_NECK, WEAR_EAR, WEAR_SHIELD }; + static std::vector vec_slots (abSlot, abSlot + _countof(abSlot)); + pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots); + } + break; + case POINT_COSTUME_ATTR_BONUS: + { + static BYTE abSlot[] = { WEAR_COSTUME_BODY, WEAR_COSTUME_HAIR }; + static std::vector vec_slots (abSlot, abSlot + _countof(abSlot)); + pBuff = M2_NEW CBuffOnAttributes(this, bType, &vec_slots); + } + break; + default: + break; + } + m_map_buff_on_attrs.insert(TMapBuffOnAttrs::value_type(bType, pBuff)); + + } + else + pBuff = it->second; + + pBuff->On(bNewValue); + } + else + { + if (m_map_buff_on_attrs.end() == it) + return; + else + it->second->ChangeBuffValue(bNewValue); + } +} + + +LPITEM CHARACTER::FindSpecifyItem(DWORD vnum) const +{ + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + if (GetInventoryItem(i) && GetInventoryItem(i)->GetVnum() == vnum) + return GetInventoryItem(i); + + return NULL; +} + +LPITEM CHARACTER::FindItemByID(DWORD id) const +{ + for (int i=0 ; i < INVENTORY_MAX_NUM ; ++i) + { + if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id) + return GetInventoryItem(i); + } + + for (int i=BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END ; ++i) + { + if (NULL != GetInventoryItem(i) && GetInventoryItem(i)->GetID() == id) + return GetInventoryItem(i); + } + + return NULL; +} + +int CHARACTER::CountSpecifyItem(DWORD vnum) const +{ + int count = 0; + LPITEM item; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + item = GetInventoryItem(i); + if (NULL != item && item->GetVnum() == vnum) + { + // ϵ ̸ Ѿ. + if (m_pkMyShop && m_pkMyShop->IsSellingItem(item->GetID())) + { + continue; + } + else + { + count += item->GetCount(); + } + } + } + + return count; +} + +void CHARACTER::RemoveSpecifyItem(DWORD vnum, DWORD count) +{ + if (0 == count) + return; + + for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i) + { + if (NULL == GetInventoryItem(i)) + continue; + + if (GetInventoryItem(i)->GetVnum() != vnum) + continue; + + // ϵ ̸ Ѿ. ( Ǹŵɶ κ !) + if(m_pkMyShop) + { + bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID()); + if (isItemSelling) + continue; + } + + if (vnum >= 80003 && vnum <= 80007) + LogManager::instance().GoldBarLog(GetPlayerID(), GetInventoryItem(i)->GetID(), QUEST, "RemoveSpecifyItem"); + + if (count >= GetInventoryItem(i)->GetCount()) + { + count -= GetInventoryItem(i)->GetCount(); + GetInventoryItem(i)->SetCount(0); + + if (0 == count) + return; + } + else + { + GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count); + return; + } + } + + // ó ϴ. + if (count) + sys_log(0, "CHARACTER::RemoveSpecifyItem cannot remove enough item vnum %u, still remain %d", vnum, count); +} + +int CHARACTER::CountSpecifyTypeItem(BYTE type) const +{ + int count = 0; + + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM pItem = GetInventoryItem(i); + if (pItem != NULL && pItem->GetType() == type) + { + count += pItem->GetCount(); + } + } + + return count; +} + +void CHARACTER::RemoveSpecifyTypeItem(BYTE type, DWORD count) +{ + if (0 == count) + return; + + for (UINT i = 0; i < INVENTORY_MAX_NUM; ++i) + { + if (NULL == GetInventoryItem(i)) + continue; + + if (GetInventoryItem(i)->GetType() != type) + continue; + + // ϵ ̸ Ѿ. ( Ǹŵɶ κ !) + if(m_pkMyShop) + { + bool isItemSelling = m_pkMyShop->IsSellingItem(GetInventoryItem(i)->GetID()); + if (isItemSelling) + continue; + } + + if (count >= GetInventoryItem(i)->GetCount()) + { + count -= GetInventoryItem(i)->GetCount(); + GetInventoryItem(i)->SetCount(0); + + if (0 == count) + return; + } + else + { + GetInventoryItem(i)->SetCount(GetInventoryItem(i)->GetCount() - count); + return; + } + } +} + +void CHARACTER::AutoGiveItem(LPITEM item, bool longOwnerShip) +{ + if (NULL == item) + { + sys_err ("NULL point."); + return; + } + if (item->GetOwner()) + { + sys_err ("item %d 's owner exists!",item->GetID()); + return; + } + + int cell; + if (item->IsDragonSoul()) + { + cell = GetEmptyDragonSoulInventory(item); + } + else + { + cell = GetEmptyInventory (item->GetSize()); + } + + if (cell != -1) + { + if (item->IsDragonSoul()) + item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, cell)); + else + item->AddToCharacter(this, TItemPos(INVENTORY, cell)); + + LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName()); + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION) + { + TQuickslot * pSlot; + + if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE) + { + TQuickslot slot; + slot.type = QUICKSLOT_TYPE_ITEM; + slot.pos = cell; + SetQuickslot(0, slot); + } + } + } + else + { + item->AddToGround (GetMapIndex(), GetXYZ()); + item->StartDestroyEvent(); + + if (longOwnerShip) + item->SetOwnership (this, 300); + else + item->SetOwnership (this, 60); + LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName()); + } +} + +LPITEM CHARACTER::AutoGiveItem(DWORD dwItemVnum, BYTE bCount, int iRarePct, bool bMsg) +{ + TItemTable * p = ITEM_MANAGER::instance().GetTable(dwItemVnum); + + if (!p) + return NULL; + + DBManager::instance().SendMoneyLog(MONEY_LOG_DROP, dwItemVnum, bCount); + + if (p->dwFlags & ITEM_FLAG_STACKABLE && p->bType != ITEM_BLEND) + { + for (int i = 0; i < INVENTORY_MAX_NUM; ++i) + { + LPITEM item = GetInventoryItem(i); + + if (!item) + continue; + + if (item->GetVnum() == dwItemVnum && FN_check_item_socket(item)) + { + if (IS_SET(p->dwFlags, ITEM_FLAG_MAKECOUNT)) + { + if (bCount < p->alValues[1]) + bCount = p->alValues[1]; + } + + BYTE bCount2 = MIN(200 - item->GetCount(), bCount); + bCount -= bCount2; + + item->SetCount(item->GetCount() + bCount2); + + if (bCount == 0) + { + if (bMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + + return item; + } + } + } + } + + LPITEM item = ITEM_MANAGER::instance().CreateItem(dwItemVnum, bCount, 0, true); + + if (!item) + { + sys_err("cannot create item by vnum %u (name: %s)", dwItemVnum, GetName()); + return NULL; + } + + if (item->GetType() == ITEM_BLEND) + { + for (int i=0; i < INVENTORY_MAX_NUM; i++) + { + LPITEM inv_item = GetInventoryItem(i); + + if (inv_item == NULL) continue; + + if (inv_item->GetType() == ITEM_BLEND) + { + if (inv_item->GetVnum() == item->GetVnum()) + { + if (inv_item->GetSocket(0) == item->GetSocket(0) && + inv_item->GetSocket(1) == item->GetSocket(1) && + inv_item->GetSocket(2) == item->GetSocket(2) && + inv_item->GetCount() < ITEM_MAX_COUNT) + { + inv_item->SetCount(inv_item->GetCount() + item->GetCount()); + return inv_item; + } + } + } + } + } + + int iEmptyCell; + if (item->IsDragonSoul()) + { + iEmptyCell = GetEmptyDragonSoulInventory(item); + } + else + iEmptyCell = GetEmptyInventory(item->GetSize()); + + if (iEmptyCell != -1) + { + if (bMsg) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȹ: %s"), item->GetName()); + + if (item->IsDragonSoul()) + item->AddToCharacter(this, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell)); + else + item->AddToCharacter(this, TItemPos(INVENTORY, iEmptyCell)); + LogManager::instance().ItemLog(this, item, "SYSTEM", item->GetName()); + + if (item->GetType() == ITEM_USE && item->GetSubType() == USE_POTION) + { + TQuickslot * pSlot; + + if (GetQuickslot(0, &pSlot) && pSlot->type == QUICKSLOT_TYPE_NONE) + { + TQuickslot slot; + slot.type = QUICKSLOT_TYPE_ITEM; + slot.pos = iEmptyCell; + SetQuickslot(0, slot); + } + } + } + else + { + item->AddToGround(GetMapIndex(), GetXYZ()); + item->StartDestroyEvent(); + // Ƽ flag ɷִ , + // κ  ¿ Ʈ Ǹ, + // ownership (300) Ѵ. + if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_DROP)) + item->SetOwnership(this, 300); + else + item->SetOwnership(this, 60); + LogManager::instance().ItemLog(this, item, "SYSTEM_DROP", item->GetName()); + } + + sys_log(0, + "7: %d %d", dwItemVnum, bCount); + return item; +} + +bool CHARACTER::GiveItem(LPCHARACTER victim, TItemPos Cell) +{ + if (!CanHandleItem()) + return false; + + LPITEM item = GetItem(Cell); + + if (item && !item->IsExchanging()) + { + if (victim->CanReceiveItem(this, item)) + { + victim->ReceiveItem(this, item); + return true; + } + } + + return false; +} + +bool CHARACTER::CanReceiveItem(LPCHARACTER from, LPITEM item) const +{ + if (IsPC()) + return false; + + // TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX + if (DISTANCE_APPROX(GetX() - from->GetX(), GetY() - from->GetY()) > 2000) + return false; + // END_OF_TOO_LONG_DISTANCE_EXCHANGE_BUG_FIX + + switch (GetRaceNum()) + { + case fishing::CAMPFIRE_MOB: + if (item->GetType() == ITEM_FISH && + (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD)) + return true; + break; + + case fishing::FISHER_MOB: + if (item->GetType() == ITEM_ROD) + return true; + break; + + // BUILDING_NPC + case BLACKSMITH_WEAPON_MOB: + case DEVILTOWER_BLACKSMITH_WEAPON_MOB: + if (item->GetType() == ITEM_WEAPON && + item->GetRefinedVnum()) + return true; + else + return false; + break; + + case BLACKSMITH_ARMOR_MOB: + case DEVILTOWER_BLACKSMITH_ARMOR_MOB: + if (item->GetType() == ITEM_ARMOR && + (item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) && + item->GetRefinedVnum()) + return true; + else + return false; + break; + + case BLACKSMITH_ACCESSORY_MOB: + case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB: + if (item->GetType() == ITEM_ARMOR && + !(item->GetSubType() == ARMOR_BODY || item->GetSubType() == ARMOR_SHIELD || item->GetSubType() == ARMOR_HEAD) && + item->GetRefinedVnum()) + return true; + else + return false; + break; + // END_OF_BUILDING_NPC + + case BLACKSMITH_MOB: + if (item->GetRefinedVnum() && item->GetRefineSet() < 500) + { + return true; + } + else + { + return false; + } + + case BLACKSMITH2_MOB: + if (item->GetRefineSet() >= 500) + { + return true; + } + else + { + return false; + } + + case ALCHEMIST_MOB: + if (item->GetRefinedVnum()) + return true; + break; + + case 20101: + case 20102: + case 20103: + // ʱ + if (item->GetVnum() == ITEM_REVIVE_HORSE_1) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_2 || item->GetVnum() == ITEM_HORSE_FOOD_3) + { + return false; + } + break; + case 20104: + case 20105: + case 20106: + // ߱ + if (item->GetVnum() == ITEM_REVIVE_HORSE_2) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_2) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_3) + { + return false; + } + break; + case 20107: + case 20108: + case 20109: + // + if (item->GetVnum() == ITEM_REVIVE_HORSE_3) + { + if (!IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_3) + { + if (IsDead()) + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ϴ.")); + return false; + } + return true; + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || item->GetVnum() == ITEM_HORSE_FOOD_2) + { + return false; + } + break; + } + + //if (IS_SET(item->GetFlag(), ITEM_FLAG_QUEST_GIVE)) + { + return true; + } + + return false; +} + +void CHARACTER::ReceiveItem(LPCHARACTER from, LPITEM item) +{ + if (IsPC()) + return; + + switch (GetRaceNum()) + { + case fishing::CAMPFIRE_MOB: + if (item->GetType() == ITEM_FISH && (item->GetSubType() == FISH_ALIVE || item->GetSubType() == FISH_DEAD)) + fishing::Grill(from, item); + else + { + // TAKE_ITEM_BUG_FIX + from->SetQuestNPCID(GetVID()); + // END_OF_TAKE_ITEM_BUG_FIX + quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item); + } + break; + + // DEVILTOWER_NPC + case DEVILTOWER_BLACKSMITH_WEAPON_MOB: + case DEVILTOWER_BLACKSMITH_ARMOR_MOB: + case DEVILTOWER_BLACKSMITH_ACCESSORY_MOB: + if (item->GetRefinedVnum() != 0 && item->GetRefineSet() != 0 && item->GetRefineSet() < 500) + { + from->SetRefineNPC(this); + from->RefineInformation(item->GetCell(), REFINE_TYPE_MONEY_ONLY); + } + else + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + // END_OF_DEVILTOWER_NPC + + case BLACKSMITH_MOB: + case BLACKSMITH2_MOB: + case BLACKSMITH_WEAPON_MOB: + case BLACKSMITH_ARMOR_MOB: + case BLACKSMITH_ACCESSORY_MOB: + if (item->GetRefinedVnum()) + { + from->SetRefineNPC(this); + from->RefineInformation(item->GetCell(), REFINE_TYPE_NORMAL); + } + else + { + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + } + break; + + case 20101: + case 20102: + case 20103: + case 20104: + case 20105: + case 20106: + case 20107: + case 20108: + case 20109: + if (item->GetVnum() == ITEM_REVIVE_HORSE_1 || + item->GetVnum() == ITEM_REVIVE_HORSE_2 || + item->GetVnum() == ITEM_REVIVE_HORSE_3) + { + from->ReviveHorse(); + item->SetCount(item->GetCount()-1); + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʸ ־ϴ.")); + } + else if (item->GetVnum() == ITEM_HORSE_FOOD_1 || + item->GetVnum() == ITEM_HORSE_FOOD_2 || + item->GetVnum() == ITEM_HORSE_FOOD_3) + { + from->FeedHorse(); + from->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ḧ ־ϴ.")); + item->SetCount(item->GetCount()-1); + EffectPacket(SE_HPUP_RED); + } + break; + + default: + sys_log(0, "TakeItem %s %d %s", from->GetName(), GetRaceNum(), item->GetName()); + from->SetQuestNPCID(GetVID()); + quest::CQuestManager::instance().TakeItem(from->GetPlayerID(), GetRaceNum(), item); + break; + } +} + +bool CHARACTER::IsEquipUniqueItem(DWORD dwItemVnum) const +{ + { + LPITEM u = GetWear(WEAR_UNIQUE1); + + if (u && u->GetVnum() == dwItemVnum) + return true; + } + + { + LPITEM u = GetWear(WEAR_UNIQUE2); + + if (u && u->GetVnum() == dwItemVnum) + return true; + } + + // (ߺ) üũѴ. + if (dwItemVnum == UNIQUE_ITEM_RING_OF_LANGUAGE) + return IsEquipUniqueItem(UNIQUE_ITEM_RING_OF_LANGUAGE_SAMPLE); + + return false; +} + +// CHECK_UNIQUE_GROUP +bool CHARACTER::IsEquipUniqueGroup(DWORD dwGroupVnum) const +{ + { + LPITEM u = GetWear(WEAR_UNIQUE1); + + if (u && u->GetSpecialGroup() == (int) dwGroupVnum) + return true; + } + + { + LPITEM u = GetWear(WEAR_UNIQUE2); + + if (u && u->GetSpecialGroup() == (int) dwGroupVnum) + return true; + } + + return false; +} +// END_OF_CHECK_UNIQUE_GROUP + +void CHARACTER::SetRefineMode(int iAdditionalCell) +{ + m_iRefineAdditionalCell = iAdditionalCell; + m_bUnderRefine = true; +} + +void CHARACTER::ClearRefineMode() +{ + m_bUnderRefine = false; + SetRefineNPC( NULL ); +} + +bool CHARACTER::GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector &dwItemVnums, + std::vector &dwItemCounts, std::vector &item_gets, int &count) +{ + const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(dwGroupNum); + + if (!pGroup) + { + sys_err("cannot find special item group %d", dwGroupNum); + return false; + } + + std::vector idxes; + int n = pGroup->GetMultiIndex(idxes); + + bool bSuccess; + + for (int i = 0; i < n; i++) + { + bSuccess = false; + int idx = idxes[i]; + DWORD dwVnum = pGroup->GetVnum(idx); + DWORD dwCount = pGroup->GetCount(idx); + int iRarePct = pGroup->GetRarePct(idx); + LPITEM item_get = NULL; + switch (dwVnum) + { + case CSpecialItemGroup::GOLD: + PointChange(POINT_GOLD, dwCount); + LogManager::instance().CharLog(this, dwCount, "TREASURE_GOLD", ""); + + bSuccess = true; + break; + case CSpecialItemGroup::EXP: + { + PointChange(POINT_EXP, dwCount); + LogManager::instance().CharLog(this, dwCount, "TREASURE_EXP", ""); + + bSuccess = true; + } + break; + + case CSpecialItemGroup::MOB: + { + sys_log(0, "CSpecialItemGroup::MOB %d", dwCount); + int x = GetX() + number(-500, 500); + int y = GetY() + number(-500, 500); + + LPCHARACTER ch = CHARACTER_MANAGER::instance().SpawnMob(dwCount, GetMapIndex(), x, y, 0, true, -1); + if (ch) + ch->SetAggressive(); + bSuccess = true; + } + break; + case CSpecialItemGroup::SLOW: + { + sys_log(0, "CSpecialItemGroup::SLOW %d", -(int)dwCount); + AddAffect(AFFECT_SLOW, POINT_MOV_SPEED, -(int)dwCount, AFF_SLOW, 300, 0, true); + bSuccess = true; + } + break; + case CSpecialItemGroup::DRAIN_HP: + { + int iDropHP = GetMaxHP()*dwCount/100; + sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP); + iDropHP = MIN(iDropHP, GetHP()-1); + sys_log(0, "CSpecialItemGroup::DRAIN_HP %d", -iDropHP); + PointChange(POINT_HP, -iDropHP); + bSuccess = true; + } + break; + case CSpecialItemGroup::POISON: + { + AttackedByPoison(NULL); + bSuccess = true; + } + break; + + case CSpecialItemGroup::MOB_GROUP: + { + int sx = GetX() - number(300, 500); + int sy = GetY() - number(300, 500); + int ex = GetX() + number(300, 500); + int ey = GetY() + number(300, 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwCount, GetMapIndex(), sx, sy, ex, ey, NULL, true); + + bSuccess = true; + } + break; + default: + { + item_get = AutoGiveItem(dwVnum, dwCount, iRarePct); + + if (item_get) + { + bSuccess = true; + } + } + break; + } + + if (bSuccess) + { + dwItemVnums.push_back(dwVnum); + dwItemCounts.push_back(dwCount); + item_gets.push_back(item_get); + count++; + + } + else + { + return false; + } + } + return bSuccess; +} + +// NEW_HAIR_STYLE_ADD +bool CHARACTER::ItemProcess_Hair(LPITEM item, int iDestCell) +{ + if (item->CheckItemUseLevel(GetLevel()) == false) + { + // ѿ ɸ + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӹ Դϴ.")); + return false; + } + + DWORD hair = item->GetVnum(); + + switch (GetJob()) + { + case JOB_WARRIOR : + hair -= 72000; // 73001 - 72000 = 1001 ȣ + break; + + case JOB_ASSASSIN : + hair -= 71250; + break; + + case JOB_SURA : + hair -= 70500; + break; + + case JOB_SHAMAN : + hair -= 69750; + break; + + default : + return false; + break; + } + + if (hair == GetPart(PART_HAIR)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ӹ ŸϷδ ü ϴ.")); + return true; + } + + item->SetCount(item->GetCount() - 1); + + SetPart(PART_HAIR, hair); + UpdatePacket(); + + return true; +} +// END_NEW_HAIR_STYLE_ADD + +bool CHARACTER::ItemProcess_Polymorph(LPITEM item) +{ + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ а Դϴ.")); + return false; + } + + if (true == IsRiding()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а Դϴ.")); + return false; + } + + DWORD dwVnum = item->GetSocket(0); + + if (dwVnum == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߸ а Դϴ.")); + item->SetCount(item->GetCount()-1); + return false; + } + + const CMob* pMob = CMobManager::instance().Get(dwVnum); + + if (pMob == NULL) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("߸ а Դϴ.")); + item->SetCount(item->GetCount()-1); + return false; + } + + switch (item->GetVnum()) + { + case 70104 : + case 70105 : + case 70106 : + case 70107 : + case 71093 : + { + // а ó + sys_log(0, "USE_POLYMORPH_BALL PID(%d) vnum(%d)", GetPlayerID(), dwVnum); + + // üũ + int iPolymorphLevelLimit = MAX(0, 20 - GetLevel() * 3 / 10); + if (pMob->m_table.bLevel >= GetLevel() + iPolymorphLevelLimit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ʹ ͷδ ϴ.")); + return false; + } + + int iDuration = GetSkillLevel(POLYMORPH_SKILL_ID) == 0 ? 5 : (5 + (5 + GetSkillLevel(POLYMORPH_SKILL_ID)/40 * 25)); + iDuration *= 60; + + DWORD dwBonus = 0; + + if (true == LC_IsYMIR() || true == LC_IsKorea()) + { + dwBonus = GetSkillLevel(POLYMORPH_SKILL_ID) + 60; + } + else + { + dwBonus = (2 + GetSkillLevel(POLYMORPH_SKILL_ID)/40) * 100; + } + + AddAffect(AFFECT_POLYMORPH, POINT_POLYMORPH, dwVnum, AFF_POLYMORPH, iDuration, 0, true); + AddAffect(AFFECT_POLYMORPH, POINT_ATT_BONUS, dwBonus, AFF_POLYMORPH, iDuration, 0, false); + + item->SetCount(item->GetCount()-1); + } + break; + + case 50322: + { + // + + // а ó + // 0 1 2 + // а ȣ а + sys_log(0, "USE_POLYMORPH_BOOK: %s(%u) vnum(%u)", GetName(), GetPlayerID(), dwVnum); + + if (CPolymorphUtils::instance().PolymorphCharacter(this, item, pMob) == true) + { + CPolymorphUtils::instance().UpdateBookPracticeGrade(this, item); + } + else + { + } + } + break; + + default : + sys_err("POLYMORPH invalid item passed PID(%d) vnum(%d)", GetPlayerID(), item->GetOriginalVnum()); + return false; + } + + return true; +} + +bool CHARACTER::CanDoCube() const +{ + if (m_bIsObserver) return false; + if (GetShop()) return false; + if (GetMyShop()) return false; + if (m_bUnderRefine) return false; + if (IsWarping()) return false; + + return true; +} + +bool CHARACTER::UnEquipSpecialRideUniqueItem() +{ + LPITEM Unique1 = GetWear(WEAR_UNIQUE1); + LPITEM Unique2 = GetWear(WEAR_UNIQUE2); + + if( NULL != Unique1 ) + { + if( UNIQUE_GROUP_SPECIAL_RIDE == Unique1->GetSpecialGroup() ) + { + return UnequipItem(Unique1); + } + } + + if( NULL != Unique2 ) + { + if( UNIQUE_GROUP_SPECIAL_RIDE == Unique2->GetSpecialGroup() ) + { + return UnequipItem(Unique2); + } + } + + return true; +} + +void CHARACTER::AutoRecoveryItemProcess(const EAffectTypes type) +{ + if (true == IsDead() || true == IsStun()) + return; + + if (false == IsPC()) + return; + + if (AFFECT_AUTO_HP_RECOVERY != type && AFFECT_AUTO_SP_RECOVERY != type) + return; + + if (NULL != FindAffect(AFFECT_STUN)) + return; + + { + const DWORD stunSkills[] = { SKILL_TANHWAN, SKILL_GEOMPUNG, SKILL_BYEURAK, SKILL_GIGUNG }; + + for (size_t i=0 ; i < sizeof(stunSkills)/sizeof(DWORD) ; ++i) + { + const CAffect* p = FindAffect(stunSkills[i]); + + if (NULL != p && AFF_STUN == p->dwFlag) + return; + } + } + + const CAffect* pAffect = FindAffect(type); + const size_t idx_of_amount_of_used = 1; + const size_t idx_of_amount_of_full = 2; + + if (NULL != pAffect) + { + LPITEM pItem = FindItemByID(pAffect->dwFlag); + + if (NULL != pItem && true == pItem->GetSocket(0)) + { + if (false == CArenaManager::instance().IsArenaMap(GetMapIndex())) + { + const long amount_of_used = pItem->GetSocket(idx_of_amount_of_used); + const long amount_of_full = pItem->GetSocket(idx_of_amount_of_full); + + const int32_t avail = amount_of_full - amount_of_used; + + int32_t amount = 0; + + if (AFFECT_AUTO_HP_RECOVERY == type) + { + amount = GetMaxHP() - (GetHP() + GetPoint(POINT_HP_RECOVERY)); + } + else if (AFFECT_AUTO_SP_RECOVERY == type) + { + amount = GetMaxSP() - (GetSP() + GetPoint(POINT_SP_RECOVERY)); + } + + if (amount > 0) + { + if (avail > amount) + { + const int pct_of_used = amount_of_used * 100 / amount_of_full; + const int pct_of_will_used = (amount_of_used + amount) * 100 / amount_of_full; + + bool bLog = false; + // 뷮 10% α׸ + // (뷮 %, ڸ ٲ α׸ .) + if ((pct_of_will_used / 10) - (pct_of_used / 10) >= 1) + bLog = true; + pItem->SetSocket(idx_of_amount_of_used, amount_of_used + amount, bLog); + } + else + { + amount = avail; + + ITEM_MANAGER::instance().RemoveItem( pItem ); + } + + if (AFFECT_AUTO_HP_RECOVERY == type) + { + PointChange( POINT_HP_RECOVERY, amount ); + EffectPacket( SE_AUTO_HPUP ); + } + else if (AFFECT_AUTO_SP_RECOVERY == type) + { + PointChange( POINT_SP_RECOVERY, amount ); + EffectPacket( SE_AUTO_SPUP ); + } + } + } + else + { + pItem->Lock(false); + pItem->SetSocket(0, false); + RemoveAffect( const_cast(pAffect) ); + } + } + else + { + RemoveAffect( const_cast(pAffect) ); + } + } +} + +bool CHARACTER::IsValidItemPosition(TItemPos Pos) const +{ + BYTE window_type = Pos.window_type; + WORD cell = Pos.cell; + + switch (window_type) + { + case RESERVED_WINDOW: + return false; + + case INVENTORY: + case EQUIPMENT: + return cell < (INVENTORY_AND_EQUIP_SLOT_MAX); + + case DRAGON_SOUL_INVENTORY: + return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM); + + case SAFEBOX: + if (NULL != m_pkSafebox) + return m_pkSafebox->IsValidPosition(cell); + else + return false; + + case MALL: + if (NULL != m_pkMall) + return m_pkMall->IsValidPosition(cell); + else + return false; + default: + return false; + } +} + + +// Ƽ ũ.. exp true msg ϰ return false ϴ ũ (Ϲ verify 뵵 return ణ ݴ ̸ 򰥸 ְڴ..) +#define VERIFY_MSG(exp, msg) \ + if (true == (exp)) { \ + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(msg)); \ + return false; \ + } + + +/// ij ¸ ־ item ִ Ȯϰ, Ұ ϴٸ ijͿ ˷ִ Լ +bool CHARACTER::CanEquipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/ +{ + const TItemTable* itemTable = item->GetProto(); + BYTE itemType = item->GetType(); + BYTE itemSubType = item->GetSubType(); + + switch (GetJob()) + { + case JOB_WARRIOR: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR) + return false; + break; + + case JOB_ASSASSIN: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN) + return false; + break; + + case JOB_SHAMAN: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN) + return false; + break; + + case JOB_SURA: + if (item->GetAntiFlag() & ITEM_ANTIFLAG_SURA) + return false; + break; + } + + for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i) + { + long limit = itemTable->aLimits[i].lValue; + switch (itemTable->aLimits[i].bType) + { + case LIMIT_LEVEL: + if (GetLevel() < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + break; + + case LIMIT_STR: + if (GetPoint(POINT_ST) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٷ ϴ.")); + return false; + } + break; + + case LIMIT_INT: + if (GetPoint(POINT_IQ) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ.")); + return false; + } + break; + + case LIMIT_DEX: + if (GetPoint(POINT_DX) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ø ϴ.")); + return false; + } + break; + + case LIMIT_CON: + if (GetPoint(POINT_HT) < limit) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ü ϴ.")); + return false; + } + break; + } + } + + if (item->GetWearFlag() & WEARABLE_UNIQUE) + { + if ((GetWear(WEAR_UNIQUE1) && GetWear(WEAR_UNIQUE1)->IsSameSpecialGroup(item)) || + (GetWear(WEAR_UNIQUE2) && GetWear(WEAR_UNIQUE2)->IsSameSpecialGroup(item))) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ũ ÿ ϴ.")); + return false; + } + + if (marriage::CManager::instance().IsMarriageUniqueItem(item->GetVnum()) && + !marriage::CManager::instance().IsMarried(GetPlayerID())) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȥ ¿ ϴ.")); + return false; + } + + } + + return true; +} + +/// ij ¸ item ִ Ȯϰ, Ұ ϴٸ ijͿ ˷ִ Լ +bool CHARACTER::CanUnequipNow(const LPITEM item, const TItemPos& srcCell, const TItemPos& destCell) /*const*/ +{ + + if (ITEM_BELT == item->GetType()) + VERIFY_MSG(CBeltInventoryHelper::IsExistItemInBeltInventory(this), "Ʈ κ丮 ϸ ϴ."); + + // + if (IS_SET(item->GetFlag(), ITEM_FLAG_IRREMOVABLE)) + return false; + + // unequip κ丮 ű ڸ ִ Ȯ + { + int pos = -1; + + if (item->IsDragonSoul()) + pos = GetEmptyDragonSoulInventory(item); + else + pos = GetEmptyInventory(item->GetSize()); + + VERIFY_MSG( -1 == pos, "ǰ ϴ." ); + } + + + return true; +} diff --git a/game/src/char_manager.cpp b/game/src/char_manager.cpp new file mode 100644 index 0000000..001c164 --- /dev/null +++ b/game/src/char_manager.cpp @@ -0,0 +1,1122 @@ +#include "stdafx.h" +#include "constants.h" +#include "utils.h" +#include "desc.h" +#include "char.h" +#include "char_manager.h" +#include "mob_manager.h" +#include "party.h" +#include "regen.h" +#include "p2p.h" +#include "dungeon.h" +#include "db.h" +#include "config.h" +#include "xmas_event.h" +#include "questmanager.h" +#include "questlua.h" +#include "locale_service.h" +#include "XTrapManager.h" + +#ifndef __GNUC__ +#include +#endif + +CHARACTER_MANAGER::CHARACTER_MANAGER() : + m_iVIDCount(0), + m_pkChrSelectedStone(NULL), + m_bUsePendingDestroy(false) +{ + RegisterRaceNum(xmas::MOB_XMAS_FIRWORK_SELLER_VNUM); + RegisterRaceNum(xmas::MOB_SANTA_VNUM); + RegisterRaceNum(xmas::MOB_XMAS_TREE_VNUM); + + m_iMobItemRate = 100; + m_iMobDamageRate = 100; + m_iMobGoldAmountRate = 100; + m_iMobGoldDropRate = 100; + m_iMobExpRate = 100; + + m_iMobItemRatePremium = 100; + m_iMobGoldAmountRatePremium = 100; + m_iMobGoldDropRatePremium = 100; + m_iMobExpRatePremium = 100; + + m_iUserDamageRate = 100; + m_iUserDamageRatePremium = 100; +} + +CHARACTER_MANAGER::~CHARACTER_MANAGER() +{ + Destroy(); +} + +void CHARACTER_MANAGER::Destroy() +{ + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.begin(); + while (it != m_map_pkChrByVID.end()) { + LPCHARACTER ch = it->second; + M2_DESTROY_CHARACTER(ch); // m_map_pkChrByVID is changed here + it = m_map_pkChrByVID.begin(); + } +} + +void CHARACTER_MANAGER::GracefulShutdown() +{ + NAME_MAP::iterator it = m_map_pkPCChr.begin(); + + while (it != m_map_pkPCChr.end()) + (it++)->second->Disconnect("GracefulShutdown"); +} + +DWORD CHARACTER_MANAGER::AllocVID() +{ + ++m_iVIDCount; + return m_iVIDCount; +} + +LPCHARACTER CHARACTER_MANAGER::CreateCharacter(const char * name, DWORD dwPID) +{ + DWORD dwVID = AllocVID(); + +#ifdef M2_USE_POOL + LPCHARACTER ch = pool_.Construct(); +#else + LPCHARACTER ch = M2_NEW CHARACTER; +#endif + ch->Create(name, dwVID, dwPID ? true : false); + + m_map_pkChrByVID.insert(std::make_pair(dwVID, ch)); + + if (dwPID) + { + char szName[CHARACTER_NAME_MAX_LEN + 1]; + str_lower(name, szName, sizeof(szName)); + + m_map_pkPCChr.insert(NAME_MAP::value_type(szName, ch)); + m_map_pkChrByPID.insert(std::make_pair(dwPID, ch)); + } + + return (ch); +} + +#ifndef DEBUG_ALLOC +void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch) +#else +void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch, const char* file, size_t line) +#endif +{ + if (!ch) + return; + + // Check whether it has been already deleted or not. + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(ch->GetVID()); + if (it == m_map_pkChrByVID.end()) { + sys_err("[CHARACTER_MANAGER::DestroyCharacter] %d not found", (long)(ch->GetVID())); + return; // prevent duplicated destrunction + } + + // Ҽӵ ʹ ϵ. + if (ch->IsNPC() && !ch->IsPet() && ch->GetRider() == NULL) + { + if (ch->GetDungeon()) + { + ch->GetDungeon()->DeadCharacter(ch); + } + } + + if (m_bUsePendingDestroy) + { + m_set_pkChrPendingDestroy.insert(ch); + return; + } + + m_map_pkChrByVID.erase(it); + + if (true == ch->IsPC()) + { + char szName[CHARACTER_NAME_MAX_LEN + 1]; + + str_lower(ch->GetName(), szName, sizeof(szName)); + + NAME_MAP::iterator it = m_map_pkPCChr.find(szName); + + if (m_map_pkPCChr.end() != it) + m_map_pkPCChr.erase(it); + } + + if (0 != ch->GetPlayerID()) + { + itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(ch->GetPlayerID()); + + if (m_map_pkChrByPID.end() != it) + { + m_map_pkChrByPID.erase(it); + } + } + + UnregisterRaceNumMap(ch); + + RemoveFromStateList(ch); + +#ifdef M2_USE_POOL + pool_.Destroy(ch); +#else +#ifndef DEBUG_ALLOC + M2_DELETE(ch); +#else + M2_DELETE_EX(ch, file, line); +#endif +#endif +} + +LPCHARACTER CHARACTER_MANAGER::Find(DWORD dwVID) +{ + itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(dwVID); + + if (m_map_pkChrByVID.end() == it) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && dwVID != (DWORD)found->GetVID()) { + sys_err("[CHARACTER_MANAGER::Find] %u != %u", dwVID, (DWORD)found->GetVID()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::Find(const VID & vid) +{ + LPCHARACTER tch = Find((DWORD) vid); + + if (!tch || tch->GetVID() != vid) + return NULL; + + return tch; +} + +LPCHARACTER CHARACTER_MANAGER::FindByPID(DWORD dwPID) +{ + itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(dwPID); + + if (m_map_pkChrByPID.end() == it) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && dwPID != found->GetPlayerID()) { + sys_err("[CHARACTER_MANAGER::FindByPID] %u != %u", dwPID, found->GetPlayerID()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::FindPC(const char * name) +{ + char szName[CHARACTER_NAME_MAX_LEN + 1]; + str_lower(name, szName, sizeof(szName)); + NAME_MAP::iterator it = m_map_pkPCChr.find(szName); + + if (it == m_map_pkPCChr.end()) + return NULL; + + // Added sanity check + LPCHARACTER found = it->second; + if (found != NULL && strncasecmp(szName, found->GetName(), CHARACTER_NAME_MAX_LEN) != 0) { + sys_err("[CHARACTER_MANAGER::FindPC] %s != %s", name, found->GetName()); + return NULL; + } + return found; +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex) +{ + // ֱ ְ + { + if (dwVnum == 5001 && !quest::CQuestManager::instance().GetEventFlag("japan_regen")) + { + sys_log(1, "WAEGU[5001] regen disabled."); + return NULL; + } + } + + // ¸ ְ + { + if (dwVnum == 5002 && !quest::CQuestManager::instance().GetEventFlag("newyear_mob")) + { + sys_log(1, "HAETAE (new-year-mob) [5002] regen disabled."); + return NULL; + } + } + + // ̺Ʈ + { + if (dwVnum == 5004 && !quest::CQuestManager::instance().GetEventFlag("independence_day")) + { + sys_log(1, "INDEPENDECE DAY [5004] regen disabled."); + return NULL; + } + } + + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + + if (!pkMob) + { + sys_err("no mob data for vnum %u", dwVnum); + return NULL; + } + + if (!map_allow_find(lMapIndex)) + { + sys_err("not allowed map %u", lMapIndex); + return NULL; + } + + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + if (pkSectreeMap == NULL) { + return NULL; + } + + int i; + long x, y; + for (i=0; i<2000; i++) + { + x = number(1, (pkSectreeMap->m_setting.iWidth / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseX; + y = number(1, (pkSectreeMap->m_setting.iHeight / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseY; + //LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + LPSECTREE tree = pkSectreeMap->Find(x, y); + + if (!tree) + continue; + + DWORD dwAttr = tree->GetAttribute(x, y); + + if (IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT)) + continue; + + if (IS_SET(dwAttr, ATTR_BANPK)) + continue; + + break; + } + + if (i == 2000) + { + sys_err("cannot find valid location"); + return NULL; + } + + LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!sectree) + { + sys_log(0, "SpawnMobRandomPosition: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex); + return NULL; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName); + + if (!ch) + { + sys_log(0, "SpawnMobRandomPosition: cannot create new character"); + return NULL; + } + + ch->SetProto(pkMob); + + // if mob is npc with no empire assigned, assign to empire of map + if (pkMob->m_table.bType == CHAR_TYPE_NPC) + if (ch->GetEmpire() == 0) + ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex)); + + ch->SetRotation(number(0, 360)); + + if (!ch->Show(lMapIndex, x, y, 0, false)) + { + M2_DESTROY_CHARACTER(ch); + sys_err(0, "SpawnMobRandomPosition: cannot show monster"); + return NULL; + } + + char buf[512+1]; + long local_x = x - pkSectreeMap->m_setting.iBaseX; + long local_y = y - pkSectreeMap->m_setting.iBaseY; + snprintf(buf, sizeof(buf), "spawn %s[%d] random position at %ld %ld %ld %ld (time: %d)", ch->GetName(), dwVnum, x, y, local_x, local_y, get_global_time()); + + if (test_server) + SendNotice(buf); + + sys_log(0, buf); + return (ch); +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion, int iRot, bool bShow) +{ + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + if (!pkMob) + { + sys_err("SpawnMob: no mob data for vnum %u", dwVnum); + return NULL; + } + + if (!(pkMob->m_table.bType == CHAR_TYPE_NPC || pkMob->m_table.bType == CHAR_TYPE_WARP || pkMob->m_table.bType == CHAR_TYPE_GOTO) || mining::IsVeinOfOre (dwVnum)) + { + LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!tree) + { + sys_log(0, "no sectree for spawn at %d %d mobvnum %d mapindex %d", x, y, dwVnum, lMapIndex); + return NULL; + } + + DWORD dwAttr = tree->GetAttribute(x, y); + + bool is_set = false; + + if ( mining::IsVeinOfOre (dwVnum) ) is_set = IS_SET(dwAttr, ATTR_BLOCK); + else is_set = IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT); + + if ( is_set ) + { + // SPAWN_BLOCK_LOG + static bool s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log"); + static DWORD s_nextTime=get_global_time()+10000; + + DWORD curTime=get_global_time(); + + if (curTime>s_nextTime) + { + s_nextTime=curTime; + s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log"); + + } + + if (s_isLog) + sys_log(0, "SpawnMob: BLOCKED position for spawn %s %u at %d %d (attr %u)", pkMob->m_table.szName, dwVnum, x, y, dwAttr); + // END_OF_SPAWN_BLOCK_LOG + return NULL; + } + + if (IS_SET(dwAttr, ATTR_BANPK)) + { + sys_log(0, "SpawnMob: BAN_PK position for mob spawn %s %u at %d %d", pkMob->m_table.szName, dwVnum, x, y); + return NULL; + } + } + + LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y); + + if (!sectree) + { + sys_log(0, "SpawnMob: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex); + return NULL; + } + + LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName); + + if (!ch) + { + sys_log(0, "SpawnMob: cannot create new character"); + return NULL; + } + + if (iRot == -1) + iRot = number(0, 360); + + ch->SetProto(pkMob); + + // if mob is npc with no empire assigned, assign to empire of map + if (pkMob->m_table.bType == CHAR_TYPE_NPC) + if (ch->GetEmpire() == 0) + ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex)); + + ch->SetRotation(iRot); + + if (bShow && !ch->Show(lMapIndex, x, y, z, bSpawnMotion)) + { + M2_DESTROY_CHARACTER(ch); + sys_log(0, "SpawnMob: cannot show monster"); + return NULL; + } + + return (ch); +} + +LPCHARACTER CHARACTER_MANAGER::SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException, bool bSpawnMotion, bool bAggressive ) +{ + const CMob * pkMob = CMobManager::instance().Get(dwVnum); + + if (!pkMob) + return NULL; + + if (pkMob->m_table.bType == CHAR_TYPE_STONE) // SPAWN ִ. + bSpawnMotion = true; + + int i = 16; + + while (i--) + { + int x = number(sx, ex); + int y = number(sy, ey); + /* + if (bIsException) + if (is_regen_exception(x, y)) + continue; + */ + LPCHARACTER ch = SpawnMob(dwVnum, lMapIndex, x, y, 0, bSpawnMotion); + + if (ch) + { + sys_log(1, "MOB_SPAWN: %s(%d) %dx%d", ch->GetName(), (DWORD) ch->GetVID(), ch->GetX(), ch->GetY()); + if ( bAggressive ) + ch->SetAggressive(); + return (ch); + } + } + + return NULL; +} + +void CHARACTER_MANAGER::SelectStone(LPCHARACTER pkChr) +{ + m_pkChrSelectedStone = pkChr; +} + +bool CHARACTER_MANAGER::SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen, bool bAggressive_) +{ + CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum); + + if (!pkGroup) + { + sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex); + return false; + } + + LPCHARACTER pkChrMaster = NULL; + LPPARTY pkParty = NULL; + + const std::vector & c_rdwMembers = pkGroup->GetMemberVector(); + + bool bSpawnedByStone = false; + bool bAggressive = bAggressive_; + + if (m_pkChrSelectedStone) + { + bSpawnedByStone = true; + if (m_pkChrSelectedStone->GetDungeon()) + bAggressive = true; + } + + for (DWORD i = 0; i < c_rdwMembers.size(); ++i) + { + LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone); + + if (!tch) + { + if (i == 0) // Ͱ 쿡 ׳ + return false; + + continue; + } + + sx = tch->GetX() - number(300, 500); + sy = tch->GetY() - number(300, 500); + ex = tch->GetX() + number(300, 500); + ey = tch->GetY() + number(300, 500); + + if (m_pkChrSelectedStone) + tch->SetStone(m_pkChrSelectedStone); + else if (pkParty) + { + pkParty->Join(tch->GetVID()); + pkParty->Link(tch); + } + else if (!pkChrMaster) + { + pkChrMaster = tch; + pkChrMaster->SetRegen(pkRegen); + + pkParty = CPartyManager::instance().CreateParty(pkChrMaster); + } + if (bAggressive) + tch->SetAggressive(); + + if (tch->Goto(tx, ty)) + tch->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + + return true; +} + +bool CHARACTER_MANAGER::SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon) +{ + const DWORD dwGroupID = CMobManager::Instance().GetGroupFromGroupGroup(dwVnum); + + if( dwGroupID != 0 ) + { + return SpawnGroup(dwGroupID, lMapIndex, sx, sy, ex, ey, pkRegen, bAggressive_, pDungeon); + } + else + { + sys_err( "NOT_EXIST_GROUP_GROUP_VNUM(%u) MAP(%ld)", dwVnum, lMapIndex ); + return false; + } +} + +LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon) +{ + CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum); + + if (!pkGroup) + { + sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex); + return NULL; + } + + LPCHARACTER pkChrMaster = NULL; + LPPARTY pkParty = NULL; + + const std::vector & c_rdwMembers = pkGroup->GetMemberVector(); + + bool bSpawnedByStone = false; + bool bAggressive = bAggressive_; + + if (m_pkChrSelectedStone) + { + bSpawnedByStone = true; + + if (m_pkChrSelectedStone->GetDungeon()) + bAggressive = true; + } + + LPCHARACTER chLeader = NULL; + + for (DWORD i = 0; i < c_rdwMembers.size(); ++i) + { + LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone); + + if (!tch) + { + if (i == 0) // Ͱ 쿡 ׳ + return NULL; + + continue; + } + + if (i == 0) + chLeader = tch; + + tch->SetDungeon(pDungeon); + + sx = tch->GetX() - number(300, 500); + sy = tch->GetY() - number(300, 500); + ex = tch->GetX() + number(300, 500); + ey = tch->GetY() + number(300, 500); + + if (m_pkChrSelectedStone) + tch->SetStone(m_pkChrSelectedStone); + else if (pkParty) + { + pkParty->Join(tch->GetVID()); + pkParty->Link(tch); + } + else if (!pkChrMaster) + { + pkChrMaster = tch; + pkChrMaster->SetRegen(pkRegen); + + pkParty = CPartyManager::instance().CreateParty(pkChrMaster); + } + + if (bAggressive) + tch->SetAggressive(); + } + + return chLeader; +} + +struct FuncUpdateAndResetChatCounter +{ + void operator () (LPCHARACTER ch) + { + ch->ResetChatCounter(); + ch->CFSM::Update(); + } +}; + +void CHARACTER_MANAGER::Update(int iPulse) +{ + using namespace std; +#ifdef __GNUC__ + using namespace __gnu_cxx; +#endif + + BeginPendingDestroy(); + + // PC ij Ʈ + { + if (!m_map_pkPCChr.empty()) + { + // ̳ + CHARACTER_VECTOR v; + v.reserve(m_map_pkPCChr.size()); +#ifdef __GNUC__ + transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), select2nd()); +#else + transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), boost::bind(&NAME_MAP::value_type::second, _1)); +#endif + + if (0 == (iPulse % PASSES_PER_SEC(5))) + { + FuncUpdateAndResetChatCounter f; + for_each(v.begin(), v.end(), f); + } + else + { + //for_each(v.begin(), v.end(), mem_fun(&CFSM::Update)); + for_each(v.begin(), v.end(), bind2nd(mem_fun(&CHARACTER::UpdateCharacter), iPulse)); + } + } + +// for_each_pc(bind2nd(mem_fun(&CHARACTER::UpdateCharacter), iPulse)); + } + + // Ʈ + { + if (!m_set_pkChrState.empty()) + { + CHARACTER_VECTOR v; + v.reserve(m_set_pkChrState.size()); +#ifdef __GNUC__ + transform(m_set_pkChrState.begin(), m_set_pkChrState.end(), back_inserter(v), identity()); +#else + v.insert(v.end(), m_set_pkChrState.begin(), m_set_pkChrState.end()); +#endif + for_each(v.begin(), v.end(), bind2nd(mem_fun(&CHARACTER::UpdateStateMachine), iPulse)); + } + } + + // Ÿ Ʈ + { + CharacterVectorInteractor i; + + if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(xmas::MOB_SANTA_VNUM, i)) + { + for_each(i.begin(), i.end(), + bind2nd(mem_fun(&CHARACTER::UpdateStateMachine), iPulse)); + } + } + + // 1ð ѹ + if (0 == (iPulse % PASSES_PER_SEC(3600))) + { + for (itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.begin(); it != m_map_dwMobKillCount.end(); ++it) + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second); + +#ifdef _USE_SERVER_KEY_ + extern bool Metin2Server_IsInvalid(); + extern bool g_bShutdown; + if (Metin2Server_IsInvalid()) + { + g_bShutdown = true; + } +#endif + + m_map_dwMobKillCount.clear(); + } + + // ׽Ʈ 60ʸ ij + if (test_server && 0 == (iPulse % PASSES_PER_SEC(60))) + sys_log(0, "CHARACTER COUNT vid %zu pid %zu", m_map_pkChrByVID.size(), m_map_pkChrByPID.size()); + + // DestroyCharacter ϱ + FlushPendingDestroy(); +} + +void CHARACTER_MANAGER::ProcessDelayedSave() +{ + CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.begin(); + + while (it != m_set_pkChrForDelayedSave.end()) + { + LPCHARACTER pkChr = *it++; + pkChr->SaveReal(); + } + + m_set_pkChrForDelayedSave.clear(); +} + +bool CHARACTER_MANAGER::AddToStateList(LPCHARACTER ch) +{ + assert(ch != NULL); + + CHARACTER_SET::iterator it = m_set_pkChrState.find(ch); + + if (it == m_set_pkChrState.end()) + { + m_set_pkChrState.insert(ch); + return true; + } + + return false; +} + +void CHARACTER_MANAGER::RemoveFromStateList(LPCHARACTER ch) +{ + CHARACTER_SET::iterator it = m_set_pkChrState.find(ch); + + if (it != m_set_pkChrState.end()) + { + //sys_log(0, "RemoveFromStateList %p", ch); + m_set_pkChrState.erase(it); + } +} + +void CHARACTER_MANAGER::DelayedSave(LPCHARACTER ch) +{ + m_set_pkChrForDelayedSave.insert(ch); +} + +bool CHARACTER_MANAGER::FlushDelayedSave(LPCHARACTER ch) +{ + CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.find(ch); + + if (it == m_set_pkChrForDelayedSave.end()) + return false; + + m_set_pkChrForDelayedSave.erase(it); + ch->SaveReal(); + return true; +} + +void CHARACTER_MANAGER::RegisterForMonsterLog(LPCHARACTER ch) +{ + m_set_pkChrMonsterLog.insert(ch); +} + +void CHARACTER_MANAGER::UnregisterForMonsterLog(LPCHARACTER ch) +{ + m_set_pkChrMonsterLog.erase(ch); +} + +void CHARACTER_MANAGER::PacketMonsterLog(LPCHARACTER ch, const void* buf, int size) +{ + itertype(m_set_pkChrMonsterLog) it; + + for (it = m_set_pkChrMonsterLog.begin(); it!=m_set_pkChrMonsterLog.end();++it) + { + LPCHARACTER c = *it; + + if (ch && DISTANCE_APPROX(c->GetX()-ch->GetX(), c->GetY()-ch->GetY())>6000) + continue; + + LPDESC d = c->GetDesc(); + + if (d) + d->Packet(buf, size); + } +} + +void CHARACTER_MANAGER::KillLog(DWORD dwVnum) +{ + const DWORD SEND_LIMIT = 10000; + + itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.find(dwVnum); + + if (it == m_map_dwMobKillCount.end()) + m_map_dwMobKillCount.insert(std::make_pair(dwVnum, 1)); + else + { + ++it->second; + + if (it->second > SEND_LIMIT) + { + DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second); + m_map_dwMobKillCount.erase(it); + } + } +} + +void CHARACTER_MANAGER::RegisterRaceNum(DWORD dwVnum) +{ + m_set_dwRegisteredRaceNum.insert(dwVnum); +} + +void CHARACTER_MANAGER::RegisterRaceNumMap(LPCHARACTER ch) +{ + DWORD dwVnum = ch->GetRaceNum(); + + if (m_set_dwRegisteredRaceNum.find(dwVnum) != m_set_dwRegisteredRaceNum.end()) // ϵ ȣ ̸ + { + sys_log(0, "RegisterRaceNumMap %s %u", ch->GetName(), dwVnum); + m_map_pkChrByRaceNum[dwVnum].insert(ch); + } +} + +void CHARACTER_MANAGER::UnregisterRaceNumMap(LPCHARACTER ch) +{ + DWORD dwVnum = ch->GetRaceNum(); + + itertype(m_map_pkChrByRaceNum) it = m_map_pkChrByRaceNum.find(dwVnum); + + if (it != m_map_pkChrByRaceNum.end()) + it->second.erase(ch); +} + +bool CHARACTER_MANAGER::GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i) +{ + std::map::iterator it = m_map_pkChrByRaceNum.find(dwRaceNum); + + if (it == m_map_pkChrByRaceNum.end()) + return false; + + // ̳ + i = it->second; + return true; +} + +#define FIND_JOB_WARRIOR_0 (1 << 3) +#define FIND_JOB_WARRIOR_1 (1 << 4) +#define FIND_JOB_WARRIOR_2 (1 << 5) +#define FIND_JOB_WARRIOR (FIND_JOB_WARRIOR_0 | FIND_JOB_WARRIOR_1 | FIND_JOB_WARRIOR_2) +#define FIND_JOB_ASSASSIN_0 (1 << 6) +#define FIND_JOB_ASSASSIN_1 (1 << 7) +#define FIND_JOB_ASSASSIN_2 (1 << 8) +#define FIND_JOB_ASSASSIN (FIND_JOB_ASSASSIN_0 | FIND_JOB_ASSASSIN_1 | FIND_JOB_ASSASSIN_2) +#define FIND_JOB_SURA_0 (1 << 9) +#define FIND_JOB_SURA_1 (1 << 10) +#define FIND_JOB_SURA_2 (1 << 11) +#define FIND_JOB_SURA (FIND_JOB_SURA_0 | FIND_JOB_SURA_1 | FIND_JOB_SURA_2) +#define FIND_JOB_SHAMAN_0 (1 << 12) +#define FIND_JOB_SHAMAN_1 (1 << 13) +#define FIND_JOB_SHAMAN_2 (1 << 14) +#define FIND_JOB_SHAMAN (FIND_JOB_SHAMAN_0 | FIND_JOB_SHAMAN_1 | FIND_JOB_SHAMAN_2) + +// +// (job+1)*3+(skill_group) +// +LPCHARACTER CHARACTER_MANAGER::FindSpecifyPC(unsigned int uiJobFlag, long lMapIndex, LPCHARACTER except, int iMinLevel, int iMaxLevel) +{ + LPCHARACTER chFind = NULL; + itertype(m_map_pkChrByPID) it; + int n = 0; + + for (it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it) + { + LPCHARACTER ch = it->second; + + if (ch == except) + continue; + + if (ch->GetLevel() < iMinLevel) + continue; + + if (ch->GetLevel() > iMaxLevel) + continue; + + if (ch->GetMapIndex() != lMapIndex) + continue; + + if (uiJobFlag) + { + unsigned int uiChrJob = (1 << ((ch->GetJob() + 1) * 3 + ch->GetSkillGroup())); + + if (!IS_SET(uiJobFlag, uiChrJob)) + continue; + } + + if (!chFind || number(1, ++n) == 1) + chFind = ch; + } + + return chFind; +} + +int CHARACTER_MANAGER::GetMobItemRate(LPCHARACTER ch) +{ + //PREVENT_TOXICATION_FOR_CHINA + if ( LC_IsNewCIBN() ) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0) + return m_iMobItemRatePremium/2; + return m_iMobItemRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0) + return m_iMobItemRatePremium; + return m_iMobItemRate; +} + +int CHARACTER_MANAGER::GetMobDamageRate(LPCHARACTER ch) +{ + return m_iMobDamageRate; +} + +int CHARACTER_MANAGER::GetMobGoldAmountRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobGoldAmountRate; + + //PREVENT_TOXICATION_FOR_CHINA + if ( LC_IsNewCIBN() ) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldAmountRatePremium/2; + return m_iMobGoldAmountRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldAmountRatePremium; + return m_iMobGoldAmountRate; +} + +int CHARACTER_MANAGER::GetMobGoldDropRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobGoldDropRate; + + //PREVENT_TOXICATION_FOR_CHINA + if ( LC_IsNewCIBN() ) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldDropRatePremium/2; + return m_iMobGoldDropRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + //END_PREVENT_TOXICATION_FOR_CHINA + + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0) + return m_iMobGoldDropRatePremium; + return m_iMobGoldDropRate; +} + +int CHARACTER_MANAGER::GetMobExpRate(LPCHARACTER ch) +{ + if ( !ch ) + return m_iMobExpRate; + + if ( LC_IsNewCIBN() ) + { + if ( ch->IsOverTime( OT_3HOUR ) ) + { + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iMobExpRatePremium/2; + return m_iMobExpRate/2; + } + else if ( ch->IsOverTime( OT_5HOUR ) ) + { + return 0; + } + } + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iMobExpRatePremium; + return m_iMobExpRate; +} + +int CHARACTER_MANAGER::GetUserDamageRate(LPCHARACTER ch) +{ + if (!ch) + return m_iUserDamageRate; + + if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) + return m_iUserDamageRatePremium; + + return m_iUserDamageRate; +} + +void CHARACTER_MANAGER::SendScriptToMap(long lMapIndex, const std::string & s) +{ + LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + + if (NULL == pSecMap) + return; + + struct packet_script p; + + p.header = HEADER_GC_SCRIPT; + p.skin = 1; + p.src_size = s.size(); + + quest::FSendPacket f; + p.size = p.src_size + sizeof(struct packet_script); + f.buf.write(&p, sizeof(struct packet_script)); + f.buf.write(&s[0], s.size()); + + pSecMap->for_each(f); +} + +bool CHARACTER_MANAGER::BeginPendingDestroy() +{ + // Begin Ŀ Begin ϴ 쿡 Flush ʴ + // ̹ ۵Ǿ false ó + if (m_bUsePendingDestroy) + return false; + + m_bUsePendingDestroy = true; + return true; +} + +void CHARACTER_MANAGER::FlushPendingDestroy() +{ + using namespace std; + + m_bUsePendingDestroy = false; // ÷׸ ؾ Destroy ó + + if (!m_set_pkChrPendingDestroy.empty()) + { + sys_log(0, "FlushPendingDestroy size %d", m_set_pkChrPendingDestroy.size()); + + CHARACTER_SET::iterator it = m_set_pkChrPendingDestroy.begin(), + end = m_set_pkChrPendingDestroy.end(); + for ( ; it != end; ++it) { + M2_DESTROY_CHARACTER(*it); + } + + m_set_pkChrPendingDestroy.clear(); + } +} + +CharacterVectorInteractor::CharacterVectorInteractor(const CHARACTER_SET & r) +{ + using namespace std; +#ifdef __GNUC__ + using namespace __gnu_cxx; +#endif + + reserve(r.size()); +#ifdef __GNUC__ + transform(r.begin(), r.end(), back_inserter(*this), identity()); +#else + insert(end(), r.begin(), r.end()); +#endif + + if (CHARACTER_MANAGER::instance().BeginPendingDestroy()) + m_bMyBegin = true; +} + +CharacterVectorInteractor::~CharacterVectorInteractor() +{ + if (m_bMyBegin) + CHARACTER_MANAGER::instance().FlushPendingDestroy(); +} + diff --git a/game/src/char_manager.h b/game/src/char_manager.h new file mode 100644 index 0000000..26a4e05 --- /dev/null +++ b/game/src/char_manager.h @@ -0,0 +1,175 @@ +#ifndef __INC_METIN_II_GAME_CHARACTER_MANAGER_H__ +#define __INC_METIN_II_GAME_CHARACTER_MANAGER_H__ + +#ifdef M2_USE_POOL +#include "pool.h" +#endif + +#include "../../common/stl.h" +#include "../../common/length.h" + +#include "vid.h" + +class CDungeon; +class CHARACTER; +class CharacterVectorInteractor; + +class CHARACTER_MANAGER : public singleton +{ + public: + typedef TR1_NS::unordered_map NAME_MAP; + + CHARACTER_MANAGER(); + virtual ~CHARACTER_MANAGER(); + + void Destroy(); + + void GracefulShutdown(); // ˴ٿ . PC Ű Destroy Ѵ. + + DWORD AllocVID(); + + LPCHARACTER CreateCharacter(const char * name, DWORD dwPID = 0); +#ifndef DEBUG_ALLOC + void DestroyCharacter(LPCHARACTER ch); +#else + void DestroyCharacter(LPCHARACTER ch, const char* file, size_t line); +#endif + + void Update(int iPulse); + + LPCHARACTER SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion = false, int iRot = -1, bool bShow = true); + LPCHARACTER SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException=false, bool bSpawnMotion = false , bool bAggressive = false); + LPCHARACTER SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL); + bool SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL); + bool SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen = NULL, bool bAggressive_ = false); + LPCHARACTER SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex); + + void SelectStone(LPCHARACTER pkChrStone); + + NAME_MAP & GetPCMap() { return m_map_pkPCChr; } + + LPCHARACTER Find(DWORD dwVID); + LPCHARACTER Find(const VID & vid); + LPCHARACTER FindPC(const char * name); + LPCHARACTER FindByPID(DWORD dwPID); + + bool AddToStateList(LPCHARACTER ch); + void RemoveFromStateList(LPCHARACTER ch); + + // DelayedSave:  ƾ ؾ ϸ + // ʹ Ƿ " Ѵ" ǥø صΰ + // (: 1 frame) Ŀ Ų. + void DelayedSave(LPCHARACTER ch); + bool FlushDelayedSave(LPCHARACTER ch); // Delayed Ʈ ִٸ Ѵ. ó . + void ProcessDelayedSave(); + + template Func for_each_pc(Func f); + + void RegisterForMonsterLog(LPCHARACTER ch); + void UnregisterForMonsterLog(LPCHARACTER ch); + void PacketMonsterLog(LPCHARACTER ch, const void* buf, int size); + + void KillLog(DWORD dwVnum); + + void RegisterRaceNum(DWORD dwVnum); + void RegisterRaceNumMap(LPCHARACTER ch); + void UnregisterRaceNumMap(LPCHARACTER ch); + bool GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i); + + LPCHARACTER FindSpecifyPC(unsigned int uiJobFlag, long lMapIndex, LPCHARACTER except=NULL, int iMinLevel = 1, int iMaxLevel = PLAYER_MAX_LEVEL_CONST); + + void SetMobItemRate(int value) { m_iMobItemRate = value; } + void SetMobDamageRate(int value) { m_iMobDamageRate = value; } + void SetMobGoldAmountRate(int value) { m_iMobGoldAmountRate = value; } + void SetMobGoldDropRate(int value) { m_iMobGoldDropRate = value; } + void SetMobExpRate(int value) { m_iMobExpRate = value; } + + void SetMobItemRatePremium(int value) { m_iMobItemRatePremium = value; } + void SetMobGoldAmountRatePremium(int value) { m_iMobGoldAmountRatePremium = value; } + void SetMobGoldDropRatePremium(int value) { m_iMobGoldDropRatePremium = value; } + void SetMobExpRatePremium(int value) { m_iMobExpRatePremium = value; } + + void SetUserDamageRatePremium(int value) { m_iUserDamageRatePremium = value; } + void SetUserDamageRate(int value ) { m_iUserDamageRate = value; } + int GetMobItemRate(LPCHARACTER ch); + int GetMobDamageRate(LPCHARACTER ch); + int GetMobGoldAmountRate(LPCHARACTER ch); + int GetMobGoldDropRate(LPCHARACTER ch); + int GetMobExpRate(LPCHARACTER ch); + + int GetUserDamageRate(LPCHARACTER ch); + void SendScriptToMap(long lMapIndex, const std::string & s); + + bool BeginPendingDestroy(); + void FlushPendingDestroy(); + + private: + int m_iMobItemRate; + int m_iMobDamageRate; + int m_iMobGoldAmountRate; + int m_iMobGoldDropRate; + int m_iMobExpRate; + + int m_iMobItemRatePremium; + int m_iMobGoldAmountRatePremium; + int m_iMobGoldDropRatePremium; + int m_iMobExpRatePremium; + + int m_iUserDamageRate; + int m_iUserDamageRatePremium; + int m_iVIDCount; + + TR1_NS::unordered_map m_map_pkChrByVID; + TR1_NS::unordered_map m_map_pkChrByPID; + NAME_MAP m_map_pkPCChr; + + char dummy1[1024]; // memory barrier + CHARACTER_SET m_set_pkChrState; // FSM ư ִ + CHARACTER_SET m_set_pkChrForDelayedSave; + CHARACTER_SET m_set_pkChrMonsterLog; + + LPCHARACTER m_pkChrSelectedStone; + + std::map m_map_dwMobKillCount; + + std::set m_set_dwRegisteredRaceNum; + std::map m_map_pkChrByRaceNum; + + bool m_bUsePendingDestroy; + CHARACTER_SET m_set_pkChrPendingDestroy; + +#ifdef M2_USE_POOL + ObjectPool pool_; +#endif +}; + + template +Func CHARACTER_MANAGER::for_each_pc(Func f) +{ + TR1_NS::unordered_map::iterator it; + + for (it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it) + f(it->second); + + return f; +} + +class CharacterVectorInteractor : public CHARACTER_VECTOR +{ + public: + CharacterVectorInteractor() : m_bMyBegin(false) { } + + CharacterVectorInteractor(const CHARACTER_SET & r); + virtual ~CharacterVectorInteractor(); + + private: + bool m_bMyBegin; +}; + +#ifndef DEBUG_ALLOC +#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr) +#else +#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr, __FILE__, __LINE__) +#endif + +#endif diff --git a/game/src/char_quickslot.cpp b/game/src/char_quickslot.cpp new file mode 100644 index 0000000..884cd3b --- /dev/null +++ b/game/src/char_quickslot.cpp @@ -0,0 +1,156 @@ +#include "stdafx.h" +#include "constants.h" +#include "char.h" +#include "desc.h" +#include "desc_manager.h" +#include "packet.h" +#include "item.h" + +///////////////////////////////////////////////////////////////////////////// +// QUICKSLOT HANDLING +///////////////////////////////////////////////////////////////////////////// +void CHARACTER::SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos) // bNewPos == 255 DELETE +{ + if (bOldPos == bNewPos) + return; + + for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i) + { + if (m_quickslot[i].type == bType && m_quickslot[i].pos == bOldPos) + { + if (bNewPos == 255) + DelQuickslot(i); + else + { + TQuickslot slot; + + slot.type = bType; + slot.pos = bNewPos; + + SetQuickslot(i, slot); + } + } + } +} + +bool CHARACTER::GetQuickslot(BYTE pos, TQuickslot ** ppSlot) +{ + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + *ppSlot = &m_quickslot[pos]; + return true; +} + +bool CHARACTER::SetQuickslot(BYTE pos, TQuickslot & rSlot) +{ + struct packet_quickslot_add pack_quickslot_add; + + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + if (rSlot.type >= QUICKSLOT_TYPE_MAX_NUM) + return false; + + for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i) + { + if (rSlot.type == 0) + continue; + else if (m_quickslot[i].type == rSlot.type && m_quickslot[i].pos == rSlot.pos) + DelQuickslot(i); + } + + TItemPos srcCell(INVENTORY, rSlot.pos); + + switch (rSlot.type) + { + case QUICKSLOT_TYPE_ITEM: + if (false == srcCell.IsDefaultInventoryPosition() && false == srcCell.IsBeltInventoryPosition()) + return false; + + break; + + case QUICKSLOT_TYPE_SKILL: + if ((int) rSlot.pos >= SKILL_MAX_NUM) + return false; + + break; + + case QUICKSLOT_TYPE_COMMAND: + break; + + default: + return false; + } + + m_quickslot[pos] = rSlot; + + if (GetDesc()) + { + pack_quickslot_add.header = HEADER_GC_QUICKSLOT_ADD; + pack_quickslot_add.pos = pos; + pack_quickslot_add.slot = m_quickslot[pos]; + + GetDesc()->Packet(&pack_quickslot_add, sizeof(pack_quickslot_add)); + } + + return true; +} + +bool CHARACTER::DelQuickslot(BYTE pos) +{ + struct packet_quickslot_del pack_quickslot_del; + + if (pos >= QUICKSLOT_MAX_NUM) + return false; + + memset(&m_quickslot[pos], 0, sizeof(TQuickslot)); + + pack_quickslot_del.header = HEADER_GC_QUICKSLOT_DEL; + pack_quickslot_del.pos = pos; + + GetDesc()->Packet(&pack_quickslot_del, sizeof(pack_quickslot_del)); + return true; +} + +bool CHARACTER::SwapQuickslot(BYTE a, BYTE b) +{ + struct packet_quickslot_swap pack_quickslot_swap; + TQuickslot quickslot; + + if (a >= QUICKSLOT_MAX_NUM || b >= QUICKSLOT_MAX_NUM) + return false; + + // ڸ ٲ۴. + quickslot = m_quickslot[a]; + + m_quickslot[a] = m_quickslot[b]; + m_quickslot[b] = quickslot; + + pack_quickslot_swap.header = HEADER_GC_QUICKSLOT_SWAP; + pack_quickslot_swap.pos = a; + pack_quickslot_swap.pos_to = b; + + GetDesc()->Packet(&pack_quickslot_swap, sizeof(pack_quickslot_swap)); + return true; +} + +void CHARACTER::ChainQuickslotItem(LPITEM pItem, BYTE bType, BYTE bOldPos) +{ + if (pItem->IsDragonSoul()) + return; + for ( int i=0; i < QUICKSLOT_MAX_NUM; ++i ) + { + if ( m_quickslot[i].type == bType && m_quickslot[i].pos == bOldPos ) + { + TQuickslot slot; + slot.type = bType; + slot.pos = pItem->GetCell(); + + SetQuickslot(i, slot); + + break; + } + } +} + diff --git a/game/src/char_resist.cpp b/game/src/char_resist.cpp new file mode 100644 index 0000000..06d0c69 --- /dev/null +++ b/game/src/char_resist.cpp @@ -0,0 +1,280 @@ +#include "stdafx.h" +#include "constants.h" +#include "config.h" +#include "char.h" +#include "char_manager.h" +#include "affect.h" +#include "locale_service.h" + +// +const int poison_damage_rate[MOB_RANK_MAX_NUM] = +{ + 80, 50, 40, 30, 25, 1 +}; + +int GetPoisonDamageRate(LPCHARACTER ch) +{ + int iRate; + + if (ch->IsPC()) + { + if (LC_IsYMIR()) + iRate = 40; + else + iRate = 50; + } + else + iRate = poison_damage_rate[ch->GetMobRank()]; + + iRate = MAX(0, iRate - ch->GetPoint(POINT_POISON_REDUCE)); + return iRate; +} + +EVENTINFO(TPoisonEventInfo) +{ + DynamicCharacterPtr ch; + int count; + DWORD attacker_pid; + + TPoisonEventInfo() + : ch() + , count(0) + , attacker_pid(0) + { + } +}; + +EVENTFUNC(poison_event) +{ + TPoisonEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "poison_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); + + int dam = ch->GetMaxHP() * GetPoisonDamageRate(ch) / 1000; + if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Poison Damage %d", dam); + + if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_POISON)) + { + ch->m_pkPoisonEvent = NULL; + return 0; + } + + --info->count; + + if (info->count) + return PASSES_PER_SEC(3); + else + { + ch->m_pkPoisonEvent = NULL; + return 0; + } +} + +EVENTINFO(TFireEventInfo) +{ + DynamicCharacterPtr ch; + int count; + int amount; + DWORD attacker_pid; + + TFireEventInfo() + : ch() + , count(0) + , amount(0) + , attacker_pid(0) + { + } +}; + +EVENTFUNC(fire_event) +{ + TFireEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "fire_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); + + int dam = info->amount; + if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Fire Damage %d", dam); + + if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_FIRE)) + { + ch->m_pkFireEvent = NULL; + return 0; + } + + --info->count; + + if (info->count) + return PASSES_PER_SEC(3); + else + { + ch->m_pkFireEvent = NULL; + return 0; + } +} + +/* + + LEVEL .. + + +8 0% + +7 5% + +6 10% + +5 30% + +4 50% + +3 70% + +2 80% + +1 90% + +0 100% + -1 100% + -2 100% + -3 100% + -4 100% + -5 100% + -6 100% + -7 100% + -8 100% + + */ + +static int poison_level_adjust[9] = +{ + 100, 90, 80, 70, 50, 30, 10, 5, 0 +}; + +void CHARACTER::AttackedByFire(LPCHARACTER pkAttacker, int amount, int count) +{ + if (m_pkFireEvent) + return; + + AddAffect(AFFECT_FIRE, POINT_NONE, 0, AFF_FIRE, count*3+1, 0, true); + + TFireEventInfo* info = AllocEventInfo(); + + info->ch = this; + info->count = count; + info->amount = amount; + info->attacker_pid = pkAttacker->GetPlayerID(); + + m_pkFireEvent = event_create(fire_event, info, 1); +} + +void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker) +{ + if (m_pkPoisonEvent) + return; + + if (m_bHasPoisoned && !IsPC()) // ʹ ѹ ɸ. + return; + + if (pkAttacker && pkAttacker->GetLevel() < GetLevel()) + { + int delta = GetLevel() - pkAttacker->GetLevel(); + + if (delta > 8) + delta = 8; + + if (number(1, 100) > poison_level_adjust[delta]) + return; + } + + /*if (IsImmune(IMMUNE_POISON)) + return;*/ + + // , ɷȴ! + m_bHasPoisoned = true; + + AddAffect(AFFECT_POISON, POINT_NONE, 0, AFF_POISON, POISON_LENGTH + 1, 0, true); + + TPoisonEventInfo* info = AllocEventInfo(); + + info->ch = this; + info->count = 10; + info->attacker_pid = pkAttacker?pkAttacker->GetPlayerID():0; + + m_pkPoisonEvent = event_create(poison_event, info, 1); + + if (test_server && pkAttacker) + { + char buf[256]; + snprintf(buf, sizeof(buf), "POISON %s -> %s", pkAttacker->GetName(), GetName()); + pkAttacker->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + } +} + +void CHARACTER::RemoveFire() +{ + RemoveAffect(AFFECT_FIRE); + event_cancel(&m_pkFireEvent); +} + +void CHARACTER::RemovePoison() +{ + RemoveAffect(AFFECT_POISON); + event_cancel(&m_pkPoisonEvent); +} + +void CHARACTER::ApplyMobAttribute(const TMobTable* table) +{ + for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i) + { + if (table->cEnchants[i] != 0) + ApplyPoint(aiMobEnchantApplyIdx[i], table->cEnchants[i]); + } + + for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i) + { + if (table->cResists[i] != 0) + ApplyPoint(aiMobResistsApplyIdx[i], table->cResists[i]); + } +} + +bool CHARACTER::IsImmune(DWORD dwImmuneFlag) +{ + if (IS_SET(m_pointsInstant.dwImmuneFlag, dwImmuneFlag)) + { + int immune_pct = 90; + int percent = number(1, 100); + + if (percent <= immune_pct) // 90% Immune + { + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s)", GetName()); + + return true; + } + else + { + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s)", GetName()); + + return false; + } + } + + if (test_server && IsPC()) + ChatPacket(CHAT_TYPE_PARTY, " (%s) NO_IMMUNE_FLAG", GetName()); + + return false; +} + diff --git a/game/src/char_skill.cpp b/game/src/char_skill.cpp new file mode 100644 index 0000000..0efdb2e --- /dev/null +++ b/game/src/char_skill.cpp @@ -0,0 +1,3600 @@ +#include "stdafx.h" +#include + +#include "utils.h" +#include "config.h" +#include "vector.h" +#include "char.h" +#include "char_manager.h" +#include "battle.h" +#include "desc.h" +#include "desc_manager.h" +#include "packet.h" +#include "affect.h" +#include "item.h" +#include "sectree_manager.h" +#include "mob_manager.h" +#include "start_position.h" +#include "party.h" +#include "buffer_manager.h" +#include "guild.h" +#include "log.h" +#include "unique_item.h" +#include "questmanager.h" + +extern int test_server; + +static const DWORD s_adwSubSkillVnums[] = +{ + SKILL_LEADERSHIP, + SKILL_COMBO, + SKILL_MINING, + SKILL_LANGUAGE1, + SKILL_LANGUAGE2, + SKILL_LANGUAGE3, + SKILL_POLYMORPH, + SKILL_HORSE, + SKILL_HORSE_SUMMON, + SKILL_HORSE_WILDATTACK, + SKILL_HORSE_CHARGE, + SKILL_HORSE_ESCAPE, + SKILL_HORSE_WILDATTACK_RANGE, + SKILL_ADD_HP, + SKILL_RESIST_PENETRATE +}; + +time_t CHARACTER::GetSkillNextReadTime(DWORD dwVnum) const +{ + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (vnum: %u)", dwVnum); + return 0; + } + + return m_pSkillLevels ? m_pSkillLevels[dwVnum].tNextRead : 0; +} + +void CHARACTER::SetSkillNextReadTime(DWORD dwVnum, time_t time) +{ + if (m_pSkillLevels && dwVnum < SKILL_MAX_NUM) + m_pSkillLevels[dwVnum].tNextRead = time; +} + +bool TSkillUseInfo::HitOnce(DWORD dwVnum) +{ + // ʾ Ѵ. + if (!bUsed) + return false; + + sys_log(1, "__HitOnce NextUse %u current %u count %d scount %d", dwNextSkillUsableTime, get_dword_time(), iHitCount, iSplashCount); + + if (dwNextSkillUsableTime && dwNextSkillUsableTimeisGrandMaster = isGrandMaster; + DWORD dwCur = get_dword_time(); + + // Ÿ ʾҴ. + if (bUsed && dwNextSkillUsableTime > dwCur) + { + sys_log(0, "cooltime is not over delta %u", dwNextSkillUsableTime - dwCur); + iHitCount = 0; + return false; + } + + bUsed = true; + + if (dwCooltime) + dwNextSkillUsableTime = dwCur + dwCooltime; + else + dwNextSkillUsableTime = 0; + + iRange = range; + iMaxHitCount = iHitCount = hitcount; + + if (test_server) + sys_log(0, "UseSkill NextUse %u current %u cooltime %d hitcount %d/%d", dwNextSkillUsableTime, dwCur, dwCooltime, iHitCount, iMaxHitCount); + + dwVID = vid; + iSplashCount = splashcount; + return true; +} + +int CHARACTER::GetChainLightningMaxCount() const +{ + return aiChainLightningCountBySkillLevel[MIN(SKILL_MAX_LEVEL, GetSkillLevel(SKILL_CHAIN))]; +} + +void CHARACTER::SetAffectedEunhyung() +{ + m_dwAffectedEunhyungLevel = GetSkillPower(SKILL_EUNHYUNG); +} + +void CHARACTER::SetSkillGroup(BYTE bSkillGroup) +{ + if (bSkillGroup > 2) + return; + + if (GetLevel() < 5) + return; + + m_points.skill_group = bSkillGroup; + + TPacketGCChangeSkillGroup p; + p.header = HEADER_GC_SKILL_GROUP; + p.skill_group = m_points.skill_group; + + GetDesc()->Packet(&p, sizeof(TPacketGCChangeSkillGroup)); +} + +int CHARACTER::ComputeCooltime(int time) +{ + return CalculateDuration(GetPoint(POINT_CASTING_SPEED), time); +} + +void CHARACTER::SkillLevelPacket() +{ + if (!GetDesc()) + return; + + TPacketGCSkillLevel pack; + + pack.bHeader = HEADER_GC_SKILL_LEVEL; + thecore_memcpy(&pack.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + GetDesc()->Packet(&pack, sizeof(TPacketGCSkillLevel)); +} + +void CHARACTER::SetSkillLevel(DWORD dwVnum, BYTE bLev) +{ + if (NULL == m_pSkillLevels) + return; + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (vnum %u)", dwVnum); + return; + } + + m_pSkillLevels[dwVnum].bLevel = MIN(40, bLev); + + if (bLev >= 40) + m_pSkillLevels[dwVnum].bMasterType = SKILL_PERFECT_MASTER; + else if (bLev >= 30) + m_pSkillLevels[dwVnum].bMasterType = SKILL_GRAND_MASTER; + else if (bLev >= 20) + m_pSkillLevels[dwVnum].bMasterType = SKILL_MASTER; + else + m_pSkillLevels[dwVnum].bMasterType = SKILL_NORMAL; +} + +bool CHARACTER::IsLearnableSkill(DWORD dwSkillVnum) const +{ + const CSkillProto * pkSkill = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSkill) + return false; + + if (GetSkillLevel(dwSkillVnum) >= SKILL_MAX_LEVEL) + return false; + + if (pkSkill->dwType == 0) + { + if (GetSkillLevel(dwSkillVnum) >= pkSkill->bMaxLevel) + return false; + + return true; + } + + if (pkSkill->dwType == 5) + { + if (dwSkillVnum == SKILL_HORSE_WILDATTACK_RANGE && GetJob() != JOB_ASSASSIN) + return false; + + return true; + } + + if (GetSkillGroup() == 0) + return false; + + if (pkSkill->dwType - 1 == GetJob()) + return true; + + if (6 == pkSkill->dwType) + { + if (SKILL_7_A_ANTI_TANHWAN <= dwSkillVnum && dwSkillVnum <= SKILL_7_D_ANTI_YONGBI) + { + for (int i=0 ; i < 4 ; i++) + { + if (unsigned(SKILL_7_A_ANTI_TANHWAN + i) != dwSkillVnum) + { + if (0 != GetSkillLevel(SKILL_7_A_ANTI_TANHWAN + i)) + { + return false; + } + } + } + + return true; + } + + if (SKILL_8_A_ANTI_GIGONGCHAM <= dwSkillVnum && dwSkillVnum <= SKILL_8_D_ANTI_BYEURAK) + { + for (int i=0 ; i < 4 ; i++) + { + if (unsigned(SKILL_8_A_ANTI_GIGONGCHAM + i) != dwSkillVnum) + { + if (0 != GetSkillLevel(SKILL_8_A_ANTI_GIGONGCHAM + i)) + return false; + } + } + + return true; + } + } + + return false; +} + +// ADD_GRANDMASTER_SKILL +bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum) +{ + CSkillProto * pkSk = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSk) + return false; + + if (!IsLearnableSkill(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ųԴϴ.")); + return false; + } + + sys_log(0, "learn grand master skill[%d] cur %d, next %d", dwSkillVnum, get_global_time(), GetSkillNextReadTime(dwSkillVnum)); + + /* + if (get_global_time() < GetSkillNextReadTime(dwSkillVnum)) + { + if (!(test_server && quest::CQuestManager::instance().GetEventFlag("no_read_delay"))) + { + if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY)) + { + // ־ȼ ߿ ð + RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("־ȼ ȭԸ Խϴ.")); + } + else + { + SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time()); + return false; + } + } + } + */ + + // bType 0̸ ó å + if (pkSk->dwType == 0) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ųԴϴ.")); + return false; + } + + if (GetSkillMasterType(dwSkillVnum) != SKILL_GRAND_MASTER) + { + if (GetSkillMasterType(dwSkillVnum) > SKILL_GRAND_MASTER) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ʈ ͵ ųԴϴ. ̻ ϴ.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų ׷ ̸ ʾҽϴ.")); + return false; + } + + std::string strTrainSkill; + { + std::ostringstream os; + os << "training_grandmaster_skill.skill" << dwSkillVnum; + strTrainSkill = os.str(); + } + + // ⼭ Ȯ մϴ. + BYTE bLastLevel = GetSkillLevel(dwSkillVnum); + + int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 30); + + sys_log(0, "LearnGrandMasterSkill %s table idx %d value %d", GetName(), idx, aiGrandMasterSkillBookCountForLevelUp[idx]); + + int iTotalReadCount = GetQuestFlag(strTrainSkill) + 1; + SetQuestFlag(strTrainSkill, iTotalReadCount); + + int iMinReadCount = aiGrandMasterSkillBookMinCount[idx]; + int iMaxReadCount = aiGrandMasterSkillBookMaxCount[idx]; + + int iBookCount = aiGrandMasterSkillBookCountForLevelUp[idx]; + + if ( LC_IsYMIR() == true || LC_IsKorea() == true ) + { + const int aiGrandMasterSkillBookCountForLevelUp_euckr[10] = + { + 3, 3, 4, 5, 6, 7, 8, 9, 10, 15, + }; + + const int aiGrandMasterSkillBookMinCount_euckr[10] = + { + 1, 1, 1, 2, 2, 2, 3, 3, 4, 5 + }; + + const int aiGrandMasterSkillBookMaxCount_euckr[10] = + { + 5, 7, 9, 11, 13, 15, 18, 23, 25, 30 + }; + + iMinReadCount = aiGrandMasterSkillBookMinCount_euckr[idx]; + iMaxReadCount = aiGrandMasterSkillBookMaxCount_euckr[idx]; + iBookCount = aiGrandMasterSkillBookCountForLevelUp_euckr[idx]; + } + + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + if (iBookCount&1) + iBookCount = iBookCount / 2 + 1; + else + iBookCount = iBookCount / 2; + + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + + int n = number(1, iBookCount); + sys_log(0, "Number(%d)", n); + + DWORD nextTime = get_global_time() + number(28800, 43200); + + sys_log(0, "GrandMaster SkillBookCount min %d cur %d max %d (next_time=%d)", iMinReadCount, iTotalReadCount, iMaxReadCount, nextTime); + + bool bSuccess = n == 2; + + if (iTotalReadCount < iMinReadCount) + bSuccess = false; + if (iTotalReadCount > iMaxReadCount) + bSuccess = true; + + if (bSuccess) + { + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_QUEST); + } + + SetSkillNextReadTime(dwSkillVnum, nextTime); + + if (bLastLevel == GetSkillLevel(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ, Ⱑ ϰ ־! ̰ ȭԸΰ!? !")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" з ϴ. ٽ ֽñ ٶϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_FAIL", ""); + return false; + } + + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̾!")); + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("߰ſ ġ ־! ̰, ̰!")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_SUCCESS", ""); + return true; +} +// END_OF_ADD_GRANDMASTER_SKILL + +static bool FN_should_check_exp(LPCHARACTER ch) +{ + if (LC_IsCanada()) + return ch->GetLevel() < gPlayerMaxLevel; + + if (!LC_IsYMIR()) + return true; + + return false; +} + + +bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb) +{ + const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum); + + if (!pkSk) + return false; + + if (!IsLearnableSkill(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ųԴϴ.")); + return false; + } + + DWORD need_exp = 0; + + if (FN_should_check_exp(this)) + { + need_exp = 20000; + + if ( GetExp() < need_exp ) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ġ Ͽ å ϴ.")); + return false; + } + } + + // bType 0̸ ó å + if (pkSk->dwType != 0) + { + if (GetSkillMasterType(dwSkillVnum) != SKILL_MASTER) + { + if (GetSkillMasterType(dwSkillVnum) > SKILL_MASTER) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų å ̻ ϴ.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ų å ̸ ʾҽϴ.")); + return false; + } + } + + if (get_global_time() < GetSkillNextReadTime(dwSkillVnum)) + { + if (!(test_server && quest::CQuestManager::instance().GetEventFlag("no_read_delay"))) + { + if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY)) + { + // ־ȼ ߿ ð + RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("־ȼ ȭԸ Խϴ.")); + } + else + { + SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time()); + return false; + } + } + } + + // ⼭ Ȯ մϴ. + BYTE bLastLevel = GetSkillLevel(dwSkillVnum); + + if (bProb != 0) + { + // SKILL_BOOK_BONUS + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + bProb += bProb / 2; + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + // END_OF_SKILL_BOOK_BONUS + + sys_log(0, "LearnSkillByBook Pct %u prob %d", dwSkillVnum, bProb); + + if (number(1, 100) <= bProb) + { + if (test_server) + sys_log(0, "LearnSkillByBook %u SUCC", dwSkillVnum); + + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK); + } + else + { + if (test_server) + sys_log(0, "LearnSkillByBook %u FAIL", dwSkillVnum); + } + } + else + { + int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 20); + + sys_log(0, "LearnSkillByBook %s table idx %d value %d", GetName(), idx, aiSkillBookCountForLevelUp[idx]); + + if (!LC_IsYMIR()) + { + int need_bookcount = GetSkillLevel(dwSkillVnum) - 20; + + PointChange(POINT_EXP, -need_exp); + + quest::CQuestManager& q = quest::CQuestManager::instance(); + quest::PC* pPC = q.GetPC(GetPlayerID()); + + if (pPC) + { + char flag[128+1]; + memset(flag, 0, sizeof(flag)); + snprintf(flag, sizeof(flag), "traning_master_skill.%u.read_count", dwSkillVnum); + + int read_count = pPC->GetFlag(flag); + int percent = 65; + + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + percent = 0; + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + + if (number(1, 100) > percent) + { + // åб⿡ + if (read_count >= need_bookcount) + { + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK); + pPC->SetFlag(flag, 0); + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", ""); + return true; + } + else + { + pPC->SetFlag(flag, read_count + 1); + + switch (number(1, 3)) + { + case 1: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ذ Ǿ ѵ ѵ..")); + break; + + case 2: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̴ ǰ... ϱⰡ ʹ ..")); + break; + + case 3: + default: + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ϴ ͸ ִ ̴..")); + break; + } + + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d о Ϸ ֽϴ."), need_bookcount - read_count); + return true; + } + } + } + else + { + // Ʈ ε + } + } + // INTERNATIONAL_VERSION + else + { + int iBookCount = 99; + + if (LC_IsYMIR() == true) + { + const int aiSkillBookCountForLevelUp_euckr[10] = + { + 2, 2, 3, 3, 3, 3, 3, 3, 4, 5 + }; + + iBookCount = aiSkillBookCountForLevelUp_euckr[idx]; + } + else + iBookCount = aiSkillBookCountForLevelUp[idx]; + + if (FindAffect(AFFECT_SKILL_BOOK_BONUS)) + { + if (iBookCount & 1) // iBookCount % 2 + iBookCount = iBookCount / 2 + 1; + else + iBookCount = iBookCount / 2; + + RemoveAffect(AFFECT_SKILL_BOOK_BONUS); + } + + if (number(1, iBookCount) == 2) + SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK); + } + // END_OF_INTERNATIONAL_VERSION + } + + if (bLastLevel != GetSkillLevel(dwSkillVnum)) + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT(" ̾!")); + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("߰ſ ġ ־! ̰, ̰!")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å ̽ϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", ""); + } + else + { + ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ, Ⱑ ϰ ־! ̰ ȭԸΰ!? !")); + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" з ϴ. ٽ ֽñ ٶϴ.")); + LogManager::instance().CharLog(this, dwSkillVnum, "READ_FAIL", ""); + } + + return true; +} + +bool CHARACTER::SkillLevelDown(DWORD dwVnum) +{ + if (NULL == m_pSkillLevels) + return false; + + if (g_bSkillDisable) + return false; + + if (IsPolymorphed()) + return false; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + { + sys_err("There is no such skill by number %u", dwVnum); + return false; + } + + if (!IsLearnableSkill(dwVnum)) + return false; + + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL) + return false; + + if (!GetSkillGroup()) + return false; + + if (pkSk->dwVnum >= SKILL_MAX_NUM) + return false; + + if (m_pSkillLevels[pkSk->dwVnum].bLevel == 0) + return false; + + int idx = POINT_SKILL; + switch (pkSk->dwType) + { + case 0: + idx = POINT_SUB_SKILL; + break; + case 1: + case 2: + case 3: + case 4: + case 6: + idx = POINT_SKILL; + break; + case 5: + idx = POINT_HORSE_SKILL; + break; + default: + sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum); + return false; + + } + + PointChange(idx, +1); + SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel - 1); + + sys_log(0, "SkillDown: %s %u %u %u type %u", GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, pkSk->dwType); + Save(); + + ComputePoints(); + SkillLevelPacket(); + return true; +} + +void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod) +{ + if (NULL == m_pSkillLevels) + return; + + if (g_bSkillDisable) + return; + + if (IsPolymorphed()) + { + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (SKILL_7_A_ANTI_TANHWAN <= dwVnum && dwVnum <= SKILL_8_D_ANTI_BYEURAK) + { + if (0 == GetSkillLevel(dwVnum)) + return; + } + + const CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + { + sys_err("There is no such skill by number (vnum %u)", dwVnum); + return; + } + + if (pkSk->dwVnum >= SKILL_MAX_NUM) + { + sys_err("Skill Vnum overflow (vnum %u)", dwVnum); + return; + } + + if (!IsLearnableSkill(dwVnum)) + return; + + // ׷ ʹ Ʈθ డ + if (pkSk->dwType != 0) + { + switch (GetSkillMasterType(pkSk->dwVnum)) + { + case SKILL_GRAND_MASTER: + if (bMethod != SKILL_UP_BY_QUEST) + return; + break; + + case SKILL_PERFECT_MASTER: + return; + } + } + + if (bMethod == SKILL_UP_BY_POINT) + { + // Ͱ ƴ ¿ ð + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL) + return; + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_DISABLE_BY_POINT_UP)) + return; + } + else if (bMethod == SKILL_UP_BY_BOOK) + { + if (pkSk->dwType != 0) // ʾҰų Ʈ ø ų ó å ִ. + if (GetSkillMasterType(pkSk->dwVnum) != SKILL_MASTER) + return; + } + + if (GetLevel() < pkSk->bLevelLimit) + return; + + if (pkSk->preSkillVnum) + if (GetSkillMasterType(pkSk->preSkillVnum) == SKILL_NORMAL && + GetSkillLevel(pkSk->preSkillVnum) < pkSk->preSkillLevel) + return; + + if (!GetSkillGroup()) + return; + + if (bMethod == SKILL_UP_BY_POINT) + { + int idx; + + switch (pkSk->dwType) + { + case 0: + idx = POINT_SUB_SKILL; + break; + + case 1: + case 2: + case 3: + case 4: + case 6: + idx = POINT_SKILL; + break; + + case 5: + idx = POINT_HORSE_SKILL; + break; + + default: + sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum); + return; + } + + if (GetPoint(idx) < 1) + return; + + PointChange(idx, -1); + } + + int SkillPointBefore = GetSkillLevel(pkSk->dwVnum); + SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel + 1); + + if (pkSk->dwType != 0) + { + // ڱ ׷̵ ϴ ڵ + switch (GetSkillMasterType(pkSk->dwVnum)) + { + case SKILL_NORMAL: + // ų ׷̵ 17~20 + if (GetSkillLevel(pkSk->dwVnum) >= 17) + { + if (GetQuestFlag("reset_scroll.force_to_master_skill") > 0) + { + SetSkillLevel(pkSk->dwVnum, 20); + SetQuestFlag("reset_scroll.force_to_master_skill", 0); + } + else + { + if (number(1, 21 - MIN(20, GetSkillLevel(pkSk->dwVnum))) == 1) + SetSkillLevel(pkSk->dwVnum, 20); + } + } + break; + + case SKILL_MASTER: + if (GetSkillLevel(pkSk->dwVnum) >= 30) + { + if (number(1, 31 - MIN(30, GetSkillLevel(pkSk->dwVnum))) == 1) + SetSkillLevel(pkSk->dwVnum, 30); + } + break; + + case SKILL_GRAND_MASTER: + if (GetSkillLevel(pkSk->dwVnum) >= 40) + { + SetSkillLevel(pkSk->dwVnum, 40); + } + break; + } + } + + char szSkillUp[1024]; + + snprintf(szSkillUp, sizeof(szSkillUp), "SkillUp: %s %u %d %d[Before:%d] type %u", + GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, SkillPointBefore, pkSk->dwType); + + sys_log(0, "%s", szSkillUp); + + LogManager::instance().CharLog(this, pkSk->dwVnum, "SKILLUP", szSkillUp); + Save(); + + ComputePoints(); + SkillLevelPacket(); +} + +void CHARACTER::ComputeSkillPoints() +{ + if (g_bSkillDisable) + return; +} + +void CHARACTER::ResetSkill() +{ + if (NULL == m_pSkillLevels) + return; + + // ų ½Ű ʴ´ + std::vector > vec; + size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]); + + for (size_t i = 0; i < count; ++i) + { + if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM) + continue; + + vec.push_back(std::make_pair(s_adwSubSkillVnums[i], m_pSkillLevels[s_adwSubSkillVnums[i]])); + } + + memset(m_pSkillLevels, 0, sizeof(TPlayerSkill) * SKILL_MAX_NUM); + + std::vector >::const_iterator iter = vec.begin(); + + while (iter != vec.end()) + { + const std::pair& pair = *(iter++); + m_pSkillLevels[pair.first] = pair.second; + } + + ComputePoints(); + SkillLevelPacket(); +} + +void CHARACTER::ComputePassiveSkill(DWORD dwVnum) +{ + if (g_bSkillDisable) + return; + + if (GetSkillLevel(dwVnum) == 0) + return; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + pkSk->SetPointVar("k", GetSkillLevel(dwVnum)); + int iAmount = (int) pkSk->kPointPoly.Eval(); + + sys_log(2, "%s passive #%d on %d amount %d", GetName(), dwVnum, pkSk->bPointOn, iAmount); + PointChange(pkSk->bPointOn, iAmount); +} + +struct FFindNearVictim +{ + FFindNearVictim(LPCHARACTER center, LPCHARACTER attacker, const CHARACTER_SET& excepts_set = empty_set_) + : m_pkChrCenter(center), + m_pkChrNextTarget(NULL), + m_pkChrAttacker(attacker), + m_count(0), + m_excepts_set(excepts_set) + { + } + + void operator ()(LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!m_excepts_set.empty()) { + if (m_excepts_set.find(pkChr) != m_excepts_set.end()) + return; + } + + if (m_pkChrCenter == pkChr) + return; + + if (!battle_is_attackable(m_pkChrAttacker, pkChr)) + { + return; + } + + if (abs(m_pkChrCenter->GetX() - pkChr->GetX()) > 1000 || abs(m_pkChrCenter->GetY() - pkChr->GetY()) > 1000) + return; + + float fDist = DISTANCE_APPROX(m_pkChrCenter->GetX() - pkChr->GetX(), m_pkChrCenter->GetY() - pkChr->GetY()); + + if (fDist < 1000) + { + ++m_count; + + if ((m_count == 1) || number(1, m_count) == 1) + m_pkChrNextTarget = pkChr; + } + } + + LPCHARACTER GetVictim() + { + return m_pkChrNextTarget; + } + + LPCHARACTER m_pkChrCenter; + LPCHARACTER m_pkChrNextTarget; + LPCHARACTER m_pkChrAttacker; + int m_count; + const CHARACTER_SET & m_excepts_set; +private: + static CHARACTER_SET empty_set_; +}; + +CHARACTER_SET FFindNearVictim::empty_set_; + +EVENTINFO(chain_lightning_event_info) +{ + DWORD dwVictim; + DWORD dwChr; + + chain_lightning_event_info() + : dwVictim(0) + , dwChr(0) + { + } +}; + +EVENTFUNC(ChainLightningEvent) +{ + chain_lightning_event_info * info = dynamic_cast( event->info ); + + LPCHARACTER pkChrVictim = CHARACTER_MANAGER::instance().Find(info->dwVictim); + LPCHARACTER pkChr = CHARACTER_MANAGER::instance().Find(info->dwChr); + LPCHARACTER pkTarget = NULL; + + if (!pkChr || !pkChrVictim) + { + sys_log(1, "use chainlighting, but no character"); + return 0; + } + + sys_log(1, "chainlighting event %s", pkChr->GetName()); + + if (pkChrVictim->GetParty()) // Ƽ + { + pkTarget = pkChrVictim->GetParty()->GetNextOwnership(NULL, pkChrVictim->GetX(), pkChrVictim->GetY()); + if (pkTarget == pkChrVictim || !number(0, 2) || pkChr->GetChainLightingExcept().find(pkTarget) != pkChr->GetChainLightingExcept().end()) + pkTarget = NULL; + } + + if (!pkTarget) + { + // 1. Find Next victim + FFindNearVictim f(pkChrVictim, pkChr, pkChr->GetChainLightingExcept()); + + if (pkChrVictim->GetSectree()) + { + pkChrVictim->GetSectree()->ForEachAround(f); + // 2. If exist, compute it again + pkTarget = f.GetVictim(); + } + } + + if (pkTarget) + { + pkChrVictim->CreateFly(FLY_CHAIN_LIGHTNING, pkTarget); + pkChr->ComputeSkill(SKILL_CHAIN, pkTarget); + pkChr->AddChainLightningExcept(pkTarget); + } + else + { + sys_log(1, "%s use chainlighting, but find victim failed near %s", pkChr->GetName(), pkChrVictim->GetName()); + } + + return 0; +} + +void SetPolyVarForAttack(LPCHARACTER ch, CSkillProto * pkSk, LPITEM pkWeapon) +{ + if (ch->IsPC()) + { + if (pkWeapon && pkWeapon->GetType() == ITEM_WEAPON) + { + int iWep = number(pkWeapon->GetValue(3), pkWeapon->GetValue(4)); + iWep += pkWeapon->GetValue(5); + + int iMtk = number(pkWeapon->GetValue(1), pkWeapon->GetValue(2)); + iMtk += pkWeapon->GetValue(5); + + pkSk->SetPointVar("wep", iWep); + pkSk->SetPointVar("mtk", iMtk); + pkSk->SetPointVar("mwep", iMtk); + } + else + { + pkSk->SetPointVar("wep", 0); + pkSk->SetPointVar("mtk", 0); + pkSk->SetPointVar("mwep", 0); + } + } + else + { + int iWep = number(ch->GetMobDamageMin(), ch->GetMobDamageMax()); + pkSk->SetPointVar("wep", iWep); + pkSk->SetPointVar("mwep", iWep); + pkSk->SetPointVar("mtk", iWep); + } +} + +struct FuncSplashDamage +{ + FuncSplashDamage(int x, int y, CSkillProto * pkSk, LPCHARACTER pkChr, int iAmount, int iAG, int iMaxHit, LPITEM pkWeapon, bool bDisableCooltime, TSkillUseInfo* pInfo, BYTE bUseSkillPower) + : + m_x(x), m_y(y), m_pkSk(pkSk), m_pkChr(pkChr), m_iAmount(iAmount), m_iAG(iAG), m_iCount(0), m_iMaxHit(iMaxHit), m_pkWeapon(pkWeapon), m_bDisableCooltime(bDisableCooltime), m_pInfo(pInfo), m_bUseSkillPower(bUseSkillPower) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + { + //if (m_pkSk->dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN target not character %s", m_pkChr->GetName()); + return; + } + + LPCHARACTER pkChrVictim = (LPCHARACTER) ent; + + if (DISTANCE_APPROX(m_x - pkChrVictim->GetX(), m_y - pkChrVictim->GetY()) > m_pkSk->iSplashRange) + { + if(test_server) + sys_log(0, "XXX target too far %s", m_pkChr->GetName()); + return; + } + + if (!battle_is_attackable(m_pkChr, pkChrVictim)) + { + if(test_server) + sys_log(0, "XXX target not attackable %s", m_pkChr->GetName()); + return; + } + + if (m_pkChr->IsPC()) + // ų Ÿ ó ʴ´. + if (!(m_pkSk->dwVnum >= GUILD_SKILL_START && m_pkSk->dwVnum <= GUILD_SKILL_END)) + if (!m_bDisableCooltime && m_pInfo && !m_pInfo->HitOnce(m_pkSk->dwVnum) && m_pkSk->dwVnum != SKILL_MUYEONG) + { + if(test_server) + sys_log(0, "check guild skill %s", m_pkChr->GetName()); + return; + } + + ++m_iCount; + + int iDam; + + //////////////////////////////////////////////////////////////////////////////// + //float k = 1.0f * m_pkChr->GetSkillPower(m_pkSk->dwVnum) * m_pkSk->bMaxLevel / 100; + //m_pkSk->kPointPoly2.SetVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetPointVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetPointVar("lv", m_pkChr->GetLevel()); + m_pkSk->SetPointVar("iq", m_pkChr->GetPoint(POINT_IQ)); + m_pkSk->SetPointVar("str", m_pkChr->GetPoint(POINT_ST)); + m_pkSk->SetPointVar("dex", m_pkChr->GetPoint(POINT_DX)); + m_pkSk->SetPointVar("con", m_pkChr->GetPoint(POINT_HT)); + m_pkSk->SetPointVar("def", m_pkChr->GetPoint(POINT_DEF_GRADE)); + m_pkSk->SetPointVar("odef", m_pkChr->GetPoint(POINT_DEF_GRADE) - m_pkChr->GetPoint(POINT_DEF_GRADE_BONUS)); + m_pkSk->SetPointVar("horse_level", m_pkChr->GetHorseLevel()); + + //int iPenetratePct = (int)(1 + k*4); + bool bIgnoreDefense = false; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_PENETRATE)) + { + int iPenetratePct = (int) m_pkSk->kPointPoly2.Eval(); + + if (number(1, 100) <= iPenetratePct) + bIgnoreDefense = true; + } + + bool bIgnoreTargetRating = false; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_IGNORE_TARGET_RATING)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + + if (number(1, 100) <= iPct) + bIgnoreTargetRating = true; + } + + m_pkSk->SetPointVar("ar", CalcAttackRating(m_pkChr, pkChrVictim, bIgnoreTargetRating)); + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + m_pkSk->SetPointVar("atk", CalcMeleeDamage(m_pkChr, pkChrVictim, true, bIgnoreTargetRating)); + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + + if (1 == m_pkChr->GetArrowAndBow(&pkBow, &pkArrow, 1)) + m_pkSk->SetPointVar("atk", CalcArrowDamage(m_pkChr, pkChrVictim, pkBow, pkArrow, true)); + else + m_pkSk->SetPointVar("atk", 0); + } + + if (m_pkSk->bPointOn == POINT_MOV_SPEED) + m_pkSk->kPointPoly.SetVar("maxv", pkChrVictim->GetLimitPoint(POINT_MOV_SPEED)); + + m_pkSk->SetPointVar("maxhp", pkChrVictim->GetMaxHP()); + m_pkSk->SetPointVar("maxsp", pkChrVictim->GetMaxSP()); + + m_pkSk->SetPointVar("chain", m_pkChr->GetChainLightningIndex()); + m_pkChr->IncChainLightningIndex(); + + bool bUnderEunhyung = m_pkChr->GetAffectedEunhyung() > 0; // ̰ ⼭ ?? + + m_pkSk->SetPointVar("ek", m_pkChr->GetAffectedEunhyung()*1./100); + //m_pkChr->ClearAffectedEunhyung(); + SetPolyVarForAttack(m_pkChr, m_pkSk, m_pkWeapon); + + int iAmount = 0; + + if (m_pkChr->GetUsedSkillMasterType(m_pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + iAmount = (int) m_pkSk->kMasterBonusPoly.Eval(); + } + else + { + iAmount = (int) m_pkSk->kPointPoly.Eval(); + } + + if (test_server && iAmount == 0 && m_pkSk->bPointOn != POINT_NONE) + { + m_pkChr->ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + //////////////////////////////////////////////////////////////////////////////// + iAmount = -iAmount; + + if (m_pkSk->dwVnum == SKILL_AMSEOP) + { + float fDelta = GetDegreeDelta(m_pkChr->GetRotation(), pkChrVictim->GetRotation()); + float adjust; + + if (fDelta < 35.0f) + { + adjust = 1.5f; + + if (bUnderEunhyung) + adjust += 0.5f; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + { + //if (!g_iUseLocale) + if ( LC_IsYMIR() ) + adjust += 1.0f; + else + adjust += 0.5f; + } + } + else + { + adjust = 1.0f; + + if ( !LC_IsYMIR() ) + { + if (bUnderEunhyung) + adjust += 0.5f; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + adjust += 0.5f; + } + } + + iAmount = (int) (iAmount * adjust); + } + else if (m_pkSk->dwVnum == SKILL_GUNGSIN) + { + float adjust = 1.0; + + if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER) + { + //if (!g_iUseLocale) + if ( LC_IsYMIR() ) + adjust = 1.4f; + else + adjust = 1.35f; + } + + iAmount = (int) (iAmount * adjust); + } + //////////////////////////////////////////////////////////////////////////////// + //sys_log(0, "name: %s skill: %s amount %d to %s", m_pkChr->GetName(), m_pkSk->szName, iAmount, pkChrVictim->GetName()); + + iDam = CalcBattleDamage(iAmount, m_pkChr->GetLevel(), pkChrVictim->GetLevel()); + + if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() != (DWORD) pkChrVictim->GetVID()) + { + // + iDam = (int) (iDam * m_pkSk->kSplashAroundDamageAdjustPoly.Eval()); + } + + // TODO ų Ÿ ؾѴ. + EDamageType dt = DAMAGE_TYPE_NONE; + + switch (m_pkSk->bSkillAttrType) + { + case SKILL_ATTR_TYPE_NORMAL: + break; + + case SKILL_ATTR_TYPE_MELEE: + { + dt = DAMAGE_TYPE_MELEE; + + LPITEM pkWeapon = m_pkChr->GetWear(WEAR_WEAPON); + + if (pkWeapon) + switch (pkWeapon->GetSubType()) + { + case WEAPON_SWORD: + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_SWORD)) / 100; + break; + + case WEAPON_TWO_HANDED: + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_TWOHAND)) / 100; + // հ Ƽ 10% + //iDam = iDam * 95 / 100; + + break; + + case WEAPON_DAGGER: + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_DAGGER)) / 100; + break; + + case WEAPON_BELL: + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_BELL)) / 100; + break; + + case WEAPON_FAN: + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_FAN)) / 100; + break; + } + + if (!bIgnoreDefense) + iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE); + } + break; + + case SKILL_ATTR_TYPE_RANGE: + dt = DAMAGE_TYPE_RANGE; + // ƾƾƾ + // ߴ װ ־ ٽϸ + //iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE); + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_BOW)) / 100; + break; + + case SKILL_ATTR_TYPE_MAGIC: + dt = DAMAGE_TYPE_MAGIC; + iDam = CalcAttBonus(m_pkChr, pkChrVictim, iDam); + // ƾƾƾ + // ߴ װ ־ ٽϸ + //iDam -= pkChrVictim->GetPoint(POINT_MAGIC_DEF_GRADE); + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_MAGIC)) / 100; + break; + + default: + sys_err("Unknown skill attr type %u vnum %u", m_pkSk->bSkillAttrType, m_pkSk->dwVnum); + break; + } + + // + // 20091109 ų Ӽ û ۾ + // ų ̺ SKILL_FLAG_WIND, SKILL_FLAG_ELEC, SKILL_FLAG_FIRE ų + // Ƿ RESIST_WIND, RESIST_ELEC, RESIST_FIRE ʰ ־. + // + // PvP PvE뷱 и ǵ NPC ϵ 뷱 + // ϱ mob_proto RESIST_MAGIC RESIST_WIND, RESIST_ELEC, RESIST_FIRE + // Ͽ. + // + if (pkChrVictim->IsNPC()) + { + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_WIND)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_WIND)) / 100; + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_ELEC)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_ELEC)) / 100; + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE)) + { + iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_FIRE)) / 100; + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_COMPUTE_MAGIC_DAMAGE)) + dt = DAMAGE_TYPE_MAGIC; + + if (pkChrVictim->CanBeginFight()) + pkChrVictim->BeginFight(m_pkChr); + + if (m_pkSk->dwVnum == SKILL_CHAIN) + sys_log(0, "%s CHAIN INDEX %d DAM %d DT %d", m_pkChr->GetName(), m_pkChr->GetChainLightningIndex() - 1, iDam, dt); + + { + BYTE AntiSkillID = 0; + + switch (m_pkSk->dwVnum) + { + case SKILL_TANHWAN: AntiSkillID = SKILL_7_A_ANTI_TANHWAN; break; + case SKILL_AMSEOP: AntiSkillID = SKILL_7_B_ANTI_AMSEOP; break; + case SKILL_SWAERYUNG: AntiSkillID = SKILL_7_C_ANTI_SWAERYUNG; break; + case SKILL_YONGBI: AntiSkillID = SKILL_7_D_ANTI_YONGBI; break; + case SKILL_GIGONGCHAM: AntiSkillID = SKILL_8_A_ANTI_GIGONGCHAM; break; + case SKILL_YEONSA: AntiSkillID = SKILL_8_B_ANTI_YEONSA; break; + case SKILL_MAHWAN: AntiSkillID = SKILL_8_C_ANTI_MAHWAN; break; + case SKILL_BYEURAK: AntiSkillID = SKILL_8_D_ANTI_BYEURAK; break; + } + + if (0 != AntiSkillID) + { + BYTE AntiSkillLevel = pkChrVictim->GetSkillLevel(AntiSkillID); + + if (0 != AntiSkillLevel) + { + CSkillProto* pkSk = CSkillManager::instance().Get(AntiSkillID); + if (!pkSk) + { + sys_err ("There is no anti skill(%d) in skill proto", AntiSkillID); + } + else + { + pkSk->SetPointVar("k", 1.0f * pkChrVictim->GetSkillPower(AntiSkillID) * pkSk->bMaxLevel / 100); + + double ResistAmount = pkSk->kPointPoly.Eval(); + + sys_log(0, "ANTI_SKILL: Resist(%lf) Orig(%d) Reduce(%d)", ResistAmount, iDam, int(iDam * (ResistAmount/100.0))); + + iDam -= iDam * (ResistAmount/100.0); + } + } + } + } + + if (!pkChrVictim->Damage(m_pkChr, iDam, dt) && !pkChrVictim->IsStun()) + { + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_REMOVE_GOOD_AFFECT)) + { + int iAmount2 = (int) m_pkSk->kPointPoly2.Eval(); + int iDur2 = (int) m_pkSk->kDurationPoly2.Eval(); + iDur2 += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (number(1, 100) <= iAmount2) + { + pkChrVictim->RemoveGoodAffect(); + pkChrVictim->AddAffect(m_pkSk->dwVnum, POINT_NONE, 0, AFF_PABEOP, iDur2, 0, true); + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW | SKILL_FLAG_STUN | SKILL_FLAG_FIRE_CONT | SKILL_FLAG_POISON)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + int iDur = (int) m_pkSk->kDurationPoly2.Eval(); + + iDur += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_STUN)) + { + SkillAttackAffect(pkChrVictim, iPct, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iDur, m_pkSk->szName); + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW)) + { + SkillAttackAffect(pkChrVictim, iPct, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, iDur, m_pkSk->szName); + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE_CONT)) + { + m_pkSk->SetDurationVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100); + m_pkSk->SetDurationVar("iq", m_pkChr->GetPoint(POINT_IQ)); + + iDur = (int)m_pkSk->kDurationPoly2.Eval(); + int bonus = m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (bonus != 0) + { + iDur += bonus / 2; + } + + if (number(1, 100) <= iDur) + { + pkChrVictim->AttackedByFire(m_pkChr, iPct, 5); + } + } + else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_POISON)) + { + if (number(1, 100) <= iPct) + pkChrVictim->AttackedByPoison(m_pkChr); + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && + !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) + { + float fCrushSlidingLength = 200; + + if (m_pkChr->IsNPC()) + fCrushSlidingLength = 400; + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH_LONG)) + fCrushSlidingLength *= 2; + + float fx, fy; + float degree = GetDegreeFromPositionXY(m_pkChr->GetX(), m_pkChr->GetY(), pkChrVictim->GetX(), pkChrVictim->GetY()); + + if (m_pkSk->dwVnum == SKILL_HORSE_WILDATTACK) + { + degree -= m_pkChr->GetRotation(); + degree = fmod(degree, 360.0f) - 180.0f; + + if (degree > 0) + degree = m_pkChr->GetRotation() + 90.0f; + else + degree = m_pkChr->GetRotation() - 90.0f; + } + + GetDeltaByDegree(degree, fCrushSlidingLength, &fx, &fy); + sys_log(0, "CRUSH! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); + long tx = (long)(pkChrVictim->GetX()+fx); + long ty = (long)(pkChrVictim->GetY()+fy); + + pkChrVictim->Sync(tx, ty); + pkChrVictim->Goto(tx, ty); + pkChrVictim->CalculateMoveDuration(); + + if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) + { + //if (!g_iUseLocale) + if (LC_IsYMIR()) + SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 3, m_pkSk->szName); + else + SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); + } + else + { + pkChrVictim->SyncPacket(); + } + } + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_HP_ABSORB)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + m_pkChr->PointChange(POINT_HP, iDam * iPct / 100); + } + + if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SP_ABSORB)) + { + int iPct = (int) m_pkSk->kPointPoly2.Eval(); + m_pkChr->PointChange(POINT_SP, iDam * iPct / 100); + } + + if (m_pkSk->dwVnum == SKILL_CHAIN && m_pkChr->GetChainLightningIndex() < m_pkChr->GetChainLightningMaxCount()) + { + chain_lightning_event_info* info = AllocEventInfo(); + + info->dwVictim = pkChrVictim->GetVID(); + info->dwChr = m_pkChr->GetVID(); + + event_create(ChainLightningEvent, info, passes_per_sec / 5); + } + if(test_server) + sys_log(0, "FuncSplashDamage End :%s ", m_pkChr->GetName()); + } + + int m_x; + int m_y; + CSkillProto * m_pkSk; + LPCHARACTER m_pkChr; + int m_iAmount; + int m_iAG; + int m_iCount; + int m_iMaxHit; + LPITEM m_pkWeapon; + bool m_bDisableCooltime; + TSkillUseInfo* m_pInfo; + BYTE m_bUseSkillPower; +}; + +struct FuncSplashAffect +{ + FuncSplashAffect(LPCHARACTER ch, int x, int y, int iDist, DWORD dwVnum, BYTE bPointOn, int iAmount, DWORD dwAffectFlag, int iDuration, int iSPCost, bool bOverride, int iMaxHit) + { + m_x = x; + m_y = y; + m_iDist = iDist; + m_dwVnum = dwVnum; + m_bPointOn = bPointOn; + m_iAmount = iAmount; + m_dwAffectFlag = dwAffectFlag; + m_iDuration = iDuration; + m_iSPCost = iSPCost; + m_bOverride = bOverride; + m_pkChrAttacker = ch; + m_iMaxHit = iMaxHit; + m_iCount = 0; + } + + void operator () (LPENTITY ent) + { + if (m_iMaxHit && m_iMaxHit <= m_iCount) + return; + + if (ent->IsType(ENTITY_CHARACTER)) + { + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (test_server) + sys_log(0, "FuncSplashAffect step 1 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration); + if (DISTANCE_APPROX(m_x - pkChr->GetX(), m_y - pkChr->GetY()) < m_iDist) + { + if (test_server) + sys_log(0, "FuncSplashAffect step 2 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration); + if (m_dwVnum == SKILL_TUSOK) + if (pkChr->CanBeginFight()) + pkChr->BeginFight(m_pkChrAttacker); + + if (pkChr->IsPC() && m_dwVnum == SKILL_TUSOK) + pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration/3, m_iSPCost, m_bOverride); + else + pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration, m_iSPCost, m_bOverride); + + m_iCount ++; + } + } + } + + LPCHARACTER m_pkChrAttacker; + int m_x; + int m_y; + int m_iDist; + DWORD m_dwVnum; + BYTE m_bPointOn; + int m_iAmount; + DWORD m_dwAffectFlag; + int m_iDuration; + int m_iSPCost; + bool m_bOverride; + int m_iMaxHit; + int m_iCount; +}; + +EVENTINFO(skill_gwihwan_info) +{ + DWORD pid; + BYTE bsklv; + + skill_gwihwan_info() + : pid( 0 ) + , bsklv( 0 ) + { + } +}; + +EVENTFUNC(skill_gwihwan_event) +{ + skill_gwihwan_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "skill_gwihwan_event> Null pointer" ); + return 0; + } + + DWORD pid = info->pid; + BYTE sklv= info->bsklv; + LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pid); + + if (!ch) + return 0; + + int percent = 20 * sklv - 1; + + if (number(1, 100) <= percent) + { + PIXEL_POSITION pos; + + // + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + { + sys_log(1, "Recall: %s %d %d -> %d %d", ch->GetName(), ch->GetX(), ch->GetY(), pos.x, pos.y); + ch->WarpSet(pos.x, pos.y); + } + else + { + sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", ch->GetName(), ch->GetX(), ch->GetY()); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + } + else + { + // + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȯ Ͽϴ.")); + } + return 0; +} + +int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTarget, BYTE bSkillLevel) +{ + if (GetMountVnum()) + return BATTLE_NONE; + + if (IsPolymorphed()) + return BATTLE_NONE; + + if (g_bSkillDisable) + return BATTLE_NONE; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return BATTLE_NONE; + + if (test_server) + { + sys_log(0, "ComputeSkillAtPosition %s vnum %d x %d y %d level %d", + GetName(), dwVnum, posTarget.x, posTarget.y, bSkillLevel); + } + + // ų ġ . + //if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + // posTarget = GetXYZ(); + + // ÷ ƴ ų ̸ ̻ϴ + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + return BATTLE_NONE; + + if (0 == bSkillLevel) + { + if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0) + { + return BATTLE_NONE; + } + } + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, this, true, false)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMagicDamage(this, this)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, this, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", 0); + } + } + + if (pkSk->bPointOn == POINT_MOV_SPEED) + { + pkSk->SetPointVar("maxv", this->GetLimitPoint(POINT_MOV_SPEED)); + } + + pkSk->SetPointVar("lv", GetLevel()); + pkSk->SetPointVar("iq", GetPoint(POINT_IQ)); + pkSk->SetPointVar("str", GetPoint(POINT_ST)); + pkSk->SetPointVar("dex", GetPoint(POINT_DX)); + pkSk->SetPointVar("con", GetPoint(POINT_HT)); + pkSk->SetPointVar("maxhp", this->GetMaxHP()); + pkSk->SetPointVar("maxsp", this->GetMaxSP()); + pkSk->SetPointVar("chain", 0); + pkSk->SetPointVar("ar", CalcAttackRating(this, this)); + pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE)); + pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS)); + pkSk->SetPointVar("horse_level", GetHorseLevel()); + + if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL) + OnMove(true); + + LPITEM pkWeapon = GetWear(WEAR_WEAPON); + + SetPolyVarForAttack(this, pkSk, pkWeapon); + + pkSk->SetDurationVar("k", k/*bSkillLevel*/); + + int iAmount = (int) pkSk->kPointPoly.Eval(); + int iAmount2 = (int) pkSk->kPointPoly2.Eval(); + + // ADD_GRANDMASTER_SKILL + int iAmount3 = (int) pkSk->kPointPoly3.Eval(); + + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + /* + if (iAmount >= 0) + iAmount += (int) m_pkSk->kMasterBonusPoly.Eval(); + else + iAmount -= (int) m_pkSk->kMasterBonusPoly.Eval(); + */ + iAmount = (int) pkSk->kMasterBonusPoly.Eval(); + } + + if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE) + { + ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT)) + { + if (number(1, 100) <= iAmount2) + { + RemoveBadAffect(); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + // + // ų + // + bool bAdded = false; + + if (pkSk->bPointOn == POINT_HP && iAmount < 0) + { + int iAG = 0; + + FuncSplashDamage f(posTarget.x, posTarget.y, pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel)); + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + { + if (GetSectree()) + GetSectree()->ForEachAround(f); + } + else + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill call FuncSplashDamage %s", GetName()); + f(this); + } + } + else + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill no damage %d %s", iAmount, GetName()); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (IsPC()) + if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // ų Ÿ ó ʴ´. + if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG) + { + //if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill cannot hit %s", GetName()); + return BATTLE_NONE; + } + + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + } + + if (pkSk->bPointOn2 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + sys_log(1, "try second %u %d %d", pkSk->dwVnum, pkSk->bPointOn2, iDur); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded); + else + { + if (GetSectree()) + { + FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded, pkSk->lMaxHit); + GetSectree()->ForEachAround(f); + } + } + } + else + { + PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_DAMAGE; + } + else + { + bool bAdded = false; + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + // AffectFlag ų, toggle ϴ ƴ϶.. + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + pkSk->dwAffectFlag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + !bAdded); + + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn, iAmount); + } + + if (pkSk->bPointOn2 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + bAdded = true; + } + else + { + PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE) + { + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded); + } + else + { + PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_NONE; + } +} + +// bSkillLevel ڰ 0 ƴ 쿡 m_abSkillLevels ʰ +// bSkillLevel Ѵ. +int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) +{ + const bool bCanUseHorseSkill = CanUseHorseSkill(); + + // Ÿ ų ¶ return + if (false == bCanUseHorseSkill && true == IsRiding()) + return BATTLE_NONE; + + if (IsPolymorphed()) + return BATTLE_NONE; + + if (g_bSkillDisable) + return BATTLE_NONE; + + CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return BATTLE_NONE; + + if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE) + return BATTLE_NONE; + + + // 濡 ƴϸ Ѵ. + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + pkVictim = this; + + if (!pkVictim) + { + if (test_server) + sys_log(0, "ComputeSkill: %s Victim == null, skill %d", GetName(), dwVnum); + + return BATTLE_NONE; + } + + if (pkSk->dwTargetRange && DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()) >= pkSk->dwTargetRange + 50) + { + if (test_server) + sys_log(0, "ComputeSkill: Victim too far, skill %d : %s to %s (distance %u limit %u)", + dwVnum, + GetName(), + pkVictim->GetName(), + (long)DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()), + pkSk->dwTargetRange); + + return BATTLE_NONE; + } + + if (0 == bSkillLevel) + { + if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0) + { + if (test_server) + sys_log(0, "ComputeSkill : name:%s vnum:%d skillLevelBySkill : %d ", GetName(), pkSk->dwVnum, bSkillLevel); + return BATTLE_NONE; + } + } + + if (pkVictim->IsAffectFlag(AFF_PABEOP) && pkVictim->IsGoodAffect(dwVnum)) + { + return BATTLE_NONE; + } + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + if (pkSk->dwType == SKILL_TYPE_HORSE) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false)); + } + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE)) + { + pkSk->SetPointVar("atk", CalcMagicDamage(this, pkVictim)); + } + else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE)) + { + LPITEM pkBow, pkArrow; + if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1)) + { + pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true)); + } + else + { + pkSk->SetPointVar("atk", 0); + } + } + + if (pkSk->bPointOn == POINT_MOV_SPEED) + { + pkSk->SetPointVar("maxv", pkVictim->GetLimitPoint(POINT_MOV_SPEED)); + } + + pkSk->SetPointVar("lv", GetLevel()); + pkSk->SetPointVar("iq", GetPoint(POINT_IQ)); + pkSk->SetPointVar("str", GetPoint(POINT_ST)); + pkSk->SetPointVar("dex", GetPoint(POINT_DX)); + pkSk->SetPointVar("con", GetPoint(POINT_HT)); + pkSk->SetPointVar("maxhp", pkVictim->GetMaxHP()); + pkSk->SetPointVar("maxsp", pkVictim->GetMaxSP()); + pkSk->SetPointVar("chain", 0); + pkSk->SetPointVar("ar", CalcAttackRating(this, pkVictim)); + pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE)); + pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS)); + pkSk->SetPointVar("horse_level", GetHorseLevel()); + + if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL) + OnMove(true); + + LPITEM pkWeapon = GetWear(WEAR_WEAPON); + + SetPolyVarForAttack(this, pkSk, pkWeapon); + + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/); + + int iAmount = (int) pkSk->kPointPoly.Eval(); + int iAmount2 = (int) pkSk->kPointPoly2.Eval(); + int iAmount3 = (int) pkSk->kPointPoly3.Eval(); + + if (test_server && IsPC()) + sys_log(0, "iAmount: %d %d %d , atk:%f skLevel:%f k:%f GetSkillPower(%d) MaxLevel:%d Per:%f", + iAmount, iAmount2, iAmount3, + pkSk->kPointPoly.GetVar("atk"), + pkSk->kPointPoly.GetVar("k"), + k, + GetSkillPower(pkSk->dwVnum, bSkillLevel), + pkSk->bMaxLevel, + pkSk->bMaxLevel/100 + ); + + // ADD_GRANDMASTER_SKILL + if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + iAmount = (int) pkSk->kMasterBonusPoly.Eval(); + } + + if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE) + { + ChatPacket(CHAT_TYPE_INFO, "ȿ ϴ. ų Ȯϼ"); + } + // END_OF_ADD_GRANDMASTER_SKILL + + //sys_log(0, "XXX SKILL Calc %d Amount %d", dwVnum, iAmount); + + // REMOVE_BAD_AFFECT_BUG_FIX + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT)) + { + if (number(1, 100) <= iAmount2) + { + pkVictim->RemoveBadAffect(); + } + } + // END_OF_REMOVE_BAD_AFFECT_BUG_FIX + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE) && + !(pkSk->dwVnum == SKILL_MUYEONG && pkVictim == this) && !(pkSk->IsChargeSkill() && pkVictim == this)) + { + bool bAdded = false; + + if (pkSk->bPointOn == POINT_HP && iAmount < 0) + { + int iAG = 0; + + + FuncSplashDamage f(pkVictim->GetX(), pkVictim->GetY(), pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel)); + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + { + if (pkVictim->GetSectree()) + pkVictim->GetSectree()->ForEachAround(f); + } + else + { + f(pkVictim); + } + } + else + { + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + + if (IsPC()) + if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // ų Ÿ ó ʴ´. + if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG) + { + return BATTLE_NONE; + } + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + bAdded = true; + } + } + + if (pkSk->bPointOn2 != POINT_NONE && !pkSk->IsChargeSkill()) + { + pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + } + + // ADD_GRANDMASTER_SKILL + if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_DAMAGE; + } + else + { + if (dwVnum == SKILL_MUYEONG) + { + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + int iDur = (long) pkSk->kDurationPoly.Eval(); + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (pkVictim == this) + AddAffect(dwVnum, + POINT_NONE, 0, + AFF_MUYEONG, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + true); + + return BATTLE_NONE; + } + + bool bAdded = false; + pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly.Eval(); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + // AffectFlag ų, toggle ϴ ƴ϶.. + pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/); + + if (pkSk->bPointOn2 != POINT_NONE) + { + pkVictim->RemoveAffect(pkSk->dwVnum); + + int iDur2 = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur2 > 0) + { + if (test_server) + sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d", + GetName(), + pkSk->szName, + iDur2, + pkSk->bPointOn2, + iAmount2); + + iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS); + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false); + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + + DWORD affact_flag = pkSk->dwAffectFlag; + + // ADD_GRANDMASTER_SKILL + //if (g_iUseLocale) + if ( !LC_IsYMIR() ) + { + if ((pkSk->dwVnum == SKILL_CHUNKEON && GetUsedSkillMasterType(pkSk->dwVnum) < SKILL_GRAND_MASTER)) + affact_flag = AFF_CHEONGEUN_WITH_FALL; + } + else + { + if ((pkSk->dwVnum == SKILL_CHUNKEON && GetUsedSkillMasterType(pkSk->dwVnum) < SKILL_MASTER)) + affact_flag = AFF_CHEONGEUN_WITH_FALL; + } + // END_OF_ADD_GRANDMASTER_SKILL + + pkVictim->AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + affact_flag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + false); + } + else + { + if (test_server) + sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d", + GetName(), + pkSk->szName, + iDur, + pkSk->bPointOn, + iAmount); + + pkVictim->AddAffect(pkSk->dwVnum, + pkSk->bPointOn, + iAmount, + pkSk->dwAffectFlag, + iDur, + (long) pkSk->kDurationSPCostPoly.Eval(), + // ADD_GRANDMASTER_SKILL + !bAdded); + // END_OF_ADD_GRANDMASTER_SKILL + } + + bAdded = true; + } + else + { + if (!pkSk->IsChargeSkill()) + pkVictim->PointChange(pkSk->bPointOn, iAmount); + + if (pkSk->bPointOn2 != POINT_NONE) + { + pkVictim->RemoveAffect(pkSk->dwVnum); + + int iDur2 = (int) pkSk->kDurationPoly2.Eval(); + + if (iDur2 > 0) + { + iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (pkSk->IsChargeSkill()) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, AFF_TANHWAN_DASH, iDur2, 0, false); + else + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false); + } + else + { + pkVictim->PointChange(pkSk->bPointOn2, iAmount2); + } + + } + } + + // ADD_GRANDMASTER_SKILL + if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER) + { + + pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/); + int iDur = (int) pkSk->kDurationPoly3.Eval(); + + sys_log(0, "try third %u %d %d %d 1894", pkSk->dwVnum, pkSk->bPointOn3, iDur, iAmount3); + + if (iDur > 0) + { + iDur += GetPoint(POINT_PARTY_BUFFER_BONUS); + + if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH)) + pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded); + else + { + if (pkVictim->GetSectree()) + { + FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit); + pkVictim->GetSectree()->ForEachAround(f); + } + } + + bAdded = true; + } + else + { + pkVictim->PointChange(pkSk->bPointOn3, iAmount3); + } + } + // END_OF_ADD_GRANDMASTER_SKILL + + return BATTLE_NONE; + } +} + +bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster) +{ + if (false == CanUseSkill(dwVnum)) + return false; + + // NO_GRANDMASTER + if (test_server) + { + if (quest::CQuestManager::instance().GetEventFlag("no_grand_master")) + { + bUseGrandMaster = false; + } + } + // END_OF_NO_GRANDMASTER + + if (g_bSkillDisable) + return false; + + if (IsObserverMode()) + return false; + + if (!CanMove()) + return false; + + if (IsPolymorphed()) + return false; + + const bool bCanUseHorseSkill = CanUseHorseSkill(); + + + if (dwVnum == SKILL_HORSE_SUMMON) + { + if (GetSkillLevel(dwVnum) == 0) + return false; + + if (GetHorseLevel() <= 0) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. ° ãư.")); + else + ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯ ϼ.")); + + return true; + } + + // Ÿ ų ¶ return false + if (false == bCanUseHorseSkill && true == IsRiding()) + return false; + + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + sys_log(0, "%s: USE_SKILL: %d pkVictim %p", GetName(), dwVnum, get_pointer(pkVictim)); + + if (!pkSk) + return false; + + if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE) + return BATTLE_NONE; + + if (GetSkillLevel(dwVnum) == 0) + return false; + + + // NO_GRANDMASTER + if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER) + bUseGrandMaster = false; + // END_OF_NO_GRANDMASTER + + // MINING + if (GetWear(WEAR_WEAPON) && (GetWear(WEAR_WEAPON)->GetType() == ITEM_ROD || GetWear(WEAR_WEAPON)->GetType() == ITEM_PICK)) + return false; + // END_OF_MINING + + m_SkillUseInfo[dwVnum].TargetVIDMap.clear(); + + if (pkSk->IsChargeSkill()) + { + if (IsAffectFlag(AFF_TANHWAN_DASH) || pkVictim && pkVictim != this) + { + if (!pkVictim) + return false; + + if (!IsAffectFlag(AFF_TANHWAN_DASH)) + { + if (!UseSkill(dwVnum, this)) + return false; + } + + m_SkillUseInfo[dwVnum].SetMainTargetVID(pkVictim->GetVID()); + // DASH źȯ ݱ + ComputeSkill(dwVnum, pkVictim); + RemoveAffect(dwVnum); + return true; + } + } + + if (dwVnum == SKILL_COMBO) + { + if (m_bComboIndex) + m_bComboIndex = 0; + else + m_bComboIndex = GetSkillLevel(SKILL_COMBO); + + ChatPacket(CHAT_TYPE_COMMAND, "combo %d", m_bComboIndex); + return true; + } + + // Toggle SP (SelfOnly ) + if ((0 != pkSk->dwAffectFlag || pkSk->dwVnum == SKILL_MUYEONG) && (pkSk->dwFlag & SKILL_FLAG_TOGGLE) && RemoveAffect(pkSk->dwVnum)) + { + return true; + } + + if (IsAffectFlag(AFF_REVIVE_INVISIBLE)) + RemoveAffect(AFFECT_REVIVE_INVISIBLE); + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum) * pkSk->bMaxLevel / 100; + + pkSk->SetPointVar("k", k); + pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k); + + // Ÿ üũ + pkSk->kCooldownPoly.SetVar("k", k); + int iCooltime = (int) pkSk->kCooldownPoly.Eval(); + int lMaxHit = pkSk->lMaxHit ? pkSk->lMaxHit : -1; + + pkSk->SetSPCostVar("k", k); + + DWORD dwCur = get_dword_time(); + + if (dwVnum == SKILL_TERROR && m_SkillUseInfo[dwVnum].bUsed && m_SkillUseInfo[dwVnum].dwNextSkillUsableTime > dwCur ) + { + sys_log(0, " SKILL_TERROR's Cooltime is not delta over %u", m_SkillUseInfo[dwVnum].dwNextSkillUsableTime - dwCur ); + return false; + } + + int iNeededSP = 0; + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_HP_AS_COST)) + { + pkSk->SetSPCostVar("maxhp", GetMaxHP()); + pkSk->SetSPCostVar("v", GetHP()); + iNeededSP = (int) pkSk->kSPCostPoly.Eval(); + + // ADD_GRANDMASTER_SKILL + if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster) + { + iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval(); + } + // END_OF_ADD_GRANDMASTER_SKILL + + if (GetHP() < iNeededSP) + return false; + + PointChange(POINT_HP, -iNeededSP); + } + else + { + // SKILL_FOMULA_REFACTORING + pkSk->SetSPCostVar("maxhp", GetMaxHP()); + pkSk->SetSPCostVar("maxv", GetMaxSP()); + pkSk->SetSPCostVar("v", GetSP()); + + iNeededSP = (int) pkSk->kSPCostPoly.Eval(); + + if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster) + { + iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval(); + } + // END_OF_SKILL_FOMULA_REFACTORING + + if (GetSP() < iNeededSP) + return false; + + if (test_server) + ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s SPҸ: %d"), pkSk->szName, iNeededSP); + + PointChange(POINT_SP, -iNeededSP); + } + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + pkVictim = this; + + if (pkSk->dwVnum == SKILL_MUYEONG || pkSk->IsChargeSkill() && !IsAffectFlag(AFF_TANHWAN_DASH) && !pkVictim) + { + // ó ϴ ڽſ Affect δ. + pkVictim = this; + } + + int iSplashCount = 1; + + if (false == m_bDisableCooltime) + { + if (false == + m_SkillUseInfo[dwVnum].UseSkill( + bUseGrandMaster, + (NULL != pkVictim && SKILL_HORSE_WILDATTACK != dwVnum) ? pkVictim->GetVID() : NULL, + ComputeCooltime(iCooltime * 1000), + iSplashCount, + lMaxHit)) + { + if (test_server) + ChatPacket(CHAT_TYPE_NOTICE, "cooltime not finished %s %d", pkSk->szName, iCooltime); + + return false; + } + } + + if (dwVnum == SKILL_CHAIN) + { + ResetChainLightningIndex(); + AddChainLightningExcept(pkVictim); + } + + + if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY)) + ComputeSkill(dwVnum, this); + else if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK)) + ComputeSkill(dwVnum, pkVictim); + else if (dwVnum == SKILL_BYEURAK) + ComputeSkill(dwVnum, pkVictim); + else if (dwVnum == SKILL_MUYEONG || pkSk->IsChargeSkill()) + ComputeSkill(dwVnum, pkVictim); + + m_dwLastSkillTime = get_dword_time(); + + return true; +} + +int CHARACTER::GetUsedSkillMasterType(DWORD dwVnum) +{ + const TSkillUseInfo& rInfo = m_SkillUseInfo[dwVnum]; + + if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER) + return GetSkillMasterType(dwVnum); + + if (rInfo.isGrandMaster) + return GetSkillMasterType(dwVnum); + + return MIN(GetSkillMasterType(dwVnum), SKILL_MASTER); +} + +int CHARACTER::GetSkillMasterType(DWORD dwVnum) const +{ + if (!IsPC()) + return 0; + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + return m_pSkillLevels ? m_pSkillLevels[dwVnum].bMasterType:SKILL_NORMAL; +} + +int CHARACTER::GetSkillPower(DWORD dwVnum, BYTE bLevel) const +{ + // ξ + if (dwVnum >= SKILL_LANGUAGE1 && dwVnum <= SKILL_LANGUAGE3 && IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE)) + { + return 100; + } + + if (dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END) + { + if (GetGuild()) + return 100 * GetGuild()->GetSkillLevel(dwVnum) / 7 / 7; + else + return 0; + } + + if (bLevel) + { + //SKILL_POWER_BY_LEVEL + return GetSkillPowerByLevel(bLevel, true); + //END_SKILL_POWER_BY_LEVEL; + } + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + //SKILL_POWER_BY_LEVEL + return GetSkillPowerByLevel(GetSkillLevel(dwVnum)); + //SKILL_POWER_BY_LEVEL +} + +int CHARACTER::GetSkillLevel(DWORD dwVnum) const +{ + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("%s skill vnum overflow %u", GetName(), dwVnum); + sys_log(0, "%s skill vnum overflow %u", GetName(), dwVnum); + return 0; + } + + return MIN(SKILL_MAX_LEVEL, m_pSkillLevels ? m_pSkillLevels[dwVnum].bLevel : 0); +} + +EVENTFUNC(skill_muyoung_event) +{ + char_event_info* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "skill_muyoung_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + + if (ch == NULL) { // + return 0; + } + + if (!ch->IsAffectFlag(AFF_MUYEONG)) + { + ch->StopMuyeongEvent(); + return 0; + } + + // 1. Find Victim + FFindNearVictim f(ch, ch); + if (ch->GetSectree()) + { + ch->GetSectree()->ForEachAround(f); + // 2. Shoot! + if (f.GetVictim()) + { + ch->CreateFly(FLY_SKILL_MUYEONG, f.GetVictim()); + ch->ComputeSkill(SKILL_MUYEONG, f.GetVictim()); + } + } + + return PASSES_PER_SEC(3); +} + +void CHARACTER::StartMuyeongEvent() +{ + if (m_pkMuyeongEvent) + return; + + char_event_info* info = AllocEventInfo(); + + info->ch = this; + m_pkMuyeongEvent = event_create(skill_muyoung_event, info, PASSES_PER_SEC(1)); +} + +void CHARACTER::StopMuyeongEvent() +{ + event_cancel(&m_pkMuyeongEvent); +} + +void CHARACTER::SkillLearnWaitMoreTimeMessage(DWORD ms) +{ + //const char* str = ""; + // + if (ms < 3 * 60) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" ̱߰. . ̴ ⸦ Ű.")); + else if (ms < 5 * 60) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("׷, õõ. õõ, ׷ !")); + else if (ms < 10 * 60) // 10 + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("׷, ̾. ü Ⱑ 游.")); + else if (ms < 30 * 60) // 30 + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" о! ޿ ִ ſ ⸦ ⸸ ϸ,")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("װ ž!")); + } + else if (ms < 1 * 3600) // 1ð + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" å ̾! ̰ ־!")); + else if (ms < 2 * 3600) // 2ð + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" Ҿ! ݸ !")); + else if (ms < 3 * 3600) + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Ҿ! ݸ ̴!")); + else if (ms < 6 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("å嵵 ʾұ.")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" ȿ .")); + } + else if (ms < 12 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(" .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT(", ⼼ !")); + } + else if (ms < 18 * 3600) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ƴ  о Ӹ .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ϱ Ⱦ.")); + } + else //if (ms < 2 * 86400) + { + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("ŭ бⰡ ʱ. ص ư 뵵 .")); + ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("̷ ΰ ȵȴٱ.")); + } + /* + str = "30%"; + else if (ms < 3 * 86400) + str = "10%"; + else if (ms < 4 * 86400) + str = "5%"; + else + str = "0%";*/ + + //ChatPacket(CHAT_TYPE_TALKING, "%s", str); +} + +void CHARACTER::DisableCooltime() +{ + m_bDisableCooltime = true; +} + +bool CHARACTER::HasMobSkill() const +{ + return CountMobSkill() > 0; +} + +size_t CHARACTER::CountMobSkill() const +{ + if (!m_pkMobData) + return 0; + + size_t c = 0; + + for (size_t i = 0; i < MOB_SKILL_MAX_NUM; ++i) + if (m_pkMobData->m_table.Skills[i].dwVnum) + ++c; + + return c; +} + +const TMobSkillInfo* CHARACTER::GetMobSkill(unsigned int idx) const +{ + if (idx >= MOB_SKILL_MAX_NUM) + return NULL; + + if (!m_pkMobData) + return NULL; + + if (0 == m_pkMobData->m_table.Skills[idx].dwVnum) + return NULL; + + return &m_pkMobData->m_mobSkillInfo[idx]; +} + +bool CHARACTER::CanUseMobSkill(unsigned int idx) const +{ + const TMobSkillInfo* pInfo = GetMobSkill(idx); + + if (!pInfo) + return false; + + if (m_adwMobSkillCooltime[idx] > get_dword_time()) + return false; + + if (number(0, 1)) + return false; + + return true; +} + +EVENTINFO(mob_skill_event_info) +{ + DynamicCharacterPtr ch; + PIXEL_POSITION pos; + DWORD vnum; + int index; + BYTE level; + + mob_skill_event_info() + : ch() + , pos() + , vnum(0) + , index(0) + , level(0) + { + } +}; + +EVENTFUNC(mob_skill_hit_event) +{ + mob_skill_event_info * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "mob_skill_event_info> Null pointer" ); + return 0; + } + + // + LPCHARACTER ch = info->ch; + if (ch == NULL) { + return 0; + } + + ch->ComputeSkillAtPosition(info->vnum, info->pos, info->level); + ch->m_mapMobSkillEvent.erase(info->index); + + return 0; +} + +bool CHARACTER::UseMobSkill(unsigned int idx) +{ + if (IsPC()) + return false; + + const TMobSkillInfo* pInfo = GetMobSkill(idx); + + if (!pInfo) + return false; + + DWORD dwVnum = pInfo->dwSkillVnum; + CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum); + + if (!pkSk) + return false; + + const float k = 1.0 * GetSkillPower(pkSk->dwVnum, pInfo->bSkillLevel) * pkSk->bMaxLevel / 100; + + pkSk->kCooldownPoly.SetVar("k", k); + int iCooltime = (int) (pkSk->kCooldownPoly.Eval() * 1000); + + m_adwMobSkillCooltime[idx] = get_dword_time() + iCooltime; + + sys_log(0, "USE_MOB_SKILL: %s idx %d vnum %u cooltime %d", GetName(), idx, dwVnum, iCooltime); + + if (m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.empty()) + { + sys_err("No skill hit data for mob %s index %d", GetName(), idx); + return false; + } + + for (size_t i = 0; i < m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.size(); i++) + { + PIXEL_POSITION pos = GetXYZ(); + const TMobSplashAttackInfo& rInfo = m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack[i]; + + if (rInfo.dwHitDistance) + { + float fx, fy; + GetDeltaByDegree(GetRotation(), rInfo.dwHitDistance, &fx, &fy); + pos.x += (long) fx; + pos.y += (long) fy; + } + + if (rInfo.dwTiming) + { + if (test_server) + sys_log(0, " timing %ums", rInfo.dwTiming); + + mob_skill_event_info* info = AllocEventInfo(); + + info->ch = this; + info->pos = pos; + info->level = pInfo->bSkillLevel; + info->vnum = dwVnum; + info->index = i; + + // Cancel existing event first + itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.find(i); + if (it != m_mapMobSkillEvent.end()) { + LPEVENT existing = it->second; + event_cancel(&existing); + m_mapMobSkillEvent.erase(it); + } + + m_mapMobSkillEvent.insert(std::make_pair(i, event_create(mob_skill_hit_event, info, PASSES_PER_SEC(rInfo.dwTiming) / 1000))); + } + else + { + ComputeSkillAtPosition(dwVnum, pos, pInfo->bSkillLevel); + } + } + + return true; +} + +void CHARACTER::ResetMobSkillCooltime() +{ + memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime)); +} + +bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const +{ + DWORD selfJobGroup = (GetJob()+1) * 10 + GetSkillGroup(); + + const DWORD SKILL_NUM = 158; + static DWORD s_anSkill2JobGroup[SKILL_NUM] = { + 0, // common_skill 0 + 11, // job_skill 1 + 11, // job_skill 2 + 11, // job_skill 3 + 11, // job_skill 4 + 11, // job_skill 5 + 11, // job_skill 6 + 0, // common_skill 7 + 0, // common_skill 8 + 0, // common_skill 9 + 0, // common_skill 10 + 0, // common_skill 11 + 0, // common_skill 12 + 0, // common_skill 13 + 0, // common_skill 14 + 0, // common_skill 15 + 12, // job_skill 16 + 12, // job_skill 17 + 12, // job_skill 18 + 12, // job_skill 19 + 12, // job_skill 20 + 12, // job_skill 21 + 0, // common_skill 22 + 0, // common_skill 23 + 0, // common_skill 24 + 0, // common_skill 25 + 0, // common_skill 26 + 0, // common_skill 27 + 0, // common_skill 28 + 0, // common_skill 29 + 0, // common_skill 30 + 21, // job_skill 31 + 21, // job_skill 32 + 21, // job_skill 33 + 21, // job_skill 34 + 21, // job_skill 35 + 21, // job_skill 36 + 0, // common_skill 37 + 0, // common_skill 38 + 0, // common_skill 39 + 0, // common_skill 40 + 0, // common_skill 41 + 0, // common_skill 42 + 0, // common_skill 43 + 0, // common_skill 44 + 0, // common_skill 45 + 22, // job_skill 46 + 22, // job_skill 47 + 22, // job_skill 48 + 22, // job_skill 49 + 22, // job_skill 50 + 22, // job_skill 51 + 0, // common_skill 52 + 0, // common_skill 53 + 0, // common_skill 54 + 0, // common_skill 55 + 0, // common_skill 56 + 0, // common_skill 57 + 0, // common_skill 58 + 0, // common_skill 59 + 0, // common_skill 60 + 31, // job_skill 61 + 31, // job_skill 62 + 31, // job_skill 63 + 31, // job_skill 64 + 31, // job_skill 65 + 31, // job_skill 66 + 0, // common_skill 67 + 0, // common_skill 68 + 0, // common_skill 69 + 0, // common_skill 70 + 0, // common_skill 71 + 0, // common_skill 72 + 0, // common_skill 73 + 0, // common_skill 74 + 0, // common_skill 75 + 32, // job_skill 76 + 32, // job_skill 77 + 32, // job_skill 78 + 32, // job_skill 79 + 32, // job_skill 80 + 32, // job_skill 81 + 0, // common_skill 82 + 0, // common_skill 83 + 0, // common_skill 84 + 0, // common_skill 85 + 0, // common_skill 86 + 0, // common_skill 87 + 0, // common_skill 88 + 0, // common_skill 89 + 0, // common_skill 90 + 41, // job_skill 91 + 41, // job_skill 92 + 41, // job_skill 93 + 41, // job_skill 94 + 41, // job_skill 95 + 41, // job_skill 96 + 0, // common_skill 97 + 0, // common_skill 98 + 0, // common_skill 99 + 0, // common_skill 100 + 0, // common_skill 101 + 0, // common_skill 102 + 0, // common_skill 103 + 0, // common_skill 104 + 0, // common_skill 105 + 42, // job_skill 106 + 42, // job_skill 107 + 42, // job_skill 108 + 42, // job_skill 109 + 42, // job_skill 110 + 42, // job_skill 111 + 0, // common_skill 112 + 0, // common_skill 113 + 0, // common_skill 114 + 0, // common_skill 115 + 0, // common_skill 116 + 0, // common_skill 117 + 0, // common_skill 118 + 0, // common_skill 119 + 0, // common_skill 120 + 0, // common_skill 121 + 0, // common_skill 122 + 0, // common_skill 123 + 0, // common_skill 124 + 0, // common_skill 125 + 0, // common_skill 126 + 0, // common_skill 127 + 0, // common_skill 128 + 0, // common_skill 129 + 0, // common_skill 130 + 0, // common_skill 131 + 0, // common_skill 132 + 0, // common_skill 133 + 0, // common_skill 134 + 0, // common_skill 135 + 0, // common_skill 136 + 0, // job_skill 137 + 0, // job_skill 138 + 0, // job_skill 139 + 0, // job_skill 140 + 0, // common_skill 141 + 0, // common_skill 142 + 0, // common_skill 143 + 0, // common_skill 144 + 0, // common_skill 145 + 0, // common_skill 146 + 0, // common_skill 147 + 0, // common_skill 148 + 0, // common_skill 149 + 0, // common_skill 150 + 0, // common_skill 151 + 0, // job_skill 152 + 0, // job_skill 153 + 0, // job_skill 154 + 0, // job_skill 155 + 0, // job_skill 156 + 0, // job_skill 157 + }; // s_anSkill2JobGroup + + const DWORD MOTION_MAX_NUM = 124; + const DWORD SKILL_LIST_MAX_COUNT = 5; + + static DWORD s_anMotion2SkillVnumList[MOTION_MAX_NUM][SKILL_LIST_MAX_COUNT] = + { + // ų 罺ųID ڰųID ųID 罺ųID + { 0, 0, 0, 0, 0 }, // 0 + + // 1 ⺻ ų + { 4, 1, 31, 61, 91 }, // 1 + { 4, 2, 32, 62, 92 }, // 2 + { 4, 3, 33, 63, 93 }, // 3 + { 4, 4, 34, 64, 94 }, // 4 + { 4, 5, 35, 65, 95 }, // 5 + { 4, 6, 36, 66, 96 }, // 6 + { 0, 0, 0, 0, 0 }, // 7 + { 0, 0, 0, 0, 0 }, // 8 + // 1 ⺻ ų + + // + { 0, 0, 0, 0, 0 }, // 9 + { 0, 0, 0, 0, 0 }, // 10 + { 0, 0, 0, 0, 0 }, // 11 + { 0, 0, 0, 0, 0 }, // 12 + { 0, 0, 0, 0, 0 }, // 13 + { 0, 0, 0, 0, 0 }, // 14 + { 0, 0, 0, 0, 0 }, // 15 + // + + // 2 ⺻ ų + { 4, 16, 46, 76, 106 }, // 16 + { 4, 17, 47, 77, 107 }, // 17 + { 4, 18, 48, 78, 108 }, // 18 + { 4, 19, 49, 79, 109 }, // 19 + { 4, 20, 50, 80, 110 }, // 20 + { 4, 21, 51, 81, 111 }, // 21 + { 0, 0, 0, 0, 0 }, // 22 + { 0, 0, 0, 0, 0 }, // 23 + // 2 ⺻ ų + + // + { 0, 0, 0, 0, 0 }, // 24 + { 0, 0, 0, 0, 0 }, // 25 + // + + // 1 ų + { 4, 1, 31, 61, 91 }, // 26 + { 4, 2, 32, 62, 92 }, // 27 + { 4, 3, 33, 63, 93 }, // 28 + { 4, 4, 34, 64, 94 }, // 29 + { 4, 5, 35, 65, 95 }, // 30 + { 4, 6, 36, 66, 96 }, // 31 + { 0, 0, 0, 0, 0 }, // 32 + { 0, 0, 0, 0, 0 }, // 33 + // 1 ų + + // + { 0, 0, 0, 0, 0 }, // 34 + { 0, 0, 0, 0, 0 }, // 35 + { 0, 0, 0, 0, 0 }, // 36 + { 0, 0, 0, 0, 0 }, // 37 + { 0, 0, 0, 0, 0 }, // 38 + { 0, 0, 0, 0, 0 }, // 39 + { 0, 0, 0, 0, 0 }, // 40 + // + + // 2 ų + { 4, 16, 46, 76, 106 }, // 41 + { 4, 17, 47, 77, 107 }, // 42 + { 4, 18, 48, 78, 108 }, // 43 + { 4, 19, 49, 79, 109 }, // 44 + { 4, 20, 50, 80, 110 }, // 45 + { 4, 21, 51, 81, 111 }, // 46 + { 0, 0, 0, 0, 0 }, // 47 + { 0, 0, 0, 0, 0 }, // 48 + // 2 ų + + // + { 0, 0, 0, 0, 0 }, // 49 + { 0, 0, 0, 0, 0 }, // 50 + // + + // 1 ׷ ų + { 4, 1, 31, 61, 91 }, // 51 + { 4, 2, 32, 62, 92 }, // 52 + { 4, 3, 33, 63, 93 }, // 53 + { 4, 4, 34, 64, 94 }, // 54 + { 4, 5, 35, 65, 95 }, // 55 + { 4, 6, 36, 66, 96 }, // 56 + { 0, 0, 0, 0, 0 }, // 57 + { 0, 0, 0, 0, 0 }, // 58 + // 1 ׷ ų + + // + { 0, 0, 0, 0, 0 }, // 59 + { 0, 0, 0, 0, 0 }, // 60 + { 0, 0, 0, 0, 0 }, // 61 + { 0, 0, 0, 0, 0 }, // 62 + { 0, 0, 0, 0, 0 }, // 63 + { 0, 0, 0, 0, 0 }, // 64 + { 0, 0, 0, 0, 0 }, // 65 + // + + // 2 ׷ ų + { 4, 16, 46, 76, 106 }, // 66 + { 4, 17, 47, 77, 107 }, // 67 + { 4, 18, 48, 78, 108 }, // 68 + { 4, 19, 49, 79, 109 }, // 69 + { 4, 20, 50, 80, 110 }, // 70 + { 4, 21, 51, 81, 111 }, // 71 + { 0, 0, 0, 0, 0 }, // 72 + { 0, 0, 0, 0, 0 }, // 73 + // 2 ׷ ų + + // + { 0, 0, 0, 0, 0 }, // 74 + { 0, 0, 0, 0, 0 }, // 75 + // + + // 1 Ʈ ų + { 4, 1, 31, 61, 91 }, // 76 + { 4, 2, 32, 62, 92 }, // 77 + { 4, 3, 33, 63, 93 }, // 78 + { 4, 4, 34, 64, 94 }, // 79 + { 4, 5, 35, 65, 95 }, // 80 + { 4, 6, 36, 66, 96 }, // 81 + { 0, 0, 0, 0, 0 }, // 82 + { 0, 0, 0, 0, 0 }, // 83 + // 1 Ʈ ų + + // + { 0, 0, 0, 0, 0 }, // 84 + { 0, 0, 0, 0, 0 }, // 85 + { 0, 0, 0, 0, 0 }, // 86 + { 0, 0, 0, 0, 0 }, // 87 + { 0, 0, 0, 0, 0 }, // 88 + { 0, 0, 0, 0, 0 }, // 89 + { 0, 0, 0, 0, 0 }, // 90 + // + + // 2 Ʈ ų + { 4, 16, 46, 76, 106 }, // 91 + { 4, 17, 47, 77, 107 }, // 92 + { 4, 18, 48, 78, 108 }, // 93 + { 4, 19, 49, 79, 109 }, // 94 + { 4, 20, 50, 80, 110 }, // 95 + { 4, 21, 51, 81, 111 }, // 96 + { 0, 0, 0, 0, 0 }, // 97 + { 0, 0, 0, 0, 0 }, // 98 + // 2 Ʈ ų + + // + { 0, 0, 0, 0, 0 }, // 99 + { 0, 0, 0, 0, 0 }, // 100 + // + + // ų + { 1, 152, 0, 0, 0}, // 101 + { 1, 153, 0, 0, 0}, // 102 + { 1, 154, 0, 0, 0}, // 103 + { 1, 155, 0, 0, 0}, // 104 + { 1, 156, 0, 0, 0}, // 105 + { 1, 157, 0, 0, 0}, // 106 + // ų + + // + { 0, 0, 0, 0, 0}, // 107 + { 0, 0, 0, 0, 0}, // 108 + { 0, 0, 0, 0, 0}, // 109 + { 0, 0, 0, 0, 0}, // 110 + { 0, 0, 0, 0, 0}, // 111 + { 0, 0, 0, 0, 0}, // 112 + { 0, 0, 0, 0, 0}, // 113 + { 0, 0, 0, 0, 0}, // 114 + { 0, 0, 0, 0, 0}, // 115 + { 0, 0, 0, 0, 0}, // 116 + { 0, 0, 0, 0, 0}, // 117 + { 0, 0, 0, 0, 0}, // 118 + { 0, 0, 0, 0, 0}, // 119 + { 0, 0, 0, 0, 0}, // 120 + // + + // ¸ ų + { 2, 137, 140, 0, 0}, // 121 + { 1, 138, 0, 0, 0}, // 122 + { 1, 139, 0, 0, 0}, // 123 + // ¸ ų + }; + + if (dwMotionIndex >= MOTION_MAX_NUM) + { + sys_err("OUT_OF_MOTION_VNUM: name=%s, motion=%d/%d", GetName(), dwMotionIndex, MOTION_MAX_NUM); + return false; + } + + DWORD* skillVNums = s_anMotion2SkillVnumList[dwMotionIndex]; + + DWORD skillCount = *skillVNums++; + if (skillCount >= SKILL_LIST_MAX_COUNT) + { + sys_err("OUT_OF_SKILL_LIST: name=%s, count=%d/%d", GetName(), skillCount, SKILL_LIST_MAX_COUNT); + return false; + } + + for (DWORD skillIndex = 0; skillIndex != skillCount; ++skillIndex) + { + if (skillIndex >= SKILL_MAX_NUM) + { + sys_err("OUT_OF_SKILL_VNUM: name=%s, skill=%d/%d", GetName(), skillIndex, SKILL_MAX_NUM); + return false; + } + + DWORD eachSkillVNum = skillVNums[skillIndex]; + if ( eachSkillVNum != 0 ) + { + DWORD eachJobGroup = s_anSkill2JobGroup[eachSkillVNum]; + + if (0 == eachJobGroup || eachJobGroup == selfJobGroup) + { + // GUILDSKILL_BUG_FIX + DWORD eachSkillLevel = 0; + + if (eachSkillVNum >= GUILD_SKILL_START && eachSkillVNum <= GUILD_SKILL_END) + { + if (GetGuild()) + eachSkillLevel = GetGuild()->GetSkillLevel(eachSkillVNum); + else + eachSkillLevel = 0; + } + else + { + eachSkillLevel = GetSkillLevel(eachSkillVNum); + } + + if (eachSkillLevel > 0) + { + return true; + } + // END_OF_GUILDSKILL_BUG_FIX + } + } + } + + return false; +} + +void CHARACTER::ClearSkill() +{ + PointChange(POINT_SKILL, 4 + (GetLevel() - 5) - GetPoint(POINT_SKILL)); + + ResetSkill(); +} + +void CHARACTER::ClearSubSkill() +{ + PointChange(POINT_SUB_SKILL, GetLevel() < 10 ? 0 : (GetLevel() - 9) - GetPoint(POINT_SUB_SKILL)); + + if (m_pSkillLevels == NULL) + { + sys_err("m_pSkillLevels nil (name: %s)", GetName()); + return; + } + + TPlayerSkill CleanSkill; + memset(&CleanSkill, 0, sizeof(TPlayerSkill)); + + size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]); + + for (size_t i = 0; i < count; ++i) + { + if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM) + continue; + + m_pSkillLevels[s_adwSubSkillVnums[i]] = CleanSkill; + } + + ComputePoints(); + SkillLevelPacket(); +} + +bool CHARACTER::ResetOneSkill(DWORD dwVnum) +{ + if (NULL == m_pSkillLevels) + { + sys_err("m_pSkillLevels nil (name %s, vnum %u)", GetName(), dwVnum); + return false; + } + + if (dwVnum >= SKILL_MAX_NUM) + { + sys_err("vnum overflow (name %s, vnum %u)", GetName(), dwVnum); + return false; + } + + BYTE level = m_pSkillLevels[dwVnum].bLevel; + + m_pSkillLevels[dwVnum].bLevel = 0; + m_pSkillLevels[dwVnum].bMasterType = 0; + m_pSkillLevels[dwVnum].tNextRead = 0; + + if (level > 17) + level = 17; + + PointChange(POINT_SKILL, level); + + LogManager::instance().CharLog(this, dwVnum, "ONE_SKILL_RESET_BY_SCROLL", ""); + + ComputePoints(); + SkillLevelPacket(); + + return true; +} + +bool CHARACTER::CanUseSkill(DWORD dwSkillVnum) const +{ + if (0 == dwSkillVnum) return false; + + if (0 < GetSkillGroup()) + { + const int SKILL_COUNT = 6; + static const DWORD SkillList[JOB_MAX_NUM][SKILL_GROUP_MAX_NUM][SKILL_COUNT] = + { + { { 1, 2, 3, 4, 5, 6 }, { 16, 17, 18, 19, 20, 21 } }, + { { 31, 32, 33, 34, 35, 36 }, { 46, 47, 48, 49, 50, 51 } }, + { { 61, 62, 63, 64, 65, 66 }, { 76, 77, 78, 79, 80, 81 } }, + { { 91, 92, 93, 94, 95, 96 }, { 106,107,108,109,110,111 } }, + }; + + const DWORD* pSkill = SkillList[ GetJob() ][ GetSkillGroup()-1 ]; + + for (int i=0 ; i < SKILL_COUNT ; ++i) + { + if (pSkill[i] == dwSkillVnum) return true; + } + } + + //if (true == IsHorseRiding()) + + if (true == IsRiding()) + { + //Ʈ Ż ޸ ų 밡 + if(GetMountVnum()) + { + if( GetMountVnum() < 20209 && GetMountVnum() > 20212) + if (GetMountVnum() != 20215 || GetMountVnum() != 20218 || GetMountVnum() != 20220) + return false; + } + + switch(dwSkillVnum) + { + case SKILL_HORSE_WILDATTACK: + case SKILL_HORSE_CHARGE: + case SKILL_HORSE_ESCAPE: + case SKILL_HORSE_WILDATTACK_RANGE: + return true; + } + } + + switch( dwSkillVnum ) + { + case 121: case 122: case 124: case 126: case 127: case 128: case 129: case 130: + case 131: + case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: + return true; + } + + return false; +} + +bool CHARACTER::CheckSkillHitCount(const BYTE SkillID, const VID TargetVID) +{ + std::map::iterator iter = m_SkillUseInfo.find(SkillID); + + if (iter == m_SkillUseInfo.end()) + { + sys_log(0, "SkillHack: Skill(%u) is not in container", SkillID); + return false; + } + + TSkillUseInfo& rSkillUseInfo = iter->second; + + if (false == rSkillUseInfo.bUsed) + { + sys_log(0, "SkillHack: not used skill(%u)", SkillID); + return false; + } + + switch (SkillID) + { + case SKILL_YONGKWON: + case SKILL_HWAYEOMPOK: + case SKILL_DAEJINGAK: + case SKILL_PAERYONG: + sys_log(0, "SkillHack: cannot use attack packet for skill(%u)", SkillID); + return false; + } + + boost::unordered_map::iterator iterTargetMap = rSkillUseInfo.TargetVIDMap.find(TargetVID); + + if (rSkillUseInfo.TargetVIDMap.end() != iterTargetMap) + { + size_t MaxAttackCountPerTarget = 1; + + switch (SkillID) + { + case SKILL_SAMYEON: + case SKILL_CHARYUN: + MaxAttackCountPerTarget = 3; + break; + + case SKILL_HORSE_WILDATTACK_RANGE: + MaxAttackCountPerTarget = 5; + break; + + case SKILL_YEONSA: + MaxAttackCountPerTarget = 7; + break; + + case SKILL_HORSE_ESCAPE: + MaxAttackCountPerTarget = 10; + break; + } + + if (iterTargetMap->second >= MaxAttackCountPerTarget) + { + sys_log(0, "SkillHack: Too Many Hit count from SkillID(%u) count(%u)", SkillID, iterTargetMap->second); + return false; + } + + iterTargetMap->second++; + } + else + { + rSkillUseInfo.TargetVIDMap.insert( std::make_pair(TargetVID, 1) ); + } + + return true; +} + diff --git a/game/src/char_state.cpp b/game/src/char_state.cpp new file mode 100644 index 0000000..bdfdb94 --- /dev/null +++ b/game/src/char_state.cpp @@ -0,0 +1,1230 @@ +#include "stdafx.h" +#include "config.h" +#include "utils.h" +#include "vector.h" +#include "char.h" +#include "battle.h" +#include "char_manager.h" +#include "packet.h" +#include "motion.h" +#include "party.h" +#include "affect.h" +#include "buffer_manager.h" +#include "questmanager.h" +#include "p2p.h" +#include "item_manager.h" +#include "mob_manager.h" +#include "exchange.h" +#include "sectree_manager.h" +#include "xmas_event.h" +#include "guild_manager.h" +#include "war_map.h" +#include "locale_service.h" +#include "BlueDragon.h" + +#include "../../common/VnumHelper.h" + +BOOL g_test_server; +extern LPCHARACTER FindVictim(LPCHARACTER pkChr, int iMaxDistance); + +namespace +{ + class FuncFindChrForFlag + { + public: + FuncFindChrForFlag(LPCHARACTER pkChr) : + m_pkChr(pkChr), m_pkChrFind(NULL), m_iMinDistance(INT_MAX) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + if (ent->IsObserverMode()) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!pkChr->IsPC()) + return; + + if (!pkChr->GetGuild()) + return; + + if (pkChr->IsDead()) + return; + + int iDist = DISTANCE_APPROX(pkChr->GetX()-m_pkChr->GetX(), pkChr->GetY()-m_pkChr->GetY()); + + if (iDist <= 500 && m_iMinDistance > iDist && + !pkChr->IsAffectFlag(AFF_WAR_FLAG1) && + !pkChr->IsAffectFlag(AFF_WAR_FLAG2) && + !pkChr->IsAffectFlag(AFF_WAR_FLAG3)) + { + // 츮 + if ((DWORD) m_pkChr->GetPoint(POINT_STAT) == pkChr->GetGuild()->GetID()) + { + CWarMap * pMap = pkChr->GetWarMap(); + BYTE idx; + + if (!pMap || !pMap->GetTeamIndex(pkChr->GetGuild()->GetID(), idx)) + return; + + // 츮 ̴´. ȱ׷ ִ + // ΰ Ƿ.. + if (!pMap->IsFlagOnBase(idx)) + { + m_pkChrFind = pkChr; + m_iMinDistance = iDist; + } + } + else + { + // ̴´. + m_pkChrFind = pkChr; + m_iMinDistance = iDist; + } + } + } + + LPCHARACTER m_pkChr; + LPCHARACTER m_pkChrFind; + int m_iMinDistance; + }; + + class FuncFindChrForFlagBase + { + public: + FuncFindChrForFlagBase(LPCHARACTER pkChr) : m_pkChr(pkChr) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + if (ent->IsObserverMode()) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + if (!pkChr->IsPC()) + return; + + CGuild * pkGuild = pkChr->GetGuild(); + + if (!pkGuild) + return; + + int iDist = DISTANCE_APPROX(pkChr->GetX()-m_pkChr->GetX(), pkChr->GetY()-m_pkChr->GetY()); + + if (iDist <= 500 && + (pkChr->IsAffectFlag(AFF_WAR_FLAG1) || + pkChr->IsAffectFlag(AFF_WAR_FLAG2) || + pkChr->IsAffectFlag(AFF_WAR_FLAG3))) + { + CAffect * pkAff = pkChr->FindAffect(AFFECT_WAR_FLAG); + + sys_log(0, "FlagBase %s dist %d aff %p flag gid %d chr gid %u", + pkChr->GetName(), iDist, pkAff, m_pkChr->GetPoint(POINT_STAT), + pkChr->GetGuild()->GetID()); + + if (pkAff) + { + if ((DWORD) m_pkChr->GetPoint(POINT_STAT) == pkGuild->GetID() && + m_pkChr->GetPoint(POINT_STAT) != pkAff->lApplyValue) + { + CWarMap * pMap = pkChr->GetWarMap(); + BYTE idx; + + if (!pMap || !pMap->GetTeamIndex(pkGuild->GetID(), idx)) + return; + + //if (pMap->IsFlagOnBase(idx)) + { + BYTE idx_opp = idx == 0 ? 1 : 0; + + SendGuildWarScore(m_pkChr->GetPoint(POINT_STAT), pkAff->lApplyValue, 1); + //SendGuildWarScore(pkAff->lApplyValue, m_pkChr->GetPoint(POINT_STAT), -1); + + pMap->ResetFlag(); + //pMap->AddFlag(idx_opp); + //pkChr->RemoveAffect(AFFECT_WAR_FLAG); + + char buf[256]; + snprintf(buf, sizeof(buf), LC_TEXT("%s 尡 %s Ѿҽϴ!"), pMap->GetGuild(idx)->GetName(), pMap->GetGuild(idx_opp)->GetName()); + pMap->Notice(buf); + } + } + } + } + } + + LPCHARACTER m_pkChr; + }; + + class FuncFindGuardVictim + { + public: + FuncFindGuardVictim(LPCHARACTER pkChr, int iMaxDistance) : + m_pkChr(pkChr), + m_iMinDistance(INT_MAX), + m_iMaxDistance(iMaxDistance), + m_lx(pkChr->GetX()), + m_ly(pkChr->GetY()), + m_pkChrVictim(NULL) + { + }; + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + // ϴ PC ݾ + if (pkChr->IsPC()) + return; + + + if (pkChr->IsNPC() && !pkChr->IsMonster()) + return; + + if (pkChr->IsDead()) + return; + + if (pkChr->IsAffectFlag(AFF_EUNHYUNG) || + pkChr->IsAffectFlag(AFF_INVISIBILITY) || + pkChr->IsAffectFlag(AFF_REVIVE_INVISIBLE)) + return; + + // ֱ н + if (pkChr->GetRaceNum() == 5001) + return; + + int iDistance = DISTANCE_APPROX(m_lx - pkChr->GetX(), m_ly - pkChr->GetY()); + + if (iDistance < m_iMinDistance && iDistance <= m_iMaxDistance) + { + m_pkChrVictim = pkChr; + m_iMinDistance = iDistance; + } + } + + LPCHARACTER GetVictim() + { + return (m_pkChrVictim); + } + + private: + LPCHARACTER m_pkChr; + + int m_iMinDistance; + int m_iMaxDistance; + long m_lx; + long m_ly; + + LPCHARACTER m_pkChrVictim; + }; + +} + +bool CHARACTER::IsAggressive() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_AGGRESSIVE); +} + +void CHARACTER::SetAggressive() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_AGGRESSIVE); +} + +bool CHARACTER::IsCoward() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_COWARD); +} + +void CHARACTER::SetCoward() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_COWARD); +} + +bool CHARACTER::IsBerserker() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_BERSERK); +} + +bool CHARACTER::IsStoneSkinner() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_STONESKIN); +} + +bool CHARACTER::IsGodSpeeder() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_GODSPEED); +} + +bool CHARACTER::IsDeathBlower() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_DEATHBLOW); +} + +bool CHARACTER::IsReviver() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_REVIVE); +} + +void CHARACTER::CowardEscape() +{ + int iDist[4] = {500, 1000, 3000, 5000}; + + for (int iDistIdx = 2; iDistIdx >= 0; --iDistIdx) + for (int iTryCount = 0; iTryCount < 8; ++iTryCount) + { + SetRotation(number(0, 359)); // + + float fx, fy; + float fDist = number(iDist[iDistIdx], iDist[iDistIdx+1]); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + bool bIsWayBlocked = false; + for (int j = 1; j <= 100; ++j) + { + if (!SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx*j/100, GetY() + (int) fy*j/100)) + { + bIsWayBlocked = true; + break; + } + } + + if (bIsWayBlocked) + continue; + + m_dwStateDuration = PASSES_PER_SEC(1); + + int iDestX = GetX() + (int) fx; + int iDestY = GetY() + (int) fy; + + if (Goto(iDestX, iDestY)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + sys_log(0, "WAEGU move to %d %d (far)", iDestX, iDestY); + return; + } +} + +void CHARACTER::SetNoAttackShinsu() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKSHINSU); +} +bool CHARACTER::IsNoAttackShinsu() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKSHINSU); +} + +void CHARACTER::SetNoAttackChunjo() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKCHUNJO); +} + +bool CHARACTER::IsNoAttackChunjo() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKCHUNJO); +} + +void CHARACTER::SetNoAttackJinno() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKJINNO); +} + +bool CHARACTER::IsNoAttackJinno() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOATTACKJINNO); +} + +void CHARACTER::SetAttackMob() +{ + SET_BIT(m_pointsInstant.dwAIFlag, AIFLAG_ATTACKMOB); +} + +bool CHARACTER::IsAttackMob() const +{ + return IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_ATTACKMOB); +} + +// STATE_IDLE_REFACTORING +void CHARACTER::StateIdle() +{ + if (IsStone()) + { + __StateIdle_Stone(); + return; + } + else if (IsWarp() || IsGoto()) + { + // ̺Ʈ ó + m_dwStateDuration = 60 * passes_per_sec; + return; + } + + if (IsPC()) + return; + + // NPC ó + if (!IsMonster()) + { + __StateIdle_NPC(); + return; + } + + __StateIdle_Monster(); +} + +void CHARACTER::__StateIdle_Stone() +{ + m_dwStateDuration = PASSES_PER_SEC(1); + + int iPercent = (GetHP() * 100) / GetMaxHP(); + DWORD dwVnum = number(MIN(GetMobTable().sAttackSpeed, GetMobTable().sMovingSpeed ), MAX(GetMobTable().sAttackSpeed, GetMobTable().sMovingSpeed)); + + if (iPercent <= 10 && GetMaxSP() < 10) + { + SetMaxSP(10); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1500, GetY() - 1500, GetX() + 1500, GetY() + 1500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 20 && GetMaxSP() < 9) + { + SetMaxSP(9); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1500, GetY() - 1500, GetX() + 1500, GetY() + 1500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 30 && GetMaxSP() < 8) + { + SetMaxSP(8); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 40 && GetMaxSP() < 7) + { + SetMaxSP(7); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 50 && GetMaxSP() < 6) + { + SetMaxSP(6); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 60 && GetMaxSP() < 5) + { + SetMaxSP(5); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 70 && GetMaxSP() < 4) + { + SetMaxSP(4); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 80 && GetMaxSP() < 3) + { + SetMaxSP(3); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 90 && GetMaxSP() < 2) + { + SetMaxSP(2); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 500, GetY() - 500, GetX() + 500, GetY() + 500); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else if (iPercent <= 99 && GetMaxSP() < 1) + { + SetMaxSP(1); + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0); + + CHARACTER_MANAGER::instance().SelectStone(this); + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, GetMapIndex(), GetX() - 1000, GetY() - 1000, GetX() + 1000, GetY() + 1000); + CHARACTER_MANAGER::instance().SelectStone(NULL); + } + else + return; + + UpdatePacket(); + return; +} + +void CHARACTER::__StateIdle_NPC() +{ + MonsterChat(MONSTER_CHAT_WAIT); + m_dwStateDuration = PASSES_PER_SEC(5); + + // ý Idle ó ij͵ ؼ ϴ ¸ӽ ƴ CPetActor::Update ó. + if (IsPet()) + return; + else if (IsGuardNPC()) + { + if (!quest::CQuestManager::instance().GetEventFlag("noguard")) + { + FuncFindGuardVictim f(this, 50000); + + if (GetSectree()) + GetSectree()->ForEachAround(f); + + LPCHARACTER victim = f.GetVictim(); + + if (victim) + { + m_dwStateDuration = passes_per_sec/2; + + if (CanBeginFight()) + BeginFight(victim); + } + } + } + else + { + if (GetRaceNum() == xmas::MOB_SANTA_VNUM) // Ÿ + { + if (get_dword_time() > m_dwPlayStartTime) + { + int next_warp_time = 2 * 1000; // 2 + + m_dwPlayStartTime = get_dword_time() + next_warp_time; + + // ð Ѿ սô. + /* + * Ÿ + const int WARP_MAP_INDEX_NUM = 4; + static const long c_lWarpMapIndexs[WARP_MAP_INDEX_NUM] = {61, 62, 63, 64}; + */ + // ż ؿ + const int WARP_MAP_INDEX_NUM = 7; + static const long c_lWarpMapIndexs[WARP_MAP_INDEX_NUM] = { 61, 62, 63, 64, 3, 23, 43 }; + long lNextMapIndex; + lNextMapIndex = c_lWarpMapIndexs[number(1, WARP_MAP_INDEX_NUM) - 1]; + + if (map_allow_find(lNextMapIndex)) + { + // ̰Դϴ. + M2_DESTROY_CHARACTER(this); + int iNextSpawnDelay = 0; + if (LC_IsYMIR()) + iNextSpawnDelay = 20 * 60; + else + iNextSpawnDelay = 50 * 60; + + xmas::SpawnSanta(lNextMapIndex, iNextSpawnDelay); + } + else + { + // ٸ Դϴ. + TPacketGGXmasWarpSanta p; + p.bHeader = HEADER_GG_XMAS_WARP_SANTA; + p.bChannel = g_bChannel; + p.lMapIndex = lNextMapIndex; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGXmasWarpSanta)); + } + return; + } + } + + if (!IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + { + // + // ̵Ѵ. + // + LPCHARACTER pkChrProtege = GetProtege(); + + if (pkChrProtege) + { + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 500) + { + if (Follow(pkChrProtege, number(100, 300))) + return; + } + } + + if (!number(0, 6)) + { + SetRotation(number(0, 359)); // + + float fx, fy; + float fDist = number(200, 400); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + // Ӽ üũ; ġ ߰ ġ ٸ ʴ´. + if (!(SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx / 2, GetY() + (int) fy / 2))) + return; + + SetNowWalking(true); + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + return; + } + } + } +} + +void CHARACTER::__StateIdle_Monster() +{ + if (IsStun()) + return; + + if (!CanMove()) + return; + + if (IsCoward()) + { + // ʹ ٴմϴ. + if (!IsDead()) + CowardEscape(); + + return; + } + + if (IsBerserker()) + if (IsBerserk()) + SetBerserk(false); + + if (IsGodSpeeder()) + if (IsGodSpeed()) + SetGodSpeed(false); + + LPCHARACTER victim = GetVictim(); + + if (!victim || victim->IsDead()) + { + SetVictim(NULL); + victim = NULL; + m_dwStateDuration = PASSES_PER_SEC(1); + } + + if (!victim || victim->IsBuilding()) + { + // ȣ ó + if (m_pkChrStone) + { + victim = m_pkChrStone->GetNearestVictim(m_pkChrStone); + } + // ó + else if (!no_wander && IsAggressive()) + { + if (GetMapIndex() == 61 && quest::CQuestManager::instance().GetEventFlag("xmas_tree")); + // ѻ꿡 ʴ´. + else + victim = FindVictim(this, m_pkMobData->m_table.wAggressiveSight); + } + } + + if (victim && !victim->IsDead()) + { + if (CanBeginFight()) + BeginFight(victim); + + return; + } + + if (IsAggressive() && !victim) + m_dwStateDuration = PASSES_PER_SEC(number(1, 3)); + else + m_dwStateDuration = PASSES_PER_SEC(number(3, 5)); + + LPCHARACTER pkChrProtege = GetProtege(); + + // ȣ (, Ƽ)Է ִٸ 󰣴. + if (pkChrProtege) + { + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000) + { + if (Follow(pkChrProtege, number(150, 400))) + { + MonsterLog("[IDLE] κ ʹ ָ ! Ѵ."); + return; + } + } + } + + // + // ׳ Դٸ ٸ Ѵ. + // + if (!no_wander && !IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE)) + { + if (!number(0, 6)) + { + SetRotation(number(0, 359)); // + + float fx, fy; + float fDist = number(300, 700); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + // Ӽ üũ; ġ ߰ ġ ٸ ʴ´. + if (!(SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx/2, GetY() + (int) fy/2))) + return; + + // NOTE: Ͱ IDLE ¿ ֺ Ÿ , پ Ǿ . ( ) + // ׷ Ͱ ȴ ʹٰ ؼ ӽ÷ ƯȮ Ȱų ٰ . ( Ʋ ϴ ׽Ʈ 忡 ۵) + if (g_test_server) + { + if (number(0, 100) < 60) + SetNowWalking(false); + else + SetNowWalking(true); + } + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + + return; + } + } + + MonsterChat(MONSTER_CHAT_WAIT); +} +// END_OF_STATE_IDLE_REFACTORING + +bool __CHARACTER_GotoNearTarget(LPCHARACTER self, LPCHARACTER victim) +{ + if (IS_SET(self->GetAIFlag(), AIFLAG_NOMOVE)) + return false; + + switch (self->GetMobBattleType()) + { + case BATTLE_TYPE_RANGE: + case BATTLE_TYPE_MAGIC: + // 糪 ü Ÿ 80% Ѵ. + if (self->Follow(victim, self->GetMobAttackRange() * 8 / 10)) + return true; + break; + + default: + // 90%? + if (self->Follow(victim, self->GetMobAttackRange() * 9 / 10)) + return true; + } + + return false; +} + +void CHARACTER::StateMove() +{ + DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime; + float fRate = (float) dwElapsedTime / (float) m_dwMoveDuration; + + if (fRate > 1.0f) + fRate = 1.0f; + + int x = (int) ((float) (m_posDest.x - m_posStart.x) * fRate + m_posStart.x); + int y = (int) ((float) (m_posDest.y - m_posStart.y) * fRate + m_posStart.y); + + Move(x, y); + + if (IsPC() && (thecore_pulse() & 15) == 0) + { + UpdateSectree(); + + if (GetExchange()) + { + LPCHARACTER victim = GetExchange()->GetCompany()->GetOwner(); + int iDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + // Ÿ üũ + if (iDist >= EXCHANGE_MAX_DISTANCE) + { + GetExchange()->Cancel(); + } + } + } + + // ׹̳ 0 ̻̾ Ѵ. + if (IsPC()) + { + if (IsWalking() && GetStamina() < GetMaxStamina()) + { + // 5 ׹̳ + if (get_dword_time() - GetWalkStartTime() > 5000) + PointChange(POINT_STAMINA, GetMaxStamina() / 1); + } + + // ̸鼭 ٴ ̸ + if (!IsWalking() && !IsRiding()) + if ((get_dword_time() - GetLastAttackTime()) < 20000) + { + StartAffectEvent(); + + if (IsStaminaHalfConsume()) + { + if (thecore_pulse()&1) + PointChange(POINT_STAMINA, -STAMINA_PER_STEP); + } + else + PointChange(POINT_STAMINA, -STAMINA_PER_STEP); + + StartStaminaConsume(); + + if (GetStamina() <= 0) + { + // ׹̳ ڶ ɾ + SetStamina(0); + SetNowWalking(true); + StopStaminaConsume(); + } + } + else if (IsStaminaConsume()) + { + StopStaminaConsume(); + } + } + else + { + // XXX AGGRO + if (IsMonster() && GetVictim()) + { + LPCHARACTER victim = GetVictim(); + UpdateAggrPoint(victim, DAMAGE_TYPE_NORMAL, -(victim->GetLevel() / 3 + 1)); + + if (g_test_server) + { + // Ͱ Ѿư ̸ پ. + SetNowWalking(false); + } + } + + if (IsMonster() && GetMobRank() >= MOB_RANK_BOSS && GetVictim()) + { + LPCHARACTER victim = GetVictim(); + + // Ŵ ź + if (GetRaceNum() == 2191 && number(1, 20) == 1 && get_dword_time() - m_pkMobInst->m_dwLastWarpTime > 1000) + { + // ׽Ʈ + float fx, fy; + GetDeltaByDegree(victim->GetRotation(), 400, &fx, &fy); + long new_x = victim->GetX() + (long)fx; + long new_y = victim->GetY() + (long)fy; + SetRotation(GetDegreeFromPositionXY(new_x, new_y, victim->GetX(), victim->GetY())); + Show(victim->GetMapIndex(), new_x, new_y, 0, true); + GotoState(m_stateBattle); + m_dwStateDuration = 1; + ResetMobSkillCooltime(); + m_pkMobInst->m_dwLastWarpTime = get_dword_time(); + return; + } + + // TODO ȯ ؼ ٺ ! + if (number(0, 3) == 0) + { + if (__CHARACTER_GotoNearTarget(this, victim)) + return; + } + } + } + + if (1.0f == fRate) + { + if (IsPC()) + { + sys_log(1, " %s %d %d", GetName(), x, y); + GotoState(m_stateIdle); + StopStaminaConsume(); + } + else + { + if (GetVictim() && !IsCoward()) + { + if (!IsState(m_stateBattle)) + MonsterLog("[BATTLE] ó ݽ %s", GetVictim()->GetName()); + + GotoState(m_stateBattle); + m_dwStateDuration = 1; + } + else + { + if (!IsState(m_stateIdle)) + MonsterLog("[IDLE] "); + + GotoState(m_stateIdle); + + LPCHARACTER rider = GetRider(); + + m_dwStateDuration = PASSES_PER_SEC(number(1, 3)); + } + } + } +} + +void CHARACTER::StateBattle() +{ + if (IsStone()) + { + sys_err("Stone must not use battle state (name %s)", GetName()); + return; + } + + if (IsPC()) + return; + + if (!CanMove()) + return; + + if (IsStun()) + return; + + LPCHARACTER victim = GetVictim(); + + if (IsCoward()) + { + if (IsDead()) + return; + + SetVictim(NULL); + + if (number(1, 50) != 1) + { + GotoState(m_stateIdle); + m_dwStateDuration = 1; + } + else + CowardEscape(); + + return; + } + + if (!victim || (victim->IsStun() && IsGuardNPC()) || victim->IsDead()) + { + if (victim && victim->IsDead() && + !no_wander && IsAggressive() && (!GetParty() || GetParty()->GetLeader() == this)) + { + LPCHARACTER new_victim = FindVictim(this, m_pkMobData->m_table.wAggressiveSight); + + SetVictim(new_victim); + m_dwStateDuration = PASSES_PER_SEC(1); + + if (!new_victim) + { + switch (GetMobBattleType()) + { + case BATTLE_TYPE_MELEE: + case BATTLE_TYPE_SUPER_POWER: + case BATTLE_TYPE_SUPER_TANKER: + case BATTLE_TYPE_POWER: + case BATTLE_TYPE_TANKER: + { + float fx, fy; + float fDist = number(400, 1500); + + GetDeltaByDegree(number(0, 359), fDist, &fx, &fy); + + if (SECTREE_MANAGER::instance().IsMovablePosition(victim->GetMapIndex(), + victim->GetX() + (int) fx, + victim->GetY() + (int) fy) && + SECTREE_MANAGER::instance().IsMovablePosition(victim->GetMapIndex(), + victim->GetX() + (int) fx/2, + victim->GetY() + (int) fy/2)) + { + float dx = victim->GetX() + fx; + float dy = victim->GetY() + fy; + + SetRotation(GetDegreeFromPosition(dx, dy)); + + if (Goto((long) dx, (long) dy)) + { + sys_log(0, "KILL_AND_GO: %s distance %.1f", GetName(), fDist); + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } + } + } + } + } + return; + } + + SetVictim(NULL); + + if (IsGuardNPC()) + Return(); + + m_dwStateDuration = PASSES_PER_SEC(1); + return; + } + + if (IsSummonMonster() && !IsDead() && !IsStun()) + { + if (!GetParty()) + { + // ؼ ä Ƽ Ӵϴ. + CPartyManager::instance().CreateParty(this); + } + + LPPARTY pParty = GetParty(); + bool bPct = !number(0, 3); + + if (bPct && pParty->CountMemberByVnum(GetSummonVnum()) < SUMMON_MONSTER_COUNT) + { + MonsterLog(" ȯ!"); + // ڶ ༮ ҷ äô. + int sx = GetX() - 300; + int sy = GetY() - 300; + int ex = GetX() + 300; + int ey = GetY() + 300; + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(GetSummonVnum(), GetMapIndex(), sx, sy, ex, ey, true, true); + + if (tch) + { + pParty->Join(tch->GetVID()); + pParty->Link(tch); + } + } + } + + LPCHARACTER pkChrProtege = GetProtege(); + + float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + if (fDist >= 4000.0f) // 40 ̻ ־ + { + MonsterLog("Ÿ ־ "); + SetVictim(NULL); + + // ȣ (, Ƽ) ֺ . + if (pkChrProtege) + if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000) + Follow(pkChrProtege, number(150, 400)); + + return; + } + + if (fDist >= GetMobAttackRange() * 1.15) + { + __CHARACTER_GotoNearTarget(this, victim); + return; + } + + if (m_pkParty) + m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0); + + if (2493 == m_pkMobData->m_table.dwVnum) + { + // (2493) Ư ó + m_dwStateDuration = BlueDragon_StateBattle(this); + return; + } + + DWORD dwCurTime = get_dword_time(); + DWORD dwDuration = CalculateDuration(GetLimitPoint(POINT_ATT_SPEED), 2000); + + if ((dwCurTime - m_dwLastAttackTime) < dwDuration) // 2 ؾ Ѵ. + { + m_dwStateDuration = MAX(1, (passes_per_sec * (dwDuration - (dwCurTime - m_dwLastAttackTime)) / 1000)); + return; + } + + if (IsBerserker() == true) + if (GetHPPct() < m_pkMobData->m_table.bBerserkPoint) + if (IsBerserk() != true) + SetBerserk(true); + + if (IsGodSpeeder() == true) + if (GetHPPct() < m_pkMobData->m_table.bGodSpeedPoint) + if (IsGodSpeed() != true) + SetGodSpeed(true); + + // + // ų ó + // + if (HasMobSkill()) + { + for (unsigned int iSkillIdx = 0; iSkillIdx < MOB_SKILL_MAX_NUM; ++iSkillIdx) + { + if (CanUseMobSkill(iSkillIdx)) + { + SetRotationToXY(victim->GetX(), victim->GetY()); + + if (UseMobSkill(iSkillIdx)) + { + SendMovePacket(FUNC_MOB_SKILL, iSkillIdx, GetX(), GetY(), 0, dwCurTime); + + float fDuration = CMotionManager::instance().GetMotionDuration(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + iSkillIdx)); + m_dwStateDuration = (DWORD) (fDuration == 0.0f ? PASSES_PER_SEC(2) : PASSES_PER_SEC(fDuration)); + + if (test_server) + sys_log(0, "USE_MOB_SKILL: %s idx %u motion %u duration %.0f", GetName(), iSkillIdx, MOTION_SPECIAL_1 + iSkillIdx, fDuration); + + return; + } + } + } + } + + if (!Attack(victim)) // ж? ? TODO + m_dwStateDuration = passes_per_sec / 2; + else + { + // ٶ󺸰 . + SetRotationToXY(victim->GetX(), victim->GetY()); + + SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0, dwCurTime); + + float fDuration = CMotionManager::instance().GetMotionDuration(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_NORMAL_ATTACK)); + m_dwStateDuration = (DWORD) (fDuration == 0.0f ? PASSES_PER_SEC(2) : PASSES_PER_SEC(fDuration)); + } +} + +void CHARACTER::StateFlag() +{ + m_dwStateDuration = (DWORD) PASSES_PER_SEC(0.5); + + CWarMap * pMap = GetWarMap(); + + if (!pMap) + return; + + FuncFindChrForFlag f(this); + GetSectree()->ForEachAround(f); + + if (!f.m_pkChrFind) + return; + + if (NULL == f.m_pkChrFind->GetGuild()) + return; + + char buf[256]; + BYTE idx; + + if (!pMap->GetTeamIndex(GetPoint(POINT_STAT), idx)) + return; + + f.m_pkChrFind->AddAffect(AFFECT_WAR_FLAG, POINT_NONE, GetPoint(POINT_STAT), idx == 0 ? AFF_WAR_FLAG1 : AFF_WAR_FLAG2, INFINITE_AFFECT_DURATION, 0, false); + f.m_pkChrFind->AddAffect(AFFECT_WAR_FLAG, POINT_MOV_SPEED, 50 - f.m_pkChrFind->GetPoint(POINT_MOV_SPEED), 0, INFINITE_AFFECT_DURATION, 0, false); + + pMap->RemoveFlag(idx); + + snprintf(buf, sizeof(buf), LC_TEXT("%s %s ȹϿϴ."), pMap->GetGuild(idx)->GetName(), f.m_pkChrFind->GetName()); + pMap->Notice(buf); +} + +void CHARACTER::StateFlagBase() +{ + m_dwStateDuration = (DWORD) PASSES_PER_SEC(0.5); + + FuncFindChrForFlagBase f(this); + GetSectree()->ForEachAround(f); +} + +void CHARACTER::StateHorse() +{ + float START_FOLLOW_DISTANCE = 400.0f; // Ÿ ̻ Ѿư + float START_RUN_DISTANCE = 700.0f; // Ÿ ̻ پ Ѿư. + int MIN_APPROACH = 150; // ּ Ÿ + int MAX_APPROACH = 300; // ִ Ÿ + + DWORD STATE_DURATION = (DWORD)PASSES_PER_SEC(0.5); // ð + + bool bDoMoveAlone = true; // ijͿ ȥ ϰ -_-; + bool bRun = true; // پ ϳ? + + if (IsDead()) + return; + + m_dwStateDuration = STATE_DURATION; + + LPCHARACTER victim = GetRider(); + + // ! ƴ // ȯڰ Ŭ + if (!victim) + { + M2_DESTROY_CHARACTER(this); + return; + } + + m_pkMobInst->m_posLastAttacked = GetXYZ(); + + float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY()); + + if (fDist >= START_FOLLOW_DISTANCE) + { + if (fDist > START_RUN_DISTANCE) + SetNowWalking(!bRun); // NOTE: Լ ̸ ߴ° ˾Ҵµ SetNowWalking(false) ϸ ٴ°.. -_-; + + Follow(victim, number(MIN_APPROACH, MAX_APPROACH)); + + m_dwStateDuration = STATE_DURATION; + } + else if (bDoMoveAlone && (get_dword_time() > m_dwLastAttackTime)) + { + // wondering-.- + m_dwLastAttackTime = get_dword_time() + number(5000, 12000); + + SetRotation(number(0, 359)); // + + float fx, fy; + float fDist = number(200, 400); + + GetDeltaByDegree(GetRotation(), fDist, &fx, &fy); + + // Ӽ üũ; ġ ߰ ġ ٸ ʴ´. + if (!(SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx, GetY() + (int) fy) + && SECTREE_MANAGER::instance().IsMovablePosition(GetMapIndex(), GetX() + (int) fx/2, GetY() + (int) fy/2))) + return; + + SetNowWalking(true); + + if (Goto(GetX() + (int) fx, GetY() + (int) fy)) + SendMovePacket(FUNC_WAIT, 0, 0, 0, 0); + } +} + diff --git a/game/src/check_server.cpp b/game/src/check_server.cpp new file mode 100644 index 0000000..0b4698f --- /dev/null +++ b/game/src/check_server.cpp @@ -0,0 +1,4 @@ +#include "check_server.h" + +std::vector CheckServer::keys_; +bool CheckServer::fail_ = true; diff --git a/game/src/check_server.h b/game/src/check_server.h new file mode 100644 index 0000000..4cd7b51 --- /dev/null +++ b/game/src/check_server.h @@ -0,0 +1,48 @@ +#ifndef _M2_CHECK_SERVER_KEY_H_ +#define _M2_CHECK_SERVER_KEY_H_ + +#include +#include +#include "CheckServerKey.h" + +class CheckServer +{ +public: + static FORCEINLINE void AddServerKey(const char* serverKey) + { + keys_.push_back(serverKey); + } + + static FORCEINLINE bool CheckIp(const char* ip) + { + // Ȱɸ üũ + #ifndef _USE_SERVER_KEY_ + fail_ = false; + return true; + #endif + + for (int i = 0; i < keys_.size(); i++) + { + // ϳ ´ Ű ok + std::string errorString; + if (CheckServerKey(keys_[i].c_str(), ip, "", errorString)) + { + fail_ = false; + break; + } + } + + return !IsFail(); + } + + static FORCEINLINE bool IsFail() + { + return fail_; + } + +private: + static std::vector keys_; + static bool fail_; +}; + +#endif // #ifndef _M2_CHECK_SERVER_KEY_H_ diff --git a/game/src/cipher.cpp b/game/src/cipher.cpp new file mode 100644 index 0000000..a57ddd4 --- /dev/null +++ b/game/src/cipher.cpp @@ -0,0 +1,402 @@ +#include "stdafx.h" + +#include "cipher.h" + +#ifdef _IMPROVED_PACKET_ENCRYPTION_ + +#include +#include +#include + +// Diffie-Hellman key agreement +#include +#include + +// AES winner and candidates +//#include +#include +#include +#include +#include +#include +// Other block ciphers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace CryptoPP; + +// Block cipher algorithm selector abstract base class. +struct BlockCipherAlgorithm { + enum { + kDefault, // to give more chances to default algorithm + // AES winner and candidates +// kAES, // Rijndael + kRC6, + kMARS, + kTwofish, + kSerpent, + kCAST256, + // Other block ciphers + kIDEA, + k3DES, // DES-EDE2 + kCamellia, + kSEED, + kRC5, + kBlowfish, + kTEA, +// kSKIPJACK, + kSHACAL2, + // End sentinel + kMaxAlgorithms + }; + + BlockCipherAlgorithm() {} + virtual ~BlockCipherAlgorithm() {} + + static BlockCipherAlgorithm* Pick(int hint); + + virtual int GetBlockSize() const = 0; + virtual int GetDefaultKeyLength() const = 0; + virtual int GetIVLength() const = 0; + + virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen, + const byte* iv) const = 0; + virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen, + const byte* iv) const = 0; +}; + +// Block cipher (with CTR mode) algorithm selector template class. +template +struct BlockCipherDetail : public BlockCipherAlgorithm { + BlockCipherDetail() {} + virtual ~BlockCipherDetail() {} + + virtual int GetBlockSize() const { return T::BLOCKSIZE; } + virtual int GetDefaultKeyLength() const { return T::DEFAULT_KEYLENGTH; } + virtual int GetIVLength() const { return T::IV_LENGTH; } + + virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen, + const byte* iv) const { + return new typename CTR_Mode::Encryption(key, keylen, iv); + } + virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen, + const byte* iv) const { + return new typename CTR_Mode::Decryption(key, keylen, iv); + } +}; + +// Key agreement scheme abstract class. +class KeyAgreement { + public: + KeyAgreement() {} + virtual ~KeyAgreement() {} + + virtual size_t Prepare(void* buffer, size_t* length) = 0; + virtual bool Agree(size_t agreed_length, const void* buffer, size_t length) = 0; + + const SecByteBlock& shared() const { return shared_; } + + protected: + SecByteBlock shared_; +}; + +// Crypto++ Unified Diffie-Hellman key agreement scheme implementation. +class DH2KeyAgreement : public KeyAgreement { + public: + DH2KeyAgreement(); + virtual ~DH2KeyAgreement(); + + virtual size_t Prepare(void* buffer, size_t* length); + virtual bool Agree(size_t agreed_length, const void* buffer, size_t length); + + private: + DH dh_; + DH2 dh2_; + SecByteBlock spriv_key_; + SecByteBlock epriv_key_; +}; + +Cipher::Cipher() + : activated_(false), encoder_(NULL), decoder_(NULL), key_agreement_(NULL) { +} + +Cipher::~Cipher() { + if (activated_) { + CleanUp(); + } +} + +void Cipher::CleanUp() { + if (encoder_ != NULL) { + delete encoder_; + encoder_ = NULL; + } + if (decoder_ != NULL) { + delete decoder_; + decoder_ = NULL; + } + if (key_agreement_ != NULL) { + delete key_agreement_; + key_agreement_ = NULL; + } + activated_ = false; +} + +size_t Cipher::Prepare(void* buffer, size_t* length) { + assert(key_agreement_ == NULL); + key_agreement_ = new DH2KeyAgreement(); + assert(key_agreement_ != NULL); + size_t agreed_length = key_agreement_->Prepare(buffer, length); + if (agreed_length == 0) { + delete key_agreement_; + key_agreement_ = NULL; + } + return agreed_length; +} + +bool Cipher::Activate(bool polarity, size_t agreed_length, + const void* buffer, size_t length) { + assert(activated_ == false); + assert(key_agreement_ != NULL); + if (activated_ != false) + return false; + + if (key_agreement_->Agree(agreed_length, buffer, length)) { + activated_ = SetUp(polarity); + } + delete key_agreement_; + key_agreement_ = NULL; + return activated_; +} + +bool Cipher::SetUp(bool polarity) { + assert(key_agreement_ != NULL); + const SecByteBlock& shared = key_agreement_->shared(); + + // Pick a block cipher algorithm + + if (shared.size() < 2) { + return false; + } + int hint_0 = shared.BytePtr()[*(shared.BytePtr()) % shared.size()]; + int hint_1 = shared.BytePtr()[*(shared.BytePtr() + 1) % shared.size()]; + BlockCipherAlgorithm* detail_0 = BlockCipherAlgorithm::Pick(hint_0); + BlockCipherAlgorithm* detail_1 = BlockCipherAlgorithm::Pick(hint_1); + assert(detail_0 != NULL); + assert(detail_1 != NULL); + std::auto_ptr algorithm_0(detail_0); + std::auto_ptr algorithm_1(detail_1); + + const size_t key_length_0 = algorithm_0->GetDefaultKeyLength(); + const size_t iv_length_0 = algorithm_0->GetBlockSize(); + if (shared.size() < key_length_0 || shared.size() < iv_length_0) { + return false; + } + const size_t key_length_1 = algorithm_1->GetDefaultKeyLength(); + const size_t iv_length_1 = algorithm_1->GetBlockSize(); + if (shared.size() < key_length_1 || shared.size() < iv_length_1) { + return false; + } + + // Pick encryption keys and initial vectors + + SecByteBlock key_0(key_length_0), iv_0(iv_length_0); + SecByteBlock key_1(key_length_1), iv_1(iv_length_1); + + size_t offset; + + key_0.Assign(shared, key_length_0); + offset = key_length_0; +#ifdef __GNUC__ + offset = std::min(key_length_0, shared.size() - key_length_1); +#else + offset = min(key_length_0, shared.size() - key_length_1); +#endif + key_1.Assign(shared.BytePtr() + offset, key_length_1); + + offset = shared.size() - iv_length_0; + iv_0.Assign(shared.BytePtr() + offset, iv_length_0); + offset = (offset < iv_length_1 ? 0 : offset - iv_length_1); + iv_1.Assign(shared.BytePtr() + offset, iv_length_1); + + // Create encryption/decryption objects + + if (polarity) { + encoder_ = algorithm_1->CreateEncoder(key_1, key_1.size(), iv_1); + decoder_ = algorithm_0->CreateDecoder(key_0, key_0.size(), iv_0); + } else { + encoder_ = algorithm_0->CreateEncoder(key_0, key_0.size(), iv_0); + decoder_ = algorithm_1->CreateDecoder(key_1, key_1.size(), iv_1); + } + assert(encoder_ != NULL); + assert(decoder_ != NULL); + return true; +} + +BlockCipherAlgorithm* BlockCipherAlgorithm::Pick(int hint) { + BlockCipherAlgorithm* detail; + int selector = hint % kMaxAlgorithms; + switch (selector) { +// case kAES: +// detail = new BlockCipherDetail(); + break; + case kRC6: + detail = new BlockCipherDetail(); + break; + case kMARS: + detail = new BlockCipherDetail(); + break; + case kTwofish: + detail = new BlockCipherDetail(); + break; + case kSerpent: + detail = new BlockCipherDetail(); + break; + case kCAST256: + detail = new BlockCipherDetail(); + break; + case kIDEA: + detail = new BlockCipherDetail(); + break; + case k3DES: + detail = new BlockCipherDetail(); + break; + case kCamellia: + detail = new BlockCipherDetail(); + break; + case kSEED: + detail = new BlockCipherDetail(); + break; + case kRC5: + detail = new BlockCipherDetail(); + break; + case kBlowfish: + detail = new BlockCipherDetail(); + break; + case kTEA: + detail = new BlockCipherDetail(); + break; +// case kSKIPJACK: +// detail = new BlockCipherDetail(); +// break; + case kSHACAL2: + detail = new BlockCipherDetail(); + break; + case kDefault: + default: + detail = new BlockCipherDetail(); // default algorithm + break; + } + return detail; +} + +DH2KeyAgreement::DH2KeyAgreement() : dh_(), dh2_(dh_) { +} + +DH2KeyAgreement::~DH2KeyAgreement() { +} + +size_t DH2KeyAgreement::Prepare(void* buffer, size_t* length) { + // RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup + // http://tools.ietf.org/html/rfc5114#section-2.1 + Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" + "DF1FB2BC2E4A4371"); + + Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + "855E6EEB22B3B2E5"); + + Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353"); + + // Schnorr Group primes are of the form p = rq + 1, p and q prime. They + // provide a subgroup order. In the case of 1024-bit MODP Group, the + // security level is 80 bits (based on the 160-bit prime order subgroup). + + // For a compare/contrast of using the maximum security level, see + // dh-unified.zip. Also see http://www.cryptopp.com/wiki/Diffie-Hellman + // and http://www.cryptopp.com/wiki/Security_level . + + AutoSeededRandomPool rnd; + + dh_.AccessGroupParameters().Initialize(p, q, g); + + if(!dh_.GetGroupParameters().ValidateGroup(rnd, 3)) { + // Failed to validate prime and generator + return 0; + } + + p = dh_.GetGroupParameters().GetModulus(); + q = dh_.GetGroupParameters().GetSubgroupOrder(); + g = dh_.GetGroupParameters().GetGenerator(); + + // http://groups.google.com/group/sci.crypt/browse_thread/thread/7dc7eeb04a09f0ce + Integer v = ModularExponentiation(g, q, p); + if(v != Integer::One()) { + // Failed to verify order of the subgroup + return 0; + } + + ////////////////////////////////////////////////////////////// + + spriv_key_.New(dh2_.StaticPrivateKeyLength()); + epriv_key_.New(dh2_.EphemeralPrivateKeyLength()); + SecByteBlock spub_key(dh2_.StaticPublicKeyLength()); + SecByteBlock epub_key(dh2_.EphemeralPublicKeyLength()); + + dh2_.GenerateStaticKeyPair(rnd, spriv_key_, spub_key); + dh2_.GenerateEphemeralKeyPair(rnd, epriv_key_, epub_key); + + // Prepare key agreement data + const size_t spub_key_length = spub_key.size(); + const size_t epub_key_length = epub_key.size(); + const size_t data_length = spub_key_length + epub_key_length; + if (*length < data_length) { + // Not enough data buffer length + return 0; + } + *length = data_length; + byte* buf = (byte*)buffer; + memcpy(buf, spub_key.BytePtr(), spub_key_length); + memcpy(buf + spub_key_length, epub_key.BytePtr(), epub_key_length); + + return dh2_.AgreedValueLength(); +} + +bool DH2KeyAgreement::Agree(size_t agreed_length, const void* buffer, size_t length) { + if (agreed_length != dh2_.AgreedValueLength()) { + // Shared secret size mismatch + return false; + } + const size_t spub_key_length = dh2_.StaticPublicKeyLength(); + const size_t epub_key_length = dh2_.EphemeralPublicKeyLength(); + if (length != (spub_key_length + epub_key_length)) { + // Wrong data length + return false; + } + shared_.New(dh2_.AgreedValueLength()); + const byte* buf = (const byte*)buffer; + if (!dh2_.Agree(shared_, spriv_key_, epriv_key_, buf, buf + spub_key_length)) { + // Failed to reach shared secret + return false; + } + return true; +} + +#endif // _IMPROVED_PACKET_ENCRYPTION_ + +// EOF cipher.cpp diff --git a/game/src/cipher.h b/game/src/cipher.h new file mode 100644 index 0000000..9230dcf --- /dev/null +++ b/game/src/cipher.h @@ -0,0 +1,60 @@ +#ifndef __CIPHER_H__ +#define __CIPHER_H__ + +#ifdef _IMPROVED_PACKET_ENCRYPTION_ + +#include + +// Forward declaration +class KeyAgreement; + +// Communication channel encryption handler. +class Cipher { + public: + Cipher(); + ~Cipher(); + + void CleanUp(); + + // Returns agreed value length in bytes, or zero on failure. + size_t Prepare(void* buffer, size_t* length); + // Try to activate cipher algorithm with agreement data received from peer. + bool Activate(bool polarity, size_t agreed_length, + const void* buffer, size_t length); + + // Encrypts the given block of data. (no padding required) + void Encrypt(void* buffer, size_t length) { + assert(activated_); + if (!activated_) { + return; + } + encoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); + } + // Decrypts the given block of data. (no padding required) + void Decrypt(void* buffer, size_t length) { + assert(activated_); + if (!activated_) { + return; + } + decoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); + } + + bool activated() const { return activated_; } + void set_activated(bool value) { activated_ = value; } + + bool IsKeyPrepared() { return key_agreement_ != NULL; } + + private: + bool SetUp(bool polarity); + + bool activated_; + + CryptoPP::SymmetricCipher* encoder_; + CryptoPP::SymmetricCipher* decoder_; + + KeyAgreement* key_agreement_; +}; + +#endif // _IMPROVED_PACKET_ENCRYPTION_ + +#endif // __CIPHER_H__ diff --git a/game/src/cmd.cpp b/game/src/cmd.cpp new file mode 100644 index 0000000..3eef280 --- /dev/null +++ b/game/src/cmd.cpp @@ -0,0 +1,737 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "char.h" +#include "locale_service.h" +#include "log.h" +#include "desc.h" + +ACMD(do_user_horse_ride); +ACMD(do_user_horse_back); +ACMD(do_user_horse_feed); + +ACMD(do_pcbang_update); +ACMD(do_pcbang_check); + +// ADD_COMMAND_SLOW_STUN +ACMD(do_slow); +ACMD(do_stun); +// END_OF_ADD_COMMAND_SLOW_STUN + +ACMD(do_warp); +ACMD(do_goto); +ACMD(do_item); +ACMD(do_mob); +ACMD(do_mob_ld); +ACMD(do_mob_aggresive); +ACMD(do_mob_coward); +ACMD(do_mob_map); +ACMD(do_purge); +ACMD(do_weaken); +ACMD(do_item_purge); +ACMD(do_state); +ACMD(do_notice); +ACMD(do_map_notice); +ACMD(do_big_notice); +ACMD(do_who); +ACMD(do_user); +ACMD(do_disconnect); +ACMD(do_kill); +ACMD(do_emotion_allow); +ACMD(do_emotion); +ACMD(do_transfer); +ACMD(do_set); +ACMD(do_cmd); +ACMD(do_reset); +ACMD(do_greset); +ACMD(do_mount); +ACMD(do_fishing); +ACMD(do_refine_rod); + +// REFINE_PICK +ACMD(do_max_pick); +ACMD(do_refine_pick); +// END_OF_REFINE_PICK + +ACMD(do_fishing_simul); +ACMD(do_console); +ACMD(do_restart); +ACMD(do_advance); +ACMD(do_stat); +ACMD(do_respawn); +ACMD(do_skillup); +ACMD(do_guildskillup); +ACMD(do_pvp); +ACMD(do_point_reset); +ACMD(do_safebox_size); +ACMD(do_safebox_close); +ACMD(do_safebox_password); +ACMD(do_safebox_change_password); +ACMD(do_mall_password); +ACMD(do_mall_close); +ACMD(do_ungroup); +ACMD(do_makeguild); +ACMD(do_deleteguild); +ACMD(do_shutdown); +ACMD(do_group); +ACMD(do_group_random); +ACMD(do_invisibility); +ACMD(do_event_flag); +ACMD(do_get_event_flag); +ACMD(do_private); +ACMD(do_qf); +ACMD(do_clear_quest); +ACMD(do_book); +ACMD(do_reload); +ACMD(do_war); +ACMD(do_nowar); +ACMD(do_setskill); +ACMD(do_setskillother); +ACMD(do_level); +ACMD(do_polymorph); +ACMD(do_polymorph_item); +/* + ACMD(do_b1); + ACMD(do_b2); + ACMD(do_b3); + ACMD(do_b4); + ACMD(do_b5); + ACMD(do_b6); + ACMD(do_b7); + */ +ACMD(do_close_shop); +ACMD(do_set_walk_mode); +ACMD(do_set_run_mode); +ACMD(do_set_skill_group); +ACMD(do_set_skill_point); +ACMD(do_cooltime); +ACMD(do_detaillog); +ACMD(do_monsterlog); + +ACMD(do_gwlist); +ACMD(do_stop_guild_war); +ACMD(do_cancel_guild_war); +ACMD(do_guild_state); + +ACMD(do_pkmode); +ACMD(do_mobile); +ACMD(do_mobile_auth); +ACMD(do_messenger_auth); + +ACMD(do_getqf); +ACMD(do_setqf); +ACMD(do_delqf); +ACMD(do_set_state); + +ACMD(do_forgetme); +ACMD(do_aggregate); +ACMD(do_attract_ranger); +ACMD(do_pull_monster); +ACMD(do_setblockmode); +ACMD(do_priv_empire); +ACMD(do_priv_guild); +ACMD(do_mount_test); +ACMD(do_unmount); +ACMD(do_observer); +ACMD(do_observer_exit); +ACMD(do_socket_item); +ACMD(do_xmas); +ACMD(do_stat_minus); +ACMD(do_stat_reset); +ACMD(do_view_equip); +ACMD(do_block_chat); +ACMD(do_vote_block_chat); + +// BLOCK_CHAT +ACMD(do_block_chat_list); +// END_OF_BLOCK_CHAT + +ACMD(do_party_request); +ACMD(do_party_request_deny); +ACMD(do_party_request_accept); +ACMD(do_build); +ACMD(do_clear_land); + +ACMD(do_horse_state); +ACMD(do_horse_level); +ACMD(do_horse_ride); +ACMD(do_horse_summon); +ACMD(do_horse_unsummon); +ACMD(do_horse_set_stat); + +ACMD(do_save_attribute_to_image); +ACMD(do_affect_remove); + +ACMD(do_change_attr); +ACMD(do_add_attr); +ACMD(do_add_socket); + +ACMD(do_inputall) +{ + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ɾ Էϼ.")); +} + +ACMD(do_show_arena_list); +ACMD(do_end_all_duel); +ACMD(do_end_duel); +ACMD(do_duel); + +ACMD(do_stat_plus_amount); + +ACMD(do_break_marriage); + +ACMD(do_oxevent_show_quiz); +ACMD(do_oxevent_log); +ACMD(do_oxevent_get_attender); + +ACMD(do_effect); +ACMD(do_threeway_war_info ); +ACMD(do_threeway_war_myinfo ); +// +// +ACMD(do_monarch_warpto); +ACMD(do_monarch_transfer); +ACMD(do_monarch_info); +ACMD(do_elect); +ACMD(do_monarch_tax); +ACMD(do_monarch_mob); +ACMD(do_monarch_notice); + +// +ACMD(do_rmcandidacy); +ACMD(do_setmonarch); +ACMD(do_rmmonarch); + +ACMD(do_hair); +//gift notify quest command +ACMD(do_gift); +// ť +ACMD(do_inventory); +ACMD(do_cube); +// +ACMD(do_siege); +ACMD(do_temp); +ACMD(do_frog); + +ACMD(do_check_monarch_money); + +ACMD(do_reset_subskill ); +ACMD(do_flush); + +ACMD(do_eclipse); +ACMD(do_weeklyevent); + +ACMD(do_event_helper); + +ACMD(do_in_game_mall); + +ACMD(do_get_mob_count); + +ACMD(do_dice); +ACMD(do_special_item); + +ACMD(do_click_mall); + +ACMD(do_ride); +ACMD(do_get_item_id_list); +ACMD(do_set_socket); +#ifdef __AUCTION__ +// temp_auction ӽ +ACMD(do_get_auction_list); +ACMD (do_get_my_auction_list); +ACMD (do_get_my_purchase_list); +ACMD(do_get_item_id_list); +ACMD(do_enroll_auction); +ACMD (do_auction_bid); +ACMD (do_auction_impur); +ACMD (do_enroll_wish); +ACMD (do_enroll_sale); + +ACMD (do_get_auctioned_item); +ACMD (do_buy_sold_item); +ACMD (do_cancel_auction); +ACMD (do_cancel_wish); +ACMD (do_cancel_sale); + +ACMD (do_rebid); +ACMD (do_bid_cancel); +#endif +// ڽ º +ACMD(do_costume); +ACMD(do_set_stat); + +// +ACMD (do_can_dead); + +ACMD (do_full_set); +// ְ +ACMD (do_item_full_set); +// ְ ɼ Ӽ +ACMD (do_attr_full_set); +// ų +ACMD (do_all_skill_master); +// . icon Ŭ󿡼 Ȯ . +ACMD (do_use_item); +ACMD (do_dragon_soul); +ACMD (do_ds_list); +ACMD (do_clear_affect); + +struct command_info cmd_info[] = +{ + { "!RESERVED!", NULL, 0, POS_DEAD, GM_IMPLEMENTOR }, /* ݵ ó̾ Ѵ. */ + { "who", do_who, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "war", do_war, 0, POS_DEAD, GM_PLAYER }, + { "warp", do_warp, 0, POS_DEAD, GM_LOW_WIZARD }, + { "user", do_user, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "notice", do_notice, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "notice_map", do_map_notice, 0, POS_DEAD, GM_LOW_WIZARD }, + { "big_notice", do_big_notice, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "nowar", do_nowar, 0, POS_DEAD, GM_PLAYER }, + { "purge", do_purge, 0, POS_DEAD, GM_WIZARD }, + { "weaken", do_weaken, 0, POS_DEAD, GM_GOD }, + { "dc", do_disconnect, 0, POS_DEAD, GM_LOW_WIZARD }, + { "transfer", do_transfer, 0, POS_DEAD, GM_LOW_WIZARD }, + { "goto", do_goto, 0, POS_DEAD, GM_LOW_WIZARD }, + { "level", do_level, 0, POS_DEAD, GM_LOW_WIZARD }, + { "eventflag", do_event_flag, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "geteventflag", do_get_event_flag, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "item", do_item, 0, POS_DEAD, GM_GOD }, + + { "mob", do_mob, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mob_ld", do_mob_ld, 0, POS_DEAD, GM_HIGH_WIZARD }, /* ġ ȯ /mob_ld vnum x y dir */ + { "ma", do_mob_aggresive, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mc", do_mob_coward, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mm", do_mob_map, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "kill", do_kill, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "ipurge", do_item_purge, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "group", do_group, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "grrandom", do_group_random, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "set", do_set, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reset", do_reset, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "greset", do_greset, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "advance", do_advance, 0, POS_DEAD, GM_GOD }, + { "book", do_book, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "console", do_console, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "shutdow", do_inputall, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "shutdown", do_shutdown, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "stat", do_stat, 0, POS_DEAD, GM_PLAYER }, + { "stat-", do_stat_minus, 0, POS_DEAD, GM_PLAYER }, + { "stat_reset", do_stat_reset, 0, POS_DEAD, GM_LOW_WIZARD }, + { "state", do_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + // ADD_COMMAND_SLOW_STUN + { "stun", do_stun, 0, POS_DEAD, GM_LOW_WIZARD }, + { "slow", do_slow, 0, POS_DEAD, GM_LOW_WIZARD }, + // END_OF_ADD_COMMAND_SLOW_STUN + + { "respawn", do_respawn, 0, POS_DEAD, GM_WIZARD }, + + { "makeguild", do_makeguild, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "deleteguild", do_deleteguild, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "mount", do_mount, 0, POS_MOUNTING, GM_PLAYER }, + { "restart_here", do_restart, SCMD_RESTART_HERE, POS_DEAD, GM_PLAYER }, + { "restart_town", do_restart, SCMD_RESTART_TOWN, POS_DEAD, GM_PLAYER }, + { "phase_selec", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "phase_select", do_cmd, SCMD_PHASE_SELECT, POS_DEAD, GM_PLAYER }, + { "qui", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "quit", do_cmd, SCMD_QUIT, POS_DEAD, GM_PLAYER }, + { "logou", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "logout", do_cmd, SCMD_LOGOUT, POS_DEAD, GM_PLAYER }, + { "skillup", do_skillup, 0, POS_DEAD, GM_PLAYER }, + { "gskillup", do_guildskillup, 0, POS_DEAD, GM_PLAYER }, + { "pvp", do_pvp, 0, POS_DEAD, GM_PLAYER }, + { "safebox", do_safebox_size, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "safebox_close", do_safebox_close, 0, POS_DEAD, GM_PLAYER }, + { "safebox_passwor",do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "safebox_password",do_safebox_password, 0, POS_DEAD, GM_PLAYER }, + { "safebox_change_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "safebox_change_password", do_safebox_change_password, 0, POS_DEAD, GM_PLAYER }, + { "mall_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER }, + { "mall_password", do_mall_password, 0, POS_DEAD, GM_PLAYER }, + { "mall_close", do_mall_close, 0, POS_DEAD, GM_PLAYER }, + + // Group Command + { "ungroup", do_ungroup, 0, POS_DEAD, GM_PLAYER }, + + // REFINE_ROD_HACK_BUG_FIX + { "refine_rod", do_refine_rod, 0, POS_DEAD, GM_IMPLEMENTOR }, + // END_OF_REFINE_ROD_HACK_BUG_FIX + + // REFINE_PICK + { "refine_pick", do_refine_pick, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "max_pick", do_max_pick, 0, POS_DEAD, GM_IMPLEMENTOR }, + // END_OF_REFINE_PICK + + { "fish_simul", do_fishing_simul, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "invisible", do_invisibility, 0, POS_DEAD, GM_LOW_WIZARD }, + { "qf", do_qf, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "clear_quest", do_clear_quest, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "close_shop", do_close_shop, 0, POS_DEAD, GM_PLAYER }, + + { "set_walk_mode", do_set_walk_mode, 0, POS_DEAD, GM_PLAYER }, + { "set_run_mode", do_set_run_mode, 0, POS_DEAD, GM_PLAYER }, + { "setjob",do_set_skill_group, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "setskill", do_setskill, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setskillother", do_setskillother, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "setskillpoint", do_set_skill_point, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reload", do_reload, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "cooltime", do_cooltime, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "gwlist", do_gwlist, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gwstop", do_stop_guild_war, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gwcancel", do_cancel_guild_war, 0, POS_DEAD, GM_LOW_WIZARD }, + { "gstate", do_guild_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "pkmode", do_pkmode, 0, POS_DEAD, GM_PLAYER }, + { "messenger_auth", do_messenger_auth, 0, POS_DEAD, GM_PLAYER }, + + { "getqf", do_getqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setqf", do_setqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "delqf", do_delqf, 0, POS_DEAD, GM_LOW_WIZARD }, + { "set_state", do_set_state, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "α׸", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD }, + { "ͺ", do_monsterlog, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "detaillog", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD }, + { "monsterlog", do_monsterlog, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "forgetme", do_forgetme, 0, POS_DEAD, GM_LOW_WIZARD }, + { "aggregate", do_aggregate, 0, POS_DEAD, GM_LOW_WIZARD }, + { "attract_ranger", do_attract_ranger, 0, POS_DEAD, GM_LOW_WIZARD }, + { "pull_monster", do_pull_monster, 0, POS_DEAD, GM_LOW_WIZARD }, + { "setblockmode", do_setblockmode, 0, POS_DEAD, GM_PLAYER }, + { "polymorph", do_polymorph, 0, POS_DEAD, GM_LOW_WIZARD }, + { "polyitem", do_polymorph_item, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "priv_empire", do_priv_empire, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "priv_guild", do_priv_guild, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "mount_test", do_mount_test, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "unmount", do_unmount, 0, POS_DEAD, GM_PLAYER }, + { "private", do_private, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "party_request", do_party_request, 0, POS_DEAD, GM_PLAYER }, + { "party_request_accept", do_party_request_accept,0, POS_DEAD, GM_PLAYER }, + { "party_request_deny", do_party_request_deny,0, POS_DEAD, GM_PLAYER }, + { "observer", do_observer, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "observer_exit", do_observer_exit, 0, POS_DEAD, GM_PLAYER }, + { "socketitem", do_socket_item, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "saveati", do_save_attribute_to_image, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "xmas_boom", do_xmas, SCMD_XMAS_BOOM, POS_DEAD, GM_HIGH_WIZARD }, + { "xmas_snow", do_xmas, SCMD_XMAS_SNOW, POS_DEAD, GM_HIGH_WIZARD }, + { "xmas_santa", do_xmas, SCMD_XMAS_SANTA, POS_DEAD, GM_HIGH_WIZARD }, + { "view_equip", do_view_equip, 0, POS_DEAD, GM_PLAYER }, + { "jy", do_block_chat, 0, POS_DEAD, GM_HIGH_WIZARD }, + + // BLOCK_CHAT + { "vote_block_chat", do_vote_block_chat, 0, POS_DEAD, GM_PLAYER }, + { "block_chat", do_block_chat, 0, POS_DEAD, GM_PLAYER }, + { "block_chat_list",do_block_chat_list, 0, POS_DEAD, GM_PLAYER }, + // END_OF_BLOCK_CHAT + + { "build", do_build, 0, POS_DEAD, GM_PLAYER }, + { "clear_land", do_clear_land, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "affect_remove", do_affect_remove, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "horse_state", do_horse_state, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_level", do_horse_level, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_ride", do_horse_ride, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_summon", do_horse_summon, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_unsummon", do_horse_unsummon, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "horse_set_stat", do_horse_set_stat, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "pcbang_update", do_pcbang_update, 0, POS_DEAD, GM_LOW_WIZARD }, + { "pcbang_check", do_pcbang_check, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "emotion_allow", do_emotion_allow, 0, POS_FIGHTING, GM_PLAYER }, + { "kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "slap", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "french_kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "clap", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheer1", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheer2", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + + // DANCE + { "dance1", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance2", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance3", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance4", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance5", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "dance6", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + // END_OF_DANCE + + { "congratulation", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "forgive", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "angry", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "attractive", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "sad", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "shy", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "cheerup", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "banter", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + { "joy", do_emotion, 0, POS_FIGHTING, GM_PLAYER }, + + + { "change_attr", do_change_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "add_attr", do_add_attr, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "add_socket", do_add_socket, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "user_horse_ride", do_user_horse_ride, 0, POS_FISHING, GM_PLAYER }, + { "user_horse_back", do_user_horse_back, 0, POS_FISHING, GM_PLAYER }, + { "user_horse_feed", do_user_horse_feed, 0, POS_FISHING, GM_PLAYER }, + + { "show_arena_list", do_show_arena_list, 0, POS_DEAD, GM_LOW_WIZARD }, + { "end_all_duel", do_end_all_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + { "end_duel", do_end_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + { "duel", do_duel, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "con+", do_stat_plus_amount, POINT_HT, POS_DEAD, GM_LOW_WIZARD }, + { "int+", do_stat_plus_amount, POINT_IQ, POS_DEAD, GM_LOW_WIZARD }, + { "str+", do_stat_plus_amount, POINT_ST, POS_DEAD, GM_LOW_WIZARD }, + { "dex+", do_stat_plus_amount, POINT_DX, POS_DEAD, GM_LOW_WIZARD }, + + { "break_marriage", do_break_marriage, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "show_quiz", do_oxevent_show_quiz, 0, POS_DEAD, GM_LOW_WIZARD }, + { "log_oxevent", do_oxevent_log, 0, POS_DEAD, GM_LOW_WIZARD }, + { "get_oxevent_att", do_oxevent_get_attender,0, POS_DEAD, GM_LOW_WIZARD }, + + { "effect", do_effect, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "threeway_info", do_threeway_war_info, 0, POS_DEAD, GM_LOW_WIZARD}, + { "threeway_myinfo", do_threeway_war_myinfo, 0, POS_DEAD, GM_LOW_WIZARD}, + { "mto", do_monarch_warpto, 0, POS_DEAD, GM_PLAYER}, + { "mtr", do_monarch_transfer, 0, POS_DEAD, GM_PLAYER}, + { "minfo", do_monarch_info, 0, POS_DEAD, GM_PLAYER}, + { "mtax", do_monarch_tax, 0, POS_DEAD, GM_PLAYER}, + { "mmob", do_monarch_mob, 0, POS_DEAD, GM_PLAYER}, + { "elect", do_elect, 0, POS_DEAD, GM_HIGH_WIZARD}, + { "rmcandidacy", do_rmcandidacy, 0, POS_DEAD, GM_LOW_WIZARD}, + { "setmonarch", do_setmonarch, 0, POS_DEAD, GM_LOW_WIZARD}, + { "rmmonarch", do_rmmonarch, 0, POS_DEAD, GM_LOW_WIZARD}, + { "hair", do_hair, 0, POS_DEAD, GM_PLAYER }, + { "inventory", do_inventory, 0, POS_DEAD, GM_LOW_WIZARD }, + { "cube", do_cube, 0, POS_DEAD, GM_PLAYER }, + { "siege", do_siege, 0, POS_DEAD, GM_LOW_WIZARD }, + { "temp", do_temp, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "frog", do_frog, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "check_mmoney", do_check_monarch_money, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "reset_subskill", do_reset_subskill, 0, POS_DEAD, GM_HIGH_WIZARD }, + { "flush", do_flush, 0, POS_DEAD, GM_IMPLEMENTOR }, + { "gift", do_gift, 0, POS_DEAD, GM_PLAYER }, //gift + + { "mnotice", do_monarch_notice, 0, POS_DEAD, GM_PLAYER }, + + { "eclipse", do_eclipse, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "weeklyevent", do_weeklyevent, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "eventhelper", do_event_helper, 0, POS_DEAD, GM_HIGH_WIZARD }, + + { "in_game_mall", do_in_game_mall, 0, POS_DEAD, GM_PLAYER }, + + { "get_mob_count", do_get_mob_count, 0, POS_DEAD, GM_LOW_WIZARD }, + + { "dice", do_dice, 0, POS_DEAD, GM_PLAYER }, + { "ֻ", do_dice, 0, POS_DEAD, GM_PLAYER }, + { "special_item", do_special_item, 0, POS_DEAD, GM_IMPLEMENTOR }, + + { "click_mall", do_click_mall, 0, POS_DEAD, GM_PLAYER }, + + { "ride", do_ride, 0, POS_DEAD, GM_PLAYER }, + + { "item_id_list", do_get_item_id_list, 0, POS_DEAD, GM_LOW_WIZARD }, + { "set_socket", do_set_socket, 0, POS_DEAD, GM_LOW_WIZARD }, +#ifdef __AUCTION__ + // auction ӽ + { "auction_list", do_get_auction_list, 0, POS_DEAD, GM_PLAYER }, + { "my_auction_list", do_get_my_auction_list, 0, POS_DEAD, GM_PLAYER }, + { "my_purchase_list", do_get_my_purchase_list, 0, POS_DEAD, GM_PLAYER }, + + + { "enroll_auction", do_enroll_auction, 0, POS_DEAD, GM_PLAYER }, + { "bid", do_auction_bid, 0, POS_DEAD, GM_PLAYER }, + { "impur", do_auction_impur, 0, POS_DEAD, GM_PLAYER }, + { "enroll_wish", do_enroll_wish, 0, POS_DEAD, GM_PLAYER }, + { "enroll_sale", do_enroll_sale, 0, POS_DEAD, GM_PLAYER }, + { "get_auctioned_item", do_get_auctioned_item, 0, POS_DEAD, GM_PLAYER }, + { "buy_sold_item", do_buy_sold_item, 0, POS_DEAD, GM_PLAYER }, + { "cancel_auction", do_cancel_auction, 0, POS_DEAD, GM_PLAYER }, + { "cancel_wish", do_cancel_wish, 0, POS_DEAD, GM_PLAYER }, + { "cancel_sale", do_cancel_sale, 0, POS_DEAD, GM_PLAYER }, + { "rebid", do_rebid, 0, POS_DEAD, GM_PLAYER }, + { "bid_cancel", do_bid_cancel, 0, POS_DEAD, GM_PLAYER }, + +#endif + { "costume", do_costume, 0, POS_DEAD, GM_PLAYER }, + + { "tcon", do_set_stat, POINT_HT, POS_DEAD, GM_LOW_WIZARD }, + { "tint", do_set_stat, POINT_IQ, POS_DEAD, GM_LOW_WIZARD }, + { "tstr", do_set_stat, POINT_ST, POS_DEAD, GM_LOW_WIZARD }, + { "tdex", do_set_stat, POINT_DX, POS_DEAD, GM_LOW_WIZARD }, + + { "cannot_dead", do_can_dead, 1, POS_DEAD, GM_LOW_WIZARD}, + { "can_dead", do_can_dead, 0, POS_DEAD, GM_LOW_WIZARD}, + + { "full_set", do_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "item_full_set", do_item_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "attr_full_set", do_attr_full_set, 0, POS_DEAD, GM_LOW_WIZARD}, + { "all_skill_master", do_all_skill_master, 0, POS_DEAD, GM_LOW_WIZARD}, + { "use_item", do_use_item, 0, POS_DEAD, GM_LOW_WIZARD}, + + { "dragon_soul", do_dragon_soul, 0, POS_DEAD, GM_PLAYER }, + { "ds_list", do_ds_list, 0, POS_DEAD, GM_PLAYER }, + { "do_clear_affect", do_clear_affect, 0, POS_DEAD, GM_LOW_WIZARD}, + + { "\n", NULL, 0, POS_DEAD, GM_IMPLEMENTOR } /* ݵ ̾ Ѵ. */ +}; + +void interpreter_set_privilege(const char *cmd, int lvl) +{ + int i; + + for (i = 0; *cmd_info[i].command != '\n'; ++i) + { + if (!str_cmp(cmd, cmd_info[i].command)) + { + cmd_info[i].gm_level = lvl; + sys_log(0, "Setting command privilege: %s -> %d", cmd, lvl); + break; + } + } +} + +void double_dollar(const char *src, size_t src_len, char *dest, size_t dest_len) +{ + const char * tmp = src; + size_t cur_len = 0; + + // \0 ڸ Ȯ + dest_len -= 1; + + while (src_len-- && *tmp) + { + if (*tmp == '$') + { + if (cur_len + 1 >= dest_len) + break; + + *(dest++) = '$'; + *(dest++) = *(tmp++); + cur_len += 2; + } + else + { + if (cur_len >= dest_len) + break; + + *(dest++) = *(tmp++); + cur_len += 1; + } + } + + *dest = '\0'; +} + +void interpret_command(LPCHARACTER ch, const char * argument, size_t len) +{ + if (NULL == ch) + { + sys_err ("NULL CHRACTER"); + return ; + } + + char cmd[128 + 1]; // buffer overflow ʵ Ϻη ̸ ª + char new_line[256 + 1]; + const char * line; + int icmd; + + if (len == 0 || !*argument) + return; + + double_dollar(argument, len, new_line, sizeof(new_line)); + + size_t cmdlen; + line = first_cmd(new_line, cmd, sizeof(cmd), &cmdlen); + + for (icmd = 1; *cmd_info[icmd].command != '\n'; ++icmd) + { + if (cmd_info[icmd].command_pointer == do_cmd) + { + if (!strcmp(cmd_info[icmd].command, cmd)) // do_cmd ɾ ľ ִ. + break; + } + else if (!strncmp(cmd_info[icmd].command, cmd, cmdlen)) + break; + } + + if (ch->GetPosition() < cmd_info[icmd].minimum_position) + { + switch (ch->GetPosition()) + { + case POS_MOUNTING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ź ¿ ϴ.")); + break; + + case POS_DEAD: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ¿ ϴ.")); + break; + + case POS_SLEEPING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("޼ӿ Կ?")); + break; + + case POS_RESTING: + case POS_SITTING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ͼ .")); + break; + /* + case POS_FIGHTING: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ɰ Դϴ. ϼ.")); + break; + */ + default: + sys_err("unknown position %d", ch->GetPosition()); + break; + } + + return; + } + + if (*cmd_info[icmd].command == '\n') + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + if (cmd_info[icmd].gm_level && cmd_info[icmd].gm_level > ch->GetGMLevel()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + if (strncmp("phase", cmd_info[icmd].command, 5) != 0) // ɾ ó + sys_log(0, "COMMAND: %s: %s", ch->GetName(), cmd_info[icmd].command); + + ((*cmd_info[icmd].command_pointer) (ch, line, icmd, cmd_info[icmd].subcmd)); + + if (ch->GetGMLevel() >= GM_LOW_WIZARD) + { + if (cmd_info[icmd].gm_level >= GM_LOW_WIZARD) + { + if (LC_IsEurope() == true || /*LC_IsNewCIBN() == true || */LC_IsCanada() == true || LC_IsBrazil() == true || LC_IsSingapore() == true ) + { + char buf[1024]; + snprintf( buf, sizeof(buf), "%s", argument ); + + LogManager::instance().GMCommandLog(ch->GetPlayerID(), ch->GetName(), ch->GetDesc()->GetHostName(), g_bChannel, buf); + } + } + } +} + diff --git a/game/src/cmd.h b/game/src/cmd.h new file mode 100644 index 0000000..562c35e --- /dev/null +++ b/game/src/cmd.h @@ -0,0 +1,69 @@ +#ifndef __INC_METIN_II_GAME_CMD_H__ +#define __INC_METIN_II_GAME_CMD_H__ + +#define ACMD(name) void (name)(LPCHARACTER ch, const char *argument, int cmd, int subcmd) +#define CMD_NAME(name) cmd_info[cmd].command + +struct command_info +{ + const char * command; + void (*command_pointer) (LPCHARACTER ch, const char *argument, int cmd, int subcmd); + int subcmd; + int minimum_position; + int gm_level; +}; + +extern struct command_info cmd_info[]; + +extern void interpret_command(LPCHARACTER ch, const char * argument, size_t len); +extern void interpreter_set_privilege(const char * cmd, int lvl); + +enum SCMD_ACTION +{ + SCMD_SLAP, + SCMD_KISS, + SCMD_FRENCH_KISS, + SCMD_HUG, + SCMD_LONG_HUG, + SCMD_SHOLDER, + SCMD_FOLD_ARM +}; + +enum SCMD_CMD +{ + SCMD_LOGOUT, + SCMD_QUIT, + SCMD_PHASE_SELECT, + SCMD_SHUTDOWN, +}; + +enum SCMD_RESTART +{ + SCMD_RESTART_TOWN, + SCMD_RESTART_HERE +}; + +enum SCMD_XMAS +{ + SCMD_XMAS_BOOM, + SCMD_XMAS_SNOW, + SCMD_XMAS_SANTA, +}; + +extern void Shutdown(int iSec); +extern void SendNotice(const char * c_pszBuf); // Ӽ +extern void SendLog(const char * c_pszBuf); // ڿԸ +extern void BroadcastNotice(const char * c_pszBuf); // +extern void SendNoticeMap(const char* c_pszBuf, int nMapIndex, bool bBigFont); // ʿ +extern void SendMonarchNotice(BYTE bEmpire, const char * c_pszBuf); // + +// LUA_ADD_BGM_INFO +void CHARACTER_SetBGMVolumeEnable(); +void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol); +// END_OF_LUA_ADD_BGM_INFO + +// LUA_ADD_GOTO_INFO +extern void CHARACTER_AddGotoInfo(const std::string& c_st_name, BYTE empire, int mapIndex, DWORD x, DWORD y); +// END_OF_LUA_ADD_GOTO_INFO + +#endif diff --git a/game/src/cmd_emotion.cpp b/game/src/cmd_emotion.cpp new file mode 100644 index 0000000..0d1ab5d --- /dev/null +++ b/game/src/cmd_emotion.cpp @@ -0,0 +1,267 @@ +#include "stdafx.h" +#include "utils.h" +#include "char.h" +#include "char_manager.h" +#include "motion.h" +#include "packet.h" +#include "buffer_manager.h" +#include "unique_item.h" +#include "wedding.h" + +#define NEED_TARGET (1 << 0) +#define NEED_PC (1 << 1) +#define WOMAN_ONLY (1 << 2) +#define OTHER_SEX_ONLY (1 << 3) +#define SELF_DISARM (1 << 4) +#define TARGET_DISARM (1 << 5) +#define BOTH_DISARM (SELF_DISARM | TARGET_DISARM) + +struct emotion_type_s +{ + const char * command; + const char * command_to_client; + long flag; + float extra_delay; +} emotion_types[] = { + { "Ű", "french_kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 2.0f }, + { "ǻ", "kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 1.5f }, + { "", "slap", NEED_PC | SELF_DISARM, 1.5f }, + { "ڼ", "clap", 0, 1.0f }, + { "", "cheer1", 0, 1.0f }, + { "", "cheer2", 0, 1.0f }, + + // DANCE + { "1", "dance1", 0, 1.0f }, + { "2", "dance2", 0, 1.0f }, + { "3", "dance3", 0, 1.0f }, + { "4", "dance4", 0, 1.0f }, + { "5", "dance5", 0, 1.0f }, + { "6", "dance6", 0, 1.0f }, + // END_OF_DANCE + { "", "congratulation", 0, 1.0f }, + { "뼭", "forgive", 0, 1.0f }, + { "ȭ", "angry", 0, 1.0f }, + { "Ȥ", "attractive", 0, 1.0f }, + { "", "sad", 0, 1.0f }, + { "", "shy", 0, 1.0f }, + { "", "cheerup", 0, 1.0f }, + { "", "banter", 0, 1.0f }, + { "", "joy", 0, 1.0f }, + { "\n", "\n", 0, 0.0f }, + /* + //{ "Ű", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_FRENCH_KISS, 1.0f }, + { "ǻ", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_KISS, 1.0f }, + { "ȱ", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_SHORT_HUG, 1.0f }, + { "", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_LONG_HUG, 1.0f }, + { "", NEED_PC | SELF_DISARM, MOTION_ACTION_PUT_ARMS_SHOULDER, 0.0f }, + { "¯", NEED_PC | WOMAN_ONLY | SELF_DISARM, MOTION_ACTION_FOLD_ARM, 0.0f }, + { "", NEED_PC | SELF_DISARM, MOTION_ACTION_SLAP, 1.5f }, + + { "Ķ", 0, MOTION_ACTION_CHEER_01, 0.0f }, + { "", 0, MOTION_ACTION_CHEER_02, 0.0f }, + { "ڼ", 0, MOTION_ACTION_CHEER_03, 0.0f }, + + { "ȣȣ", 0, MOTION_ACTION_LAUGH_01, 0.0f }, + { "űű", 0, MOTION_ACTION_LAUGH_02, 0.0f }, + { "", 0, MOTION_ACTION_LAUGH_03, 0.0f }, + + { "", 0, MOTION_ACTION_CRY_01, 0.0f }, + { "", 0, MOTION_ACTION_CRY_02, 0.0f }, + + { "λ", 0, MOTION_ACTION_GREETING_01, 0.0f }, + { "", 0, MOTION_ACTION_GREETING_02, 0.0f }, + { "λ", 0, MOTION_ACTION_GREETING_03, 0.0f }, + + { "", 0, MOTION_ACTION_INSULT_01, 0.0f }, + { "", SELF_DISARM, MOTION_ACTION_INSULT_02, 0.0f }, + { "", 0, MOTION_ACTION_INSULT_03, 0.0f }, + + { "", 0, MOTION_ACTION_ETC_01, 0.0f }, + { "", 0, MOTION_ACTION_ETC_02, 0.0f }, + { "", 0, MOTION_ACTION_ETC_03, 0.0f }, + { "", 0, MOTION_ACTION_ETC_04, 0.0f }, + { "ơ", 0, MOTION_ACTION_ETC_05, 0.0f }, + { "", 0, MOTION_ACTION_ETC_06, 0.0f }, + */ +}; + + +std::set > s_emotion_set; + +ACMD(do_emotion_allow) +{ + if ( ch->GetArena() ) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD val = 0; str_to_number(val, arg1); + s_emotion_set.insert(std::make_pair(ch->GetVID(), val)); +} + +bool CHARACTER_CanEmotion(CHARACTER& rch) +{ + // ȥ ʿ ִ. + if (marriage::WeddingManager::instance().IsWeddingMap(rch.GetMapIndex())) + return true; + + // ִ. + if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK)) + return true; + + if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK2)) + return true; + + return false; +} + +ACMD(do_emotion) +{ + int i; + { + if (ch->IsRiding()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ǥ ϴ.")); + return; + } + } + + for (i = 0; *emotion_types[i].command != '\n'; ++i) + { + if (!strcmp(cmd_info[cmd].command, emotion_types[i].command)) + break; + + if (!strcmp(cmd_info[cmd].command, emotion_types[i].command_to_client)) + break; + } + + if (*emotion_types[i].command == '\n') + { + sys_err("cannot find emotion"); + return; + } + + if (!CHARACTER_CanEmotion(*ch)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ÿ ֽϴ.")); + return; + } + + if (IS_SET(emotion_types[i].flag, WOMAN_ONLY) && SEX_MALE==GET_SEX(ch)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڸ ֽϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + LPCHARACTER victim = NULL; + + if (*arg1) + victim = ch->FindCharacterInView(arg1, IS_SET(emotion_types[i].flag, NEED_PC)); + + if (IS_SET(emotion_types[i].flag, NEED_TARGET | NEED_PC)) + { + if (!victim) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ϴ.")); + return; + } + } + + if (victim) + { + if (!victim->IsPC() || victim == ch) + return; + + if (victim->IsRiding()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ǥ ϴ.")); + return; + } + + long distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY()); + + if (distance < 10) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ʹ ֽϴ.")); + return; + } + + if (distance > 500) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ʹ ָ ֽϴ")); + return; + } + + if (IS_SET(emotion_types[i].flag, OTHER_SEX_ONLY)) + { + if (GET_SEX(ch)==GET_SEX(victim)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̼ ֽϴ.")); + return; + } + } + + if (IS_SET(emotion_types[i].flag, NEED_PC)) + { + if (s_emotion_set.find(std::make_pair(victim->GetVID(), ch->GetVID())) == s_emotion_set.end()) + { + if (true == marriage::CManager::instance().IsMarried( ch->GetPlayerID() )) + { + const marriage::TMarriage* marriageInfo = marriage::CManager::instance().Get( ch->GetPlayerID() ); + + const DWORD other = marriageInfo->GetOther( ch->GetPlayerID() ); + + if (0 == other || other != victim->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ൿ ȣ Ͽ մϴ.")); + return; + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ൿ ȣ Ͽ մϴ.")); + return; + } + } + + s_emotion_set.insert(std::make_pair(ch->GetVID(), victim->GetVID())); + } + } + + char chatbuf[256+1]; + int len = snprintf(chatbuf, sizeof(chatbuf), "%s %u %u", + emotion_types[i].command_to_client, + (DWORD) ch->GetVID(), victim ? (DWORD) victim->GetVID() : 0); + + if (len < 0 || len >= (int) sizeof(chatbuf)) + len = sizeof(chatbuf) - 1; + + ++len; // \0 + + TPacketGCChat pack_chat; + pack_chat.header = HEADER_GC_CHAT; + pack_chat.size = sizeof(TPacketGCChat) + len; + pack_chat.type = CHAT_TYPE_COMMAND; + pack_chat.id = 0; + TEMP_BUFFER buf; + buf.write(&pack_chat, sizeof(TPacketGCChat)); + buf.write(chatbuf, len); + + ch->PacketAround(buf.read_peek(), buf.size()); + + if (victim) + sys_log(1, "ACTION: %s TO %s", emotion_types[i].command, victim->GetName()); + else + sys_log(1, "ACTION: %s", emotion_types[i].command); +} + diff --git a/game/src/cmd_general.cpp b/game/src/cmd_general.cpp new file mode 100644 index 0000000..1729218 --- /dev/null +++ b/game/src/cmd_general.cpp @@ -0,0 +1,2678 @@ +#include "stdafx.h" +#ifdef __FreeBSD__ +#include +#else +#include "../../libthecore/include/xmd5.h" +#endif + +#include "utils.h" +#include "config.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "char.h" +#include "char_manager.h" +#include "motion.h" +#include "packet.h" +#include "affect.h" +#include "pvp.h" +#include "start_position.h" +#include "party.h" +#include "guild_manager.h" +#include "p2p.h" +#include "dungeon.h" +#include "messenger_manager.h" +#include "war_map.h" +#include "questmanager.h" +#include "item_manager.h" +#include "monarch.h" +#include "mob_manager.h" +#include "dev_log.h" +#include "item.h" +#include "arena.h" +#include "buffer_manager.h" +#include "unique_item.h" +#include "threeway_war.h" +#include "log.h" +#include "../../common/VnumHelper.h" +#ifdef __AUCTION__ +#include "auction_manager.h" +#endif + +extern int g_server_id; + +extern int g_nPortalLimitTime; + +ACMD(do_user_horse_ride) +{ + if (ch->IsObserverMode()) + return; + + if (ch->IsDead() || ch->IsStun()) + return; + + if (ch->IsHorseRiding() == false) + { + // ƴ ٸŻ Ÿִ. + if (ch->GetMountVnum()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ż ̿Դϴ.")); + return; + } + + if (ch->GetHorse() == NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + return; + } + + ch->StartRiding(); + } + else + { + ch->StopRiding(); + } +} + +ACMD(do_user_horse_back) +{ + if (ch->GetHorse() != NULL) + { + ch->HorseSummon(false); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ½ϴ.")); + } + else if (ch->IsHorseRiding() == true) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ.")); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + } +} + +ACMD(do_user_horse_feed) +{ + // λ ¿ ̸ . + if (ch->GetMyShop()) + return; + + if (ch->GetHorse() == NULL) + { + if (ch->IsHorseRiding() == false) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ź ¿ ̸ ϴ.")); + return; + } + + DWORD dwFood = ch->GetHorseGrade() + 50054 - 1; + + if (ch->CountSpecifyItem(dwFood) > 0) + { + ch->RemoveSpecifyItem(dwFood, 1); + ch->FeedHorse(); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %s%s ־ϴ."), + ITEM_MANAGER::instance().GetTable(dwFood)->szLocaleName, + g_iUseLocale ? "" : under_han(ITEM_MANAGER::instance().GetTable(dwFood)->szLocaleName) ? LC_TEXT("") : LC_TEXT("")); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ʿմϴ"), ITEM_MANAGER::instance().GetTable(dwFood)->szLocaleName); + } +} + +#define MAX_REASON_LEN 128 + +EVENTINFO(TimedEventInfo) +{ + DynamicCharacterPtr ch; + int subcmd; + int left_second; + char szReason[MAX_REASON_LEN]; + + TimedEventInfo() + : ch() + , subcmd( 0 ) + , left_second( 0 ) + { + ::memset( szReason, 0, MAX_REASON_LEN ); + } +}; + +struct SendDisconnectFunc +{ + void operator () (LPDESC d) + { + if (d->GetCharacter()) + { + if (d->GetCharacter()->GetGMLevel() == GM_PLAYER) + d->GetCharacter()->ChatPacket(CHAT_TYPE_COMMAND, "quit Shutdown(SendDisconnectFunc)"); + } + } +}; + +struct DisconnectFunc +{ + void operator () (LPDESC d) + { + if (d->GetType() == DESC_TYPE_CONNECTOR) + return; + + if (d->IsPhase(PHASE_P2P)) + return; + + if (d->GetCharacter()) + d->GetCharacter()->Disconnect("Shutdown(DisconnectFunc)"); + + d->SetPhase(PHASE_CLOSE); + } +}; + +EVENTINFO(shutdown_event_data) +{ + int seconds; + + shutdown_event_data() + : seconds( 0 ) + { + } +}; + +EVENTFUNC(shutdown_event) +{ + shutdown_event_data* info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "shutdown_event> Null pointer" ); + return 0; + } + + int * pSec = & (info->seconds); + + if (*pSec < 0) + { + sys_log(0, "shutdown_event sec %d", *pSec); + + if (--*pSec == -10) + { + const DESC_MANAGER::DESC_SET & c_set_desc = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_set_desc.begin(), c_set_desc.end(), DisconnectFunc()); + return passes_per_sec; + } + else if (*pSec < -10) + return 0; + + return passes_per_sec; + } + else if (*pSec == 0) + { + const DESC_MANAGER::DESC_SET & c_set_desc = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_set_desc.begin(), c_set_desc.end(), SendDisconnectFunc()); + g_bNoMoreClient = true; + --*pSec; + return passes_per_sec; + } + else + { + char buf[64]; + snprintf(buf, sizeof(buf), LC_TEXT("˴ٿ %d ҽϴ."), *pSec); + SendNotice(buf); + + --*pSec; + return passes_per_sec; + } +} + +void Shutdown(int iSec) +{ + if (g_bNoMoreClient) + { + thecore_shutdown(); + return; + } + + CWarMapManager::instance().OnShutdown(); + + char buf[64]; + snprintf(buf, sizeof(buf), LC_TEXT("%d ˴ٿ ˴ϴ."), iSec); + + SendNotice(buf); + + shutdown_event_data* info = AllocEventInfo(); + info->seconds = iSec; + + event_create(shutdown_event, info, 1); +} + +ACMD(do_shutdown) +{ + if (NULL == ch) + { + sys_err("Accept shutdown command from %s.", ch->GetName()); + } + TPacketGGShutdown p; + p.bHeader = HEADER_GG_SHUTDOWN; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGShutdown)); + + Shutdown(10); +} + +EVENTFUNC(timed_event) +{ + TimedEventInfo * info = dynamic_cast( event->info ); + + if ( info == NULL ) + { + sys_err( "timed_event> Null pointer" ); + return 0; + } + + LPCHARACTER ch = info->ch; + if (ch == NULL) { // + return 0; + } + LPDESC d = ch->GetDesc(); + + if (info->left_second <= 0) + { + ch->m_pkTimedEvent = NULL; + + if (true == LC_IsEurope() || true == LC_IsYMIR() || true == LC_IsKorea()) + { + switch (info->subcmd) + { + case SCMD_LOGOUT: + case SCMD_QUIT: + case SCMD_PHASE_SELECT: + { + TPacketNeedLoginLogInfo acc_info; + acc_info.dwPlayerID = ch->GetDesc()->GetAccountTable().id; + + db_clientdesc->DBPacket( HEADER_GD_VALID_LOGOUT, 0, &acc_info, sizeof(acc_info) ); + + LogManager::instance().DetailLoginLog( false, ch ); + } + break; + } + } + + switch (info->subcmd) + { + case SCMD_LOGOUT: + if (d) + d->SetPhase(PHASE_CLOSE); + break; + + case SCMD_QUIT: + ch->ChatPacket(CHAT_TYPE_COMMAND, "quit"); + break; + + case SCMD_PHASE_SELECT: + { + ch->Disconnect("timed_event - SCMD_PHASE_SELECT"); + + if (d) + { + d->SetPhase(PHASE_SELECT); + } + } + break; + } + + return 0; + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ҽϴ."), info->left_second); + --info->left_second; + } + + return PASSES_PER_SEC(1); +} + +ACMD(do_cmd) +{ + /* RECALL_DELAY + if (ch->m_pkRecallEvent != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿϴ.")); + event_cancel(&ch->m_pkRecallEvent); + return; + } + // END_OF_RECALL_DELAY */ + + if (ch->m_pkTimedEvent) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ǿϴ.")); + event_cancel(&ch->m_pkTimedEvent); + return; + } + + switch (subcmd) + { + case SCMD_LOGOUT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("α ȭ ϴ. ø ٸ.")); + break; + + case SCMD_QUIT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. ø ٸ.")); + break; + + case SCMD_PHASE_SELECT: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ij͸ ȯ մϴ. ø ٸ.")); + break; + } + + int nExitLimitTime = 10; + + if (ch->IsHack(false, true, nExitLimitTime) && + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex()) && + (!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG)) + { + return; + } + + switch (subcmd) + { + case SCMD_LOGOUT: + case SCMD_QUIT: + case SCMD_PHASE_SELECT: + { + TimedEventInfo* info = AllocEventInfo(); + + { + if (ch->IsPosition(POS_FIGHTING)) + info->left_second = 10; + else + info->left_second = 3; + } + + info->ch = ch; + info->subcmd = subcmd; + strlcpy(info->szReason, argument, sizeof(info->szReason)); + + ch->m_pkTimedEvent = event_create(timed_event, info, 1); + } + break; + } +} + +ACMD(do_mount) +{ + /* + char arg1[256]; + struct action_mount_param param; + + // ̹ Ÿ + if (ch->GetMountingChr()) + { + char arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + param.x = atoi(arg1); + param.y = atoi(arg2); + param.vid = ch->GetMountingChr()->GetVID(); + param.is_unmount = true; + + float distance = DISTANCE_SQRT(param.x - (DWORD) ch->GetX(), param.y - (DWORD) ch->GetY()); + + if (distance > 600.0f) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" .")); + return; + } + + action_enqueue(ch, ACTION_TYPE_MOUNT, ¶m, 0.0f, true); + return; + } + + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(atoi(arg1)); + + if (!tch->IsNPC() || !tch->IsMountable()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ű⿡ Ż .")); + return; + } + + float distance = DISTANCE_SQRT(tch->GetX() - ch->GetX(), tch->GetY() - ch->GetY()); + + if (distance > 600.0f) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ÿ.")); + return; + } + + param.vid = tch->GetVID(); + param.is_unmount = false; + + action_enqueue(ch, ACTION_TYPE_MOUNT, ¶m, 0.0f, true); + */ +} + +ACMD(do_fishing) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + ch->SetRotation(atof(arg1)); + ch->fishing(); +} + +ACMD(do_console) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "ConsoleEnable"); +} + +ACMD(do_restart) +{ + if (false == ch->IsDead()) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + ch->StartRecoveryEvent(); + return; + } + + if (NULL == ch->m_pkDeadEvent) + return; + + int iTimeToDead = (event_time(ch->m_pkDeadEvent) / passes_per_sec); + + if (subcmd != SCMD_RESTART_TOWN && (!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG)) + { + if (!test_server) + { + if (ch->IsHack()) + { + // ϰ쿡 üũ ʴ´. + if (false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - (180 - g_nPortalLimitTime)); + return; + } + } + + if (iTimeToDead > 170) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - 170); + return; + } + } + } + + //PREVENT_HACK + //DESC : â, ȯ â Ż ϴ ׿ ̿ɼ ־ + // Ÿ ߰ + if (subcmd == SCMD_RESTART_TOWN) + { + if (ch->IsHack()) + { + //, ʿ üũ ʴ´. + if ((!ch->GetWarMap() || ch->GetWarMap()->GetType() == GUILD_WAR_TYPE_FLAG) || + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - (180 - g_nPortalLimitTime)); + return; + } + } + + if (iTimeToDead > 173) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ϴ. (%d )"), iTimeToDead - 173); + return; + } + } + //END_PREVENT_HACK + + ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); + + ch->GetDesc()->SetPhase(PHASE_GAME); + ch->SetPosition(POS_STANDING); + ch->StartRecoveryEvent(); + + //FORKED_LOAD + //DESC: Ÿ Ȱ Ұ Ա ƴ Ÿ ̵ϰ ȴ. + if (1 == quest::CQuestManager::instance().GetEventFlag("threeway_war")) + { + if (subcmd == SCMD_RESTART_TOWN || subcmd == SCMD_RESTART_HERE) + { + if (true == CThreeWayWar::instance().IsThreeWayWarMapIndex(ch->GetMapIndex()) && + false == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + + ch->ReviveInvisible(5); + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + + return; + } + + // + if (true == CThreeWayWar::instance().IsSungZiMapIndex(ch->GetMapIndex())) + { + if (CThreeWayWar::instance().GetReviveTokenForPlayer(ch->GetPlayerID()) <= 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Ȱ ȸ Ҿϴ! ̵մϴ!")); + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + } + else + { + ch->Show(ch->GetMapIndex(), GetSungziStartX(ch->GetEmpire()), GetSungziStartY(ch->GetEmpire())); + } + + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + + return; + } + } + } + //END_FORKED_LOAD + + if (ch->GetDungeon()) + ch->GetDungeon()->UseRevive(ch); + + if (ch->GetWarMap() && !ch->IsObserverMode()) + { + CWarMap * pMap = ch->GetWarMap(); + DWORD dwGuildOpponent = pMap ? pMap->GetGuildOpponent(ch) : 0; + + if (dwGuildOpponent) + { + switch (subcmd) + { + case SCMD_RESTART_TOWN: + sys_log(0, "do_restart: restart town"); + PIXEL_POSITION pos; + + if (CWarMapManager::instance().GetStartPosition(ch->GetMapIndex(), ch->GetGuild()->GetID() < dwGuildOpponent ? 0 : 1, pos)) + ch->Show(ch->GetMapIndex(), pos.x, pos.y); + else + ch->ExitToSavedLocation(); + + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + break; + + case SCMD_RESTART_HERE: + sys_log(0, "do_restart: restart here"); + ch->RestartAtSamePos(); + //ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY()); + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->ReviveInvisible(5); + break; + } + + return; + } + } + + switch (subcmd) + { + case SCMD_RESTART_TOWN: + sys_log(0, "do_restart: restart town"); + PIXEL_POSITION pos; + + if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) + ch->WarpSet(pos.x, pos.y); + else + ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); + + ch->PointChange(POINT_HP, 50 - ch->GetHP()); + ch->DeathPenalty(1); + break; + + case SCMD_RESTART_HERE: + sys_log(0, "do_restart: restart here"); + ch->RestartAtSamePos(); + //ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY()); + ch->PointChange(POINT_HP, 50 - ch->GetHP()); + ch->DeathPenalty(0); + ch->ReviveInvisible(5); + break; + } +} + +#define MAX_STAT 90 + +ACMD(do_stat_reset) +{ + ch->PointChange(POINT_STAT_RESET_COUNT, 12 - ch->GetPoint(POINT_STAT_RESET_COUNT)); +} + +ACMD(do_stat_minus) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (ch->IsPolymorphed()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (ch->GetPoint(POINT_STAT_RESET_COUNT) <= 0) + return; + + if (!strcmp(arg1, "st")) + { + if (ch->GetRealPoint(POINT_ST) <= JobInitialPoints[ch->GetJob()].st) + return; + + ch->SetRealPoint(POINT_ST, ch->GetRealPoint(POINT_ST) - 1); + ch->SetPoint(POINT_ST, ch->GetPoint(POINT_ST) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_ST, 0); + } + else if (!strcmp(arg1, "dx")) + { + if (ch->GetRealPoint(POINT_DX) <= JobInitialPoints[ch->GetJob()].dx) + return; + + ch->SetRealPoint(POINT_DX, ch->GetRealPoint(POINT_DX) - 1); + ch->SetPoint(POINT_DX, ch->GetPoint(POINT_DX) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_DX, 0); + } + else if (!strcmp(arg1, "ht")) + { + if (ch->GetRealPoint(POINT_HT) <= JobInitialPoints[ch->GetJob()].ht) + return; + + ch->SetRealPoint(POINT_HT, ch->GetRealPoint(POINT_HT) - 1); + ch->SetPoint(POINT_HT, ch->GetPoint(POINT_HT) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_HT, 0); + ch->PointChange(POINT_MAX_HP, 0); + } + else if (!strcmp(arg1, "iq")) + { + if (ch->GetRealPoint(POINT_IQ) <= JobInitialPoints[ch->GetJob()].iq) + return; + + ch->SetRealPoint(POINT_IQ, ch->GetRealPoint(POINT_IQ) - 1); + ch->SetPoint(POINT_IQ, ch->GetPoint(POINT_IQ) - 1); + ch->ComputePoints(); + ch->PointChange(POINT_IQ, 0); + ch->PointChange(POINT_MAX_SP, 0); + } + else + return; + + ch->PointChange(POINT_STAT, +1); + ch->PointChange(POINT_STAT_RESET_COUNT, -1); + ch->ComputePoints(); +} + +ACMD(do_stat) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (ch->IsPolymorphed()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("а ߿ ɷ ø ϴ.")); + return; + } + + if (ch->GetPoint(POINT_STAT) <= 0) + return; + + BYTE idx = 0; + + if (!strcmp(arg1, "st")) + idx = POINT_ST; + else if (!strcmp(arg1, "dx")) + idx = POINT_DX; + else if (!strcmp(arg1, "ht")) + idx = POINT_HT; + else if (!strcmp(arg1, "iq")) + idx = POINT_IQ; + else + return; + + if (ch->GetRealPoint(idx) >= MAX_STAT) + return; + + ch->SetRealPoint(idx, ch->GetRealPoint(idx) + 1); + ch->SetPoint(idx, ch->GetPoint(idx) + 1); + ch->ComputePoints(); + ch->PointChange(idx, 0); + + if (idx == POINT_IQ) + { + ch->PointChange(POINT_MAX_HP, 0); + } + else if (idx == POINT_HT) + { + ch->PointChange(POINT_MAX_SP, 0); + } + + ch->PointChange(POINT_STAT, -1); + ch->ComputePoints(); +} + +ACMD(do_pvp) +{ + if (ch->GetArena() != NULL || CArenaManager::instance().IsArenaMap(ch->GetMapIndex()) == true) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(vid); + + if (!pkVictim) + return; + + if (pkVictim->IsNPC()) + return; + + if (pkVictim->GetArena() != NULL) + { + pkVictim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" Դϴ.")); + return; + } + + CPVPManager::instance().Insert(ch, pkVictim); +} + +ACMD(do_guildskillup) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + if (!ch->GetGuild()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 忡 ʽϴ.")); + return; + } + + CGuild* g = ch->GetGuild(); + TGuildMember* gm = g->GetMember(ch->GetPlayerID()); + if (gm->grade == GUILD_LEADER_GRADE) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + g->SkillLevelUp(vnum); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ų ϴ.")); + } +} + +ACMD(do_skillup) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vnum = 0; + str_to_number(vnum, arg1); + + if (true == ch->CanUseSkill(vnum)) + { + ch->SkillLevelUp(vnum); + } + else + { + switch(vnum) + { + case SKILL_HORSE_WILDATTACK: + case SKILL_HORSE_CHARGE: + case SKILL_HORSE_ESCAPE: + case SKILL_HORSE_WILDATTACK_RANGE: + + case SKILL_7_A_ANTI_TANHWAN: + case SKILL_7_B_ANTI_AMSEOP: + case SKILL_7_C_ANTI_SWAERYUNG: + case SKILL_7_D_ANTI_YONGBI: + + case SKILL_8_A_ANTI_GIGONGCHAM: + case SKILL_8_B_ANTI_YEONSA: + case SKILL_8_C_ANTI_MAHWAN: + case SKILL_8_D_ANTI_BYEURAK: + + case SKILL_ADD_HP: + case SKILL_RESIST_PENETRATE: + ch->SkillLevelUp(vnum); + break; + } + } +} + +// +// @version 05/06/20 Bang2ni - Ŀǵ ó Delegate to CHARACTER class +// +ACMD(do_safebox_close) +{ + ch->CloseSafebox(); +} + +// +// @version 05/06/20 Bang2ni - Ŀǵ ó Delegate to CHARACTER class +// +ACMD(do_safebox_password) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + ch->ReqSafeboxLoad(arg1); +} + +ACMD(do_safebox_change_password) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || strlen(arg1)>6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + if (!*arg2 || strlen(arg2)>6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + if (LC_IsBrazil() == true) + { + for (int i = 0; i < 6; ++i) + { + if (arg2[i] == '\0') + break; + + if (isalpha(arg2[i]) == false) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> йȣ ڸ մϴ.")); + return; + } + } + } + + TSafeboxChangePasswordPacket p; + + p.dwID = ch->GetDesc()->GetAccountTable().id; + strlcpy(p.szOldPassword, arg1, sizeof(p.szOldPassword)); + strlcpy(p.szNewPassword, arg2, sizeof(p.szNewPassword)); + + db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_CHANGE_PASSWORD, ch->GetDesc()->GetHandle(), &p, sizeof(p)); +} + +ACMD(do_mall_password) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1 || strlen(arg1) > 6) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> ߸ ȣ Էϼ̽ϴ.")); + return; + } + + int iPulse = thecore_pulse(); + + if (ch->GetMall()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â ̹ ֽϴ.")); + return; + } + + if (iPulse - ch->GetMallLoadTime() < passes_per_sec * 10) // 10ʿ ѹ û + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â> â 10 ȿ ϴ.")); + return; + } + + ch->SetMallLoadTime(iPulse); + + TSafeboxLoadPacket p; + p.dwID = ch->GetDesc()->GetAccountTable().id; + strlcpy(p.szLogin, ch->GetDesc()->GetAccountTable().login, sizeof(p.szLogin)); + strlcpy(p.szPassword, arg1, sizeof(p.szPassword)); + + db_clientdesc->DBPacket(HEADER_GD_MALL_LOAD, ch->GetDesc()->GetHandle(), &p, sizeof(p)); +} + +ACMD(do_mall_close) +{ + if (ch->GetMall()) + { + ch->SetMallLoadTime(thecore_pulse()); + ch->CloseMall(); + ch->Save(); + } +} + +ACMD(do_ungroup) +{ + if (!ch->GetParty()) + return; + + if (!CPartyManager::instance().IsEnablePCParty()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ó ϴ.")); + return; + } + + if (ch->GetDungeon()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> ȿ Ƽ ϴ.")); + return; + } + + LPPARTY pParty = ch->GetParty(); + + if (pParty->GetMemberCount() == 2) + { + // party disband + CPartyManager::instance().DeleteParty(pParty); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<Ƽ> Ƽ ̽ϴ.")); + //pParty->SendPartyRemoveOneToAll(ch); + pParty->Quit(ch->GetPlayerID()); + //pParty->SendPartyRemoveAllToOne(ch); + } +} + +ACMD(do_close_shop) +{ + if (ch->GetMyShop()) + { + ch->CloseMyShop(); + return; + } +} + +ACMD(do_set_walk_mode) +{ + ch->SetNowWalking(true); + ch->SetWalking(true); +} + +ACMD(do_set_run_mode) +{ + ch->SetNowWalking(false); + ch->SetWalking(false); +} + +ACMD(do_war) +{ + // + CGuild * g = ch->GetGuild(); + + if (!g) + return; + + // üũѹ! + if (g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ ٸ £ Դϴ.")); + return; + } + + //Ķ͸ ι + char arg1[256], arg2[256]; + int type = GUILD_WAR_TYPE_FIELD; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + return; + + if (*arg2) + { + str_to_number(type, arg2); + + if (type >= GUILD_WAR_TYPE_MAX_NUM) + type = GUILD_WAR_TYPE_FIELD; + } + + // ̵ µ + DWORD gm_pid = g->GetMasterPID(); + + // üũ( 常 ) + if (gm_pid != ch->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϴ.")); + return; + } + + // 带 + CGuild * opp_g = CGuildManager::instance().FindGuildByName(arg1); + + if (!opp_g) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ׷ 尡 ϴ.")); + return; + } + + // üũ + switch (g->GetGuildWarState(opp_g->GetID())) + { + case GUILD_WAR_NONE: + { + if (opp_g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 尡 ̹ Դϴ.")); + return; + } + + int iWarPrice = KOR_aGuildWarInfo[type].iWarPrice; + + if (g->GetGuildMoney() < iWarPrice) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + + if (opp_g->GetGuildMoney() < iWarPrice) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + } + break; + + case GUILD_WAR_SEND_DECLARE: + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Դϴ.")); + return; + } + break; + + case GUILD_WAR_RECV_DECLARE: + { + if (opp_g->UnderAnyWar()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> 尡 ̹ Դϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + } + } + break; + + case GUILD_WAR_RESERVE: + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ Դϴ.")); + return; + } + break; + + case GUILD_WAR_END: + return; + + default: + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ̹ Դϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + } + + if (!g->CanStartWar(type)) + { + // ִ ʴ´. + if (g->GetLadderPoint() == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ڶ ϴ.")); + sys_log(0, "GuildWar.StartError.NEED_LADDER_POINT"); + } + else if (g->GetMemberCount() < GUILD_WAR_MIN_MEMBER_COUNT) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϱ ؼ ּ %d ־ մϴ."), GUILD_WAR_MIN_MEMBER_COUNT); + sys_log(0, "GuildWar.StartError.NEED_MINIMUM_MEMBER[%d]", GUILD_WAR_MIN_MEMBER_COUNT); + } + else + { + sys_log(0, "GuildWar.StartError.UNKNOWN_ERROR"); + } + return; + } + + // ʵ üũ ϰ üũ ³Ҷ Ѵ. + if (!opp_g->CanStartWar(GUILD_WAR_TYPE_FIELD)) + { + if (opp_g->GetLadderPoint() == 0) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ڶ ϴ.")); + else if (opp_g->GetMemberCount() < GUILD_WAR_MIN_MEMBER_COUNT) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> Ͽ ϴ.")); + return; + } + + do + { + if (g->GetMasterCharacter() != NULL) + break; + + CCI *pCCI = P2P_MANAGER::instance().FindByPID(g->GetMasterPID()); + + if (pCCI != NULL) + break; + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ƴմϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + + } while (false); + + do + { + if (opp_g->GetMasterCharacter() != NULL) + break; + + CCI *pCCI = P2P_MANAGER::instance().FindByPID(opp_g->GetMasterPID()); + + if (pCCI != NULL) + break; + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ƴմϴ.")); + g->RequestRefuseWar(opp_g->GetID()); + return; + + } while (false); + + g->RequestDeclareWar(opp_g->GetID(), type); +} + +ACMD(do_nowar) +{ + CGuild* g = ch->GetGuild(); + if (!g) + return; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD gm_pid = g->GetMasterPID(); + + if (gm_pid != ch->GetPlayerID()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ϴ.")); + return; + } + + CGuild* opp_g = CGuildManager::instance().FindGuildByName(arg1); + + if (!opp_g) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<> ׷ 尡 ϴ.")); + return; + } + + g->RequestRefuseWar(opp_g->GetID()); +} + +ACMD(do_detaillog) +{ + ch->DetailLog(); +} + +ACMD(do_monsterlog) +{ + ch->ToggleMonsterLog(); +} + +ACMD(do_pkmode) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + BYTE mode = 0; + str_to_number(mode, arg1); + + if (mode == PK_MODE_PROTECT) + return; + + if (ch->GetLevel() < PK_PROTECT_LEVEL && mode != 0) + return; + + ch->SetPKMode(mode); +} + +ACMD(do_messenger_auth) +{ + if (ch->GetArena()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + char answer = LOWER(*arg1); + + if (answer != 'y') + { + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg2); + + if (tch) + tch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ģ ź ߽ϴ."), ch->GetName()); + } + + MessengerManager::instance().AuthToAdd(ch->GetName(), arg2, answer == 'y' ? false : true); // DENY +} + +ACMD(do_setblockmode) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + BYTE flag = 0; + str_to_number(flag, arg1); + ch->SetBlockMode(flag); + } +} + +ACMD(do_unmount) +{ + if (true == ch->UnEquipSpecialRideUniqueItem()) + { + ch->RemoveAffect(AFFECT_MOUNT); + ch->RemoveAffect(AFFECT_MOUNT_BONUS); + + if (ch->IsHorseRiding()) + { + ch->StopRiding(); + } + } + else + { + ch->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("κ丮 ϴ.")); + } + +} + +ACMD(do_observer_exit) +{ + if (ch->IsObserverMode()) + { + if (ch->GetWarMap()) + ch->SetWarMap(NULL); + + if (ch->GetArena() != NULL || ch->GetArenaObserverMode() == true) + { + ch->SetArenaObserverMode(false); + + if (ch->GetArena() != NULL) + ch->GetArena()->RemoveObserver(ch->GetPlayerID()); + + ch->SetArena(NULL); + ch->WarpSet(ARENA_RETURN_POINT_X(ch->GetEmpire()), ARENA_RETURN_POINT_Y(ch->GetEmpire())); + } + else + { + ch->ExitToSavedLocation(); + } + ch->SetObserverMode(false); + } +} + +ACMD(do_view_equip) +{ + if (ch->GetGMLevel() <= GM_PLAYER) + return; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (!tch) + return; + + if (!tch->IsPC()) + return; + /* + int iSPCost = ch->GetMaxSP() / 3; + + if (ch->GetSP() < iSPCost) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŷ Ͽ ٸ ϴ.")); + return; + } + ch->PointChange(POINT_SP, -iSPCost); + */ + tch->SendEquipment(ch); + } +} + +ACMD(do_party_request) +{ + if (ch->GetArena()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("忡 Ͻ ϴ.")); + return; + } + + if (ch->GetParty()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("̹ Ƽ Ƿ Խû ϴ.")); + return; + } + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + if (!ch->RequestToParty(tch)) + ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied"); +} + +ACMD(do_party_request_accept) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + ch->AcceptToParty(tch); +} + +ACMD(do_party_request_deny) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + DWORD vid = 0; + str_to_number(vid, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().Find(vid); + + if (tch) + ch->DenyToParty(tch); +} + +ACMD(do_monarch_warpto) +{ + if (true == LC_IsYMIR() || true == LC_IsKorea()) + return; + + if (!CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + return; + } + + // Ÿ ˻ + if (!ch->IsMCOK(CHARACTER::MI_WARP)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_WARP)); + return; + } + + // ȯ + const int WarpPrice = 10000; + + // ˻ + if (!CMonarch::instance().IsMoneyOk(WarpPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, WarpPrice); + return; + } + + int x = 0, y = 0; + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(": warpto ")); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bEmpire != ch->GetEmpire()) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("Ÿ Դ ̵Ҽ ϴ")); + return; + } + + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ش %d äο ֽϴ. ( ä %d)"), pkCCI->bChannel, g_bChannel); + return; + } + if (!IsMonarchWarpZone(pkCCI->lMapIndex)) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + + PIXEL_POSITION pos; + + if (!SECTREE_MANAGER::instance().GetCenterPositionOfMap(pkCCI->lMapIndex, pos)) + ch->ChatPacket(CHAT_TYPE_INFO, "Cannot find map (index %d)", pkCCI->lMapIndex); + else + { + //ch->ChatPacket(CHAT_TYPE_INFO, "You warp to (%d, %d)", pos.x, pos.y); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Է ̵մϴ"), arg1); + ch->WarpSet(pos.x, pos.y); + + // 谨 + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + //Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_WARP); + } + } + else if (NULL == CHARACTER_MANAGER::instance().FindPC(arg1)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no one by that name"); + } + + return; + } + else + { + if (tch->GetEmpire() != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Ÿ Դ ̵Ҽ ϴ")); + return; + } + if (!IsMonarchWarpZone(tch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + x = tch->GetX(); + y = tch->GetY(); + } + + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s Է ̵մϴ"), arg1); + ch->WarpSet(x, y); + ch->Stop(); + + // 谨 + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + + //Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_WARP); +} + +ACMD(do_monarch_transfer) +{ + if (true == LC_IsYMIR() || true == LC_IsKorea()) + return; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(": transfer ")); + return; + } + + if (!CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + return; + } + + // Ÿ ˻ + if (!ch->IsMCOK(CHARACTER::MI_TRANSFER)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_TRANSFER)); + return; + } + + // + const int WarpPrice = 10000; + + // ˻ + if (!CMonarch::instance().IsMoneyOk(WarpPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, WarpPrice); + return; + } + + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bEmpire != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ȯ ϴ.")); + return; + } + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s %d äο Դϴ. ( ä: %d)"), arg1, pkCCI->bChannel, g_bChannel); + return; + } + if (!IsMonarchWarpZone(pkCCI->lMapIndex)) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + if (!IsMonarchWarpZone(ch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ȯ ϴ.")); + return; + } + + TPacketGGTransfer pgg; + + pgg.bHeader = HEADER_GG_TRANSFER; + strlcpy(pgg.szName, arg1, sizeof(pgg.szName)); + pgg.lX = ch->GetX(); + pgg.lY = ch->GetY(); + + P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGTransfer)); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȯϿϴ."), arg1); + + // 谨 + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + //Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_TRANSFER); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ԷϽ ̸ ڰ ϴ.")); + } + + return; + } + + + if (ch == tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڽ ȯ ϴ.")); + return; + } + + if (tch->GetEmpire() != ch->GetEmpire()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ٸ ȯ ϴ.")); + return; + } + if (!IsMonarchWarpZone(tch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ̵ ϴ.")); + return; + } + if (!IsMonarchWarpZone(ch->GetMapIndex())) + { + ch->ChatPacket (CHAT_TYPE_INFO, LC_TEXT("ش ȯ ϴ.")); + return; + } + + //tch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ()); + tch->WarpSet(ch->GetX(), ch->GetY(), ch->GetMapIndex()); + + // 谨 + CMonarch::instance().SendtoDBDecMoney(WarpPrice, ch->GetEmpire(), ch); + //Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_TRANSFER); +} + +ACMD(do_monarch_info) +{ + if (CMonarch::instance().IsMonarch(ch->GetPlayerID(), ch->GetEmpire())) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ")); + TMonarchInfo * p = CMonarch::instance().GetMonarch(); + for (int n = 1; n < 4; ++n) + { + if (n == ch->GetEmpire()) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s ݾ %lld "), EMPIRE_NAME(n), p->name[n], p->money[n]); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s "), EMPIRE_NAME(n), p->name[n]); + + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ")); + TMonarchInfo * p = CMonarch::instance().GetMonarch(); + for (int n = 1; n < 4; ++n) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[%s] : %s "), EMPIRE_NAME(n), p->name[n]); + + } + } + +} + +ACMD(do_elect) +{ + db_clientdesc->DBPacketHeader(HEADER_GD_COME_TO_VOTE, ch->GetDesc()->GetHandle(), 0); +} + +// LUA_ADD_GOTO_INFO +struct GotoInfo +{ + std::string st_name; + + BYTE empire; + int mapIndex; + DWORD x, y; + + GotoInfo() + { + st_name = ""; + empire = 0; + mapIndex = 0; + + x = 0; + y = 0; + } + + GotoInfo(const GotoInfo& c_src) + { + __copy__(c_src); + } + + void operator = (const GotoInfo& c_src) + { + __copy__(c_src); + } + + void __copy__(const GotoInfo& c_src) + { + st_name = c_src.st_name; + empire = c_src.empire; + mapIndex = c_src.mapIndex; + + x = c_src.x; + y = c_src.y; + } +}; + +extern void BroadcastNotice(const char * c_pszBuf); + +ACMD(do_monarch_tax) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: monarch_tax <1-50>"); + return; + } + + // ˻ + if (!ch->IsMonarch()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Ҽ ִ Դϴ")); + return; + } + + // ݼ + int tax = 0; + str_to_number(tax, arg1); + + if (tax < 1 || tax > 50) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("1-50 ġ ּ")); + + quest::CQuestManager::instance().SetEventFlag("trade_tax", tax); + + // ֿ ޼ ϳ + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" %d % Ǿϴ")); + + // + char szMsg[1024]; + + snprintf(szMsg, sizeof(szMsg), " %d %% Ǿϴ", tax); + BroadcastNotice(szMsg); + + snprintf(szMsg, sizeof(szMsg), "δ ŷ ݾ %d %% Ե˴ϴ.", tax); + BroadcastNotice(szMsg); + + // Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_TAX); +} + +static const DWORD cs_dwMonarchMobVnums[] = +{ + 191, // ߽ + 192, // + 193, // + 194, // ȣ + 391, // + 392, // + 393, // + 394, // + 491, // ȯ + 492, // + 493, // + 494, // + 591, // ܴ + 691, // + 791, // б + 1304, // + 1901, // ȣ + 2091, // հŹ + 2191, // Ŵ縷ź + 2206, // ȭi + 0, +}; + +ACMD(do_monarch_mob) +{ + char arg1[256]; + LPCHARACTER tch; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!ch->IsMonarch()) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Ҽ ִ Դϴ")); + return; + } + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mmob "); + return; + } + + BYTE pcEmpire = ch->GetEmpire(); + BYTE mapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(ch->GetMapIndex()); + + if (LC_IsYMIR() == true || LC_IsKorea() == true) + { + if (mapEmpire != pcEmpire && mapEmpire != 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ڱ 信 ִ Դϴ")); + return; + } + } + + // ȯ + const int SummonPrice = 5000000; + + // Ÿ ˻ + if (!ch->IsMCOK(CHARACTER::MI_SUMMON)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d ʰ Ÿ Դϴ."), ch->GetMCLTime(CHARACTER::MI_SUMMON)); + return; + } + + // ˻ + if (!CMonarch::instance().IsMoneyOk(SummonPrice, ch->GetEmpire())) + { + int NationMoney = CMonarch::instance().GetMoney(ch->GetEmpire()); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" մϴ. : %u ʿݾ : %u"), NationMoney, SummonPrice); + return; + } + + const CMob * pkMob; + DWORD vnum = 0; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + DWORD count; + + // ȯ ˻ + for (count = 0; cs_dwMonarchMobVnums[count] != 0; ++count) + if (cs_dwMonarchMobVnums[count] == vnum) + break; + + if (0 == cs_dwMonarchMobVnums[count]) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ȯҼ Դϴ. ȯ ʹ Ȩ ϼ")); + return; + } + + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE, + true); + + if (tch) + { + // 谨 + CMonarch::instance().SendtoDBDecMoney(SummonPrice, ch->GetEmpire(), ch); + + // Ÿ ʱȭ + ch->SetMC(CHARACTER::MI_SUMMON); + } +} + +static const char* FN_point_string(int apply_number) +{ + switch (apply_number) + { + case POINT_MAX_HP: return LC_TEXT("ִ +%d"); + case POINT_MAX_SP: return LC_TEXT("ִ ŷ +%d"); + case POINT_HT: return LC_TEXT("ü +%d"); + case POINT_IQ: return LC_TEXT(" +%d"); + case POINT_ST: return LC_TEXT("ٷ +%d"); + case POINT_DX: return LC_TEXT("ø +%d"); + case POINT_ATT_SPEED: return LC_TEXT("ݼӵ +%d"); + case POINT_MOV_SPEED: return LC_TEXT("̵ӵ %d"); + case POINT_CASTING_SPEED: return LC_TEXT("Ÿ -%d"); + case POINT_HP_REGEN: return LC_TEXT(" ȸ +%d"); + case POINT_SP_REGEN: return LC_TEXT("ŷ ȸ +%d"); + case POINT_POISON_PCT: return LC_TEXT(" %d"); + case POINT_STUN_PCT: return LC_TEXT(" +%d"); + case POINT_SLOW_PCT: return LC_TEXT("ο +%d"); + case POINT_CRITICAL_PCT: return LC_TEXT("%d%% Ȯ ġŸ "); + case POINT_RESIST_CRITICAL: return LC_TEXT(" ġŸ Ȯ %d%% "); + case POINT_PENETRATE_PCT: return LC_TEXT("%d%% Ȯ "); + case POINT_RESIST_PENETRATE: return LC_TEXT(" Ȯ %d%% "); + case POINT_ATTBONUS_HUMAN: return LC_TEXT("ΰ Ÿġ +%d%%"); + case POINT_ATTBONUS_ANIMAL: return LC_TEXT(" Ÿġ +%d%%"); + case POINT_ATTBONUS_ORC: return LC_TEXT(" Ÿġ +%d%%"); + case POINT_ATTBONUS_MILGYO: return LC_TEXT("б Ÿġ +%d%%"); + case POINT_ATTBONUS_UNDEAD: return LC_TEXT("ü Ÿġ +%d%%"); + case POINT_ATTBONUS_DEVIL: return LC_TEXT("Ǹ Ÿġ +%d%%"); + case POINT_STEAL_HP: return LC_TEXT("Ÿġ %d%% "); + case POINT_STEAL_SP: return LC_TEXT("Ÿġ %d%% ŷ "); + case POINT_MANA_BURN_PCT: return LC_TEXT("%d%% Ȯ Ÿݽ ŷ Ҹ"); + case POINT_DAMAGE_SP_RECOVER: return LC_TEXT("%d%% Ȯ ؽ ŷ ȸ"); + case POINT_BLOCK: return LC_TEXT("Ÿݽ Ȯ %d%%"); + case POINT_DODGE: return LC_TEXT("Ȱ ȸ Ȯ %d%%"); + case POINT_RESIST_SWORD: return LC_TEXT("Ѽհ %d%%"); + case POINT_RESIST_TWOHAND: return LC_TEXT("հ %d%%"); + case POINT_RESIST_DAGGER: return LC_TEXT("μհ %d%%"); + case POINT_RESIST_BELL: return LC_TEXT(" %d%%"); + case POINT_RESIST_FAN: return LC_TEXT("ä %d%%"); + case POINT_RESIST_BOW: return LC_TEXT("Ȱ %d%%"); + case POINT_RESIST_FIRE: return LC_TEXT("ȭ %d%%"); + case POINT_RESIST_ELEC: return LC_TEXT(" %d%%"); + case POINT_RESIST_MAGIC: return LC_TEXT(" %d%%"); + case POINT_RESIST_WIND: return LC_TEXT("ٶ %d%%"); + case POINT_RESIST_ICE: return LC_TEXT("ñ %d%%"); + case POINT_RESIST_EARTH: return LC_TEXT(" %d%%"); + case POINT_RESIST_DARK: return LC_TEXT(" %d%%"); + case POINT_REFLECT_MELEE: return LC_TEXT(" Ÿġ ݻ Ȯ : %d%%"); + case POINT_REFLECT_CURSE: return LC_TEXT(" ǵ Ȯ %d%%"); + case POINT_POISON_REDUCE: return LC_TEXT(" %d%%"); + case POINT_KILL_SP_RECOVER: return LC_TEXT("%d%% Ȯ ġ ŷ ȸ"); + case POINT_EXP_DOUBLE_BONUS: return LC_TEXT("%d%% Ȯ ġ ġ ߰ "); + case POINT_GOLD_DOUBLE_BONUS: return LC_TEXT("%d%% Ȯ ġ 2 "); + case POINT_ITEM_DROP_BONUS: return LC_TEXT("%d%% Ȯ ġ 2 "); + case POINT_POTION_BONUS: return LC_TEXT(" %d%% "); + case POINT_KILL_HP_RECOVERY: return LC_TEXT("%d%% Ȯ ġ ȸ"); +// case POINT_IMMUNE_STUN: return LC_TEXT(" %d%%"); +// case POINT_IMMUNE_SLOW: return LC_TEXT(" %d%%"); +// case POINT_IMMUNE_FALL: return LC_TEXT("Ѿ %d%%"); +// case POINT_SKILL: return LC_TEXT(""); +// case POINT_BOW_DISTANCE: return LC_TEXT(""); + case POINT_ATT_GRADE_BONUS: return LC_TEXT("ݷ +%d"); + case POINT_DEF_GRADE_BONUS: return LC_TEXT(" +%d"); + case POINT_MAGIC_ATT_GRADE: return LC_TEXT(" ݷ +%d"); + case POINT_MAGIC_DEF_GRADE: return LC_TEXT(" +%d"); +// case POINT_CURSE_PCT: return LC_TEXT(""); + case POINT_MAX_STAMINA: return LC_TEXT("ִ +%d"); + case POINT_ATTBONUS_WARRIOR: return LC_TEXT("翡 +%d%%"); + case POINT_ATTBONUS_ASSASSIN: return LC_TEXT("ڰ +%d%%"); + case POINT_ATTBONUS_SURA: return LC_TEXT("󿡰 +%d%%"); + case POINT_ATTBONUS_SHAMAN: return LC_TEXT("翡 +%d%%"); + case POINT_ATTBONUS_MONSTER: return LC_TEXT("Ϳ +%d%%"); + case POINT_MALL_ATTBONUS: return LC_TEXT("ݷ +%d%%"); + case POINT_MALL_DEFBONUS: return LC_TEXT(" +%d%%"); + case POINT_MALL_EXPBONUS: return LC_TEXT("ġ %d%%"); + case POINT_MALL_ITEMBONUS: return LC_TEXT(" %.1f"); + case POINT_MALL_GOLDBONUS: return LC_TEXT(" %.1f"); + case POINT_MAX_HP_PCT: return LC_TEXT("ִ +%d%%"); + case POINT_MAX_SP_PCT: return LC_TEXT("ִ ŷ +%d%%"); + case POINT_SKILL_DAMAGE_BONUS: return LC_TEXT("ų %d%%"); + case POINT_NORMAL_HIT_DAMAGE_BONUS: return LC_TEXT("Ÿ %d%%"); + case POINT_SKILL_DEFEND_BONUS: return LC_TEXT("ų %d%%"); + case POINT_NORMAL_HIT_DEFEND_BONUS: return LC_TEXT("Ÿ %d%%"); +// case POINT_PC_BANG_EXP_BONUS: return LC_TEXT(""); +// case POINT_PC_BANG_DROP_BONUS: return LC_TEXT(""); +// case POINT_EXTRACT_HP_PCT: return LC_TEXT(""); + case POINT_RESIST_WARRIOR: return LC_TEXT("ݿ %d%% "); + case POINT_RESIST_ASSASSIN: return LC_TEXT("ڰݿ %d%% "); + case POINT_RESIST_SURA: return LC_TEXT("ݿ %d%% "); + case POINT_RESIST_SHAMAN: return LC_TEXT("ݿ %d%% "); + default: return NULL; + } +} + +static bool FN_hair_affect_string(LPCHARACTER ch, char *buf, size_t bufsiz) +{ + if (NULL == ch || NULL == buf) + return false; + + CAffect* aff = NULL; + time_t expire = 0; + struct tm ltm; + int year, mon, day; + int offset = 0; + + aff = ch->FindAffect(AFFECT_HAIR); + + if (NULL == aff) + return false; + + expire = ch->GetQuestFlag("hair.limit_time"); + + if (expire < get_global_time()) + return false; + + // set apply string + offset = snprintf(buf, bufsiz, FN_point_string(aff->bApplyOn), aff->lApplyValue); + + if (offset < 0 || offset >= (int) bufsiz) + offset = bufsiz - 1; + + localtime_r(&expire, <m); + + year = ltm.tm_year + 1900; + mon = ltm.tm_mon + 1; + day = ltm.tm_mday; + + snprintf(buf + offset, bufsiz - offset, LC_TEXT(" ( : %d %d %d)"), year, mon, day); + + return true; +} + +ACMD(do_costume) +{ + char buf[512]; + const size_t bufferSize = sizeof(buf); + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + CItem* pBody = ch->GetWear(WEAR_COSTUME_BODY); + CItem* pHair = ch->GetWear(WEAR_COSTUME_HAIR); + + ch->ChatPacket(CHAT_TYPE_INFO, "COSTUME status:"); + + if (pHair) + { + const char* itemName = pHair->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " HAIR : %s", itemName); + + for (int i = 0; i < pHair->GetAttributeCount(); ++i) + { + const TPlayerItemAttribute& attr = pHair->GetAttribute(i); + if (0 < attr.bType) + { + snprintf(buf, bufferSize, FN_point_string(attr.bType), attr.sValue); + ch->ChatPacket(CHAT_TYPE_INFO, " %s", buf); + } + } + + if (pHair->IsEquipped() && arg1[0] == 'h') + ch->UnequipItem(pHair); + } + + if (pBody) + { + const char* itemName = pBody->GetName(); + ch->ChatPacket(CHAT_TYPE_INFO, " BODY : %s", itemName); + + if (pBody->IsEquipped() && arg1[0] == 'b') + ch->UnequipItem(pBody); + } +} + +ACMD(do_hair) +{ + char buf[256]; + + if (false == FN_hair_affect_string(ch, buf, sizeof(buf))) + return; + + ch->ChatPacket(CHAT_TYPE_INFO, buf); +} + +ACMD(do_inventory) +{ + int index = 0; + int count = 1; + + char arg1[256]; + char arg2[256]; + + LPITEM item; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: inventory "); + return; + } + + if (!*arg2) + { + index = 0; + str_to_number(count, arg1); + } + else + { + str_to_number(index, arg1); index = MIN(index, INVENTORY_MAX_NUM); + str_to_number(count, arg2); count = MIN(count, INVENTORY_MAX_NUM); + } + + for (int i = 0; i < count; ++i) + { + if (index >= INVENTORY_MAX_NUM) + break; + + item = ch->GetInventoryItem(index); + + ch->ChatPacket(CHAT_TYPE_INFO, "inventory [%d] = %s", + index, item ? item->GetName() : ""); + ++index; + } +} + +//gift notify quest command +ACMD(do_gift) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "gift"); +} + +ACMD(do_cube) +{ + if (!ch->CanDoCube()) + return; + + dev_log(LOG_DEB0, "CUBE COMMAND <%s>: %s", ch->GetName(), argument); + int cube_index = 0, inven_index = 0; + const char *line; + + char arg1[256], arg2[256], arg3[256]; + + line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(line, arg3, sizeof(arg3)); + + if (0 == arg1[0]) + { + // print usage + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: cube open"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube close"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube add "); + ch->ChatPacket(CHAT_TYPE_INFO, " cube delete "); + ch->ChatPacket(CHAT_TYPE_INFO, " cube list"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube cancel"); + ch->ChatPacket(CHAT_TYPE_INFO, " cube make [all]"); + return; + } + + const std::string& strArg1 = std::string(arg1); + + // r_info (request information) + // /cube r_info ==> (Client -> Server) NPC ִ û + // (Server -> Client) /cube r_list npcVNUM resultCOUNT 123,1/125,1/128,1/130,5 + // + // /cube r_info 3 ==> (Client -> Server) NPC ִ 3° ʿ û + // /cube r_info 3 5 ==> (Client -> Server) NPC ִ 3° ۺ 5 ʿ û + // (Server -> Client) /cube m_info startIndex count 125,1|126,2|127,2|123,5&555,5&555,4/120000@125,1|126,2|127,2|123,5&555,5&555,4/120000 + // + if (strArg1 == "r_info") + { + if (0 == arg2[0]) + Cube_request_result_list(ch); + else + { + if (isdigit(*arg2)) + { + int listIndex = 0, requestCount = 1; + str_to_number(listIndex, arg2); + + if (0 != arg3[0] && isdigit(*arg3)) + str_to_number(requestCount, arg3); + + Cube_request_material_info(ch, listIndex, requestCount); + } + } + + return; + } + + switch (LOWER(arg1[0])) + { + case 'o': // open + Cube_open(ch); + break; + + case 'c': // close + Cube_close(ch); + break; + + case 'l': // list + Cube_show_list(ch); + break; + + case 'a': // add cue_index inven_index + { + if (0 == arg2[0] || !isdigit(*arg2) || + 0 == arg3[0] || !isdigit(*arg3)) + return; + + str_to_number(cube_index, arg2); + str_to_number(inven_index, arg3); + Cube_add_item (ch, cube_index, inven_index); + } + break; + + case 'd': // delete + { + if (0 == arg2[0] || !isdigit(*arg2)) + return; + + str_to_number(cube_index, arg2); + Cube_delete_item (ch, cube_index); + } + break; + + case 'm': // make + if (0 != arg2[0]) + { + while (true == Cube_make(ch)) + dev_log (LOG_DEB0, "cube make success"); + } + else + Cube_make(ch); + break; + + default: + return; + } +} + +ACMD(do_in_game_mall) +{ + if (LC_IsYMIR() == true || LC_IsKorea() == true) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://metin2.co.kr/04_mall/mall/login.htm"); + return; + } + + if (true == LC_IsTaiwan()) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://203.69.141.203/mall/mall/item_main.htm"); + return; + } + + // _ 赵 ۸ URL ϵڵ ߰ + if (true == LC_IsWE_Korea()) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://metin2.co.kr/50_we_mall/mall/login.htm"); + return; + } + + if (LC_IsJapan() == true) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://mt2.oge.jp/itemmall/itemList.php"); + return; + } + + if (LC_IsNewCIBN() == true && test_server) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://218.99.6.51/04_mall/mall/login.htm"); + return; + } + + if (LC_IsSingapore() == true) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://www.metin2.sg/ishop.php"); + return; + } + + /* + if (LC_IsCanada() == true) + { + ch->ChatPacket(CHAT_TYPE_COMMAND, "mall http://mall.z8games.com/mall_entry.aspx?tb=m2"); + return; + }*/ + + if (LC_IsEurope() == true) + { + char country_code[3]; + + switch (LC_GetLocalType()) + { + case LC_GERMANY: country_code[0] = 'd'; country_code[1] = 'e'; country_code[2] = '\0'; break; + case LC_FRANCE: country_code[0] = 'f'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_ITALY: country_code[0] = 'i'; country_code[1] = 't'; country_code[2] = '\0'; break; + case LC_SPAIN: country_code[0] = 'e'; country_code[1] = 's'; country_code[2] = '\0'; break; + case LC_UK: country_code[0] = 'e'; country_code[1] = 'n'; country_code[2] = '\0'; break; + case LC_TURKEY: country_code[0] = 't'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_POLAND: country_code[0] = 'p'; country_code[1] = 'l'; country_code[2] = '\0'; break; + case LC_PORTUGAL: country_code[0] = 'p'; country_code[1] = 't'; country_code[2] = '\0'; break; + case LC_GREEK: country_code[0] = 'g'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_RUSSIA: country_code[0] = 'r'; country_code[1] = 'u'; country_code[2] = '\0'; break; + case LC_DENMARK: country_code[0] = 'd'; country_code[1] = 'k'; country_code[2] = '\0'; break; + case LC_BULGARIA: country_code[0] = 'b'; country_code[1] = 'g'; country_code[2] = '\0'; break; + case LC_CROATIA: country_code[0] = 'h'; country_code[1] = 'r'; country_code[2] = '\0'; break; + case LC_MEXICO: country_code[0] = 'm'; country_code[1] = 'x'; country_code[2] = '\0'; break; + case LC_ARABIA: country_code[0] = 'a'; country_code[1] = 'e'; country_code[2] = '\0'; break; + case LC_CZECH: country_code[0] = 'c'; country_code[1] = 'z'; country_code[2] = '\0'; break; + case LC_ROMANIA: country_code[0] = 'r'; country_code[1] = 'o'; country_code[2] = '\0'; break; + case LC_HUNGARY: country_code[0] = 'h'; country_code[1] = 'u'; country_code[2] = '\0'; break; + case LC_NETHERLANDS: country_code[0] = 'n'; country_code[1] = 'l'; country_code[2] = '\0'; break; + case LC_USA: country_code[0] = 'u'; country_code[1] = 's'; country_code[2] = '\0'; break; + case LC_CANADA: country_code[0] = 'c'; country_code[1] = 'a'; country_code[2] = '\0'; break; + default: + if (test_server == true) + { + country_code[0] = 'd'; country_code[1] = 'e'; country_code[2] = '\0'; + } + break; + } + + char buf[512+1]; + char sas[33]; + MD5_CTX ctx; + const char sas_key[] = "GF9001"; + + snprintf(buf, sizeof(buf), "%u%u%s", ch->GetPlayerID(), ch->GetAID(), sas_key); + + MD5Init(&ctx); + MD5Update(&ctx, (const unsigned char *) buf, strlen(buf)); +#ifdef __FreeBSD__ + MD5End(&ctx, sas); +#else + static const char hex[] = "0123456789abcdef"; + unsigned char digest[16]; + MD5Final(digest, &ctx); + int i; + for (i = 0; i < 16; ++i) { + sas[i+i] = hex[digest[i] >> 4]; + sas[i+i+1] = hex[digest[i] & 0x0f]; + } + sas[i+i] = '\0'; +#endif + + snprintf(buf, sizeof(buf), "mall http://%s/ishop?pid=%u&c=%s&sid=%d&sas=%s", + g_strWebMallURL.c_str(), ch->GetPlayerID(), country_code, g_server_id, sas); + + ch->ChatPacket(CHAT_TYPE_COMMAND, buf); + } +} + +// ֻ +ACMD(do_dice) +{ + char arg1[256], arg2[256]; + int start = 1, end = 100; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (*arg1 && *arg2) + { + start = atoi(arg1); + end = atoi(arg2); + } + else if (*arg1 && !*arg2) + { + start = 1; + end = atoi(arg1); + } + + end = MAX(start, end); + start = MIN(start, end); + + int n = number(start, end); + + if (ch->GetParty()) + ch->GetParty()->ChatPacketToAllMember(CHAT_TYPE_INFO, LC_TEXT("%s ֻ %d Խϴ. (%d-%d)"), ch->GetName(), n, start, end); + else + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ֻ %d Խϴ. (%d-%d)"), n, start, end); +} + +ACMD(do_click_mall) +{ + ch->ChatPacket(CHAT_TYPE_COMMAND, "ShowMeMallPassword"); +} + +ACMD(do_ride) +{ + dev_log(LOG_DEB0, "[DO_RIDE] start"); + if (ch->IsDead() || ch->IsStun()) + return; + + // + { + if (ch->IsHorseRiding()) + { + dev_log(LOG_DEB0, "[DO_RIDE] stop riding"); + ch->StopRiding(); + return; + } + + if (ch->GetMountVnum()) + { + dev_log(LOG_DEB0, "[DO_RIDE] unmount"); + do_unmount(ch, NULL, 0, 0); + return; + } + } + + // Ÿ + { + if (ch->GetHorse() != NULL) + { + dev_log(LOG_DEB0, "[DO_RIDE] start riding"); + ch->StartRiding(); + return; + } + + for (BYTE i=0; iGetInventoryItem(i); + if (NULL == item) + continue; + + // ũ Ż + if (item->IsRideItem()) + { + if (NULL==ch->GetWear(WEAR_UNIQUE1) || NULL==ch->GetWear(WEAR_UNIQUE2)) + { + dev_log(LOG_DEB0, "[DO_RIDE] USE UNIQUE ITEM"); + //ch->EquipItem(item); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + } + + // Ϲ Ż + // TODO : ŻͿ SubType ߰ + switch (item->GetVnum()) + { + case 71114: // ̿ + case 71116: // ߽̿ + case 71118: // ̿ + case 71120: // ڿ̿ + dev_log(LOG_DEB0, "[DO_RIDE] USE QUEST ITEM"); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + + // GF mantis #113524, 52001~52090 Ż + if( (item->GetVnum() > 52000) && (item->GetVnum() < 52091) ) { + dev_log(LOG_DEB0, "[DO_RIDE] USE QUEST ITEM"); + ch->UseItem(TItemPos (INVENTORY, i)); + return; + } + } + } + + + // Ÿų + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ȯּ.")); +} + +#ifdef __AUCTION__ +// temp_auction +ACMD(do_get_item_id_list) +{ + for (int i = 0; i < INVENTORY_MAX_NUM; i++) + { + LPITEM item = ch->GetInventoryItem(i); + if (item != NULL) + ch->ChatPacket(CHAT_TYPE_INFO, "name : %s id : %d", item->GetProto()->szName, item->GetID()); + } +} + +// temp_auction + +ACMD(do_enroll_auction) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + char arg4[256]; + two_arguments (two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3), arg4, sizeof(arg4)); + + DWORD item_id = strtoul(arg1, NULL, 10); + BYTE empire = strtoul(arg2, NULL, 10); + int bidPrice = strtol(arg3, NULL, 10); + int immidiatePurchasePrice = strtol(arg4, NULL, 10); + + LPITEM item = ITEM_MANAGER::instance().Find(item_id); + if (item == NULL) + return; + + AuctionManager::instance().enroll_auction(ch, item, empire, bidPrice, immidiatePurchasePrice); +} + +ACMD(do_enroll_wish) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + one_argument (two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3)); + + DWORD item_num = strtoul(arg1, NULL, 10); + BYTE empire = strtoul(arg2, NULL, 10); + int wishPrice = strtol(arg3, NULL, 10); + + AuctionManager::instance().enroll_wish(ch, item_num, empire, wishPrice); +} + +ACMD(do_enroll_sale) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + one_argument (two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3)); + + DWORD item_id = strtoul(arg1, NULL, 10); + DWORD wisher_id = strtoul(arg2, NULL, 10); + int salePrice = strtol(arg3, NULL, 10); + + LPITEM item = ITEM_MANAGER::instance().Find(item_id); + if (item == NULL) + return; + + AuctionManager::instance().enroll_sale(ch, item, wisher_id, salePrice); +} + +// temp_auction +// packet ϰ ϰ, ̰ ؾѴ. +ACMD(do_get_auction_list) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + two_arguments (one_argument (argument, arg1, sizeof(arg1)), arg2, sizeof(arg2), arg3, sizeof(arg3)); + + AuctionManager::instance().get_auction_list (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10), strtoul(arg3, NULL, 10)); +} +// +//ACMD(do_get_wish_list) +//{ +// char arg1[256]; +// char arg2[256]; +// char arg3[256]; +// two_arguments (one_argument (argument, arg1, sizeof(arg1)), arg2, sizeof(arg2), arg3, sizeof(arg3)); +// +// AuctionManager::instance().get_wish_list (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10), strtoul(arg3, NULL, 10)); +//} +ACMD (do_get_my_auction_list) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().get_my_auction_list (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_get_my_purchase_list) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().get_my_purchase_list (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_auction_bid) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().bid (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_auction_impur) +{ + char arg1[256]; + one_argument (argument, arg1, sizeof(arg1)); + + AuctionManager::instance().immediate_purchase (ch, strtoul(arg1, NULL, 10)); +} + +ACMD (do_get_auctioned_item) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().get_auctioned_item (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_buy_sold_item) +{ + char arg1[256]; + char arg2[256]; + one_argument (argument, arg1, sizeof(arg1)); + + AuctionManager::instance().get_auctioned_item (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_cancel_auction) +{ + char arg1[256]; + one_argument (argument, arg1, sizeof(arg1)); + + AuctionManager::instance().cancel_auction (ch, strtoul(arg1, NULL, 10)); +} + +ACMD (do_cancel_wish) +{ + char arg1[256]; + one_argument (argument, arg1, sizeof(arg1)); + + AuctionManager::instance().cancel_wish (ch, strtoul(arg1, NULL, 10)); +} + +ACMD (do_cancel_sale) +{ + char arg1[256]; + one_argument (argument, arg1, sizeof(arg1)); + + AuctionManager::instance().cancel_sale (ch, strtoul(arg1, NULL, 10)); +} + +ACMD (do_rebid) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().rebid (ch, strtoul(arg1, NULL, 10), strtoul(arg2, NULL, 10)); +} + +ACMD (do_bid_cancel) +{ + char arg1[256]; + char arg2[256]; + two_arguments (argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + AuctionManager::instance().bid_cancel (ch, strtoul(arg1, NULL, 10)); +} +#endif diff --git a/game/src/cmd_gm.cpp b/game/src/cmd_gm.cpp new file mode 100644 index 0000000..ad22c34 --- /dev/null +++ b/game/src/cmd_gm.cpp @@ -0,0 +1,4411 @@ +#include "stdafx.h" +#include "utils.h" +#include "config.h" +#include "desc_client.h" +#include "desc_manager.h" +#include "char.h" +#include "char_manager.h" +#include "item_manager.h" +#include "sectree_manager.h" +#include "mob_manager.h" +#include "packet.h" +#include "cmd.h" +#include "regen.h" +#include "guild.h" +#include "guild_manager.h" +#include "p2p.h" +#include "buffer_manager.h" +#include "fishing.h" +#include "mining.h" +#include "questmanager.h" +#include "vector.h" +#include "affect.h" +#include "db.h" +#include "priv_manager.h" +#include "building.h" +#include "battle.h" +#include "arena.h" +#include "start_position.h" +#include "party.h" +#include "monarch.h" +#include "castle.h" +#include "BattleArena.h" +#include "xmas_event.h" +#include "log.h" +#include "pcbang.h" +#include "threeway_war.h" +#include "unique_item.h" +#include "DragonSoul.h" + +extern bool DropEvent_RefineBox_SetValue(const std::string& name, int value); + +// ADD_COMMAND_SLOW_STUN +enum +{ + COMMANDAFFECT_STUN, + COMMANDAFFECT_SLOW, +}; + +void Command_ApplyAffect(LPCHARACTER ch, const char* argument, const char* affectName, int cmdAffect) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + sys_log(0, arg1); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: %s ", affectName); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s is not in same map", arg1); + return; + } + + switch (cmdAffect) + { + case COMMANDAFFECT_STUN: + SkillAttackAffect(tch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, 30, "GM_STUN"); + break; + case COMMANDAFFECT_SLOW: + SkillAttackAffect(tch, 1000, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 30, "GM_SLOW"); + break; + } + + sys_log(0, "%s %s", arg1, affectName); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s %s", arg1, affectName); +} +// END_OF_ADD_COMMAND_SLOW_STUN + +ACMD(do_pcbang_update) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + unsigned long PCBangID = 0; + + if (*arg1 == '\0') + PCBangID = 0; + else + str_to_number(PCBangID, arg1); + + if (PCBangID == 0) + { + CPCBangManager::instance().RequestUpdateIPList(0); + ch->ChatPacket(CHAT_TYPE_INFO, "PCBang Info Update For All"); + } + else + { + CPCBangManager::instance().RequestUpdateIPList(PCBangID); + ch->ChatPacket(CHAT_TYPE_INFO, "PCBang Info Update For %u", PCBangID); + } + + TPacketPCBangUpdate packet; + packet.bHeader = HEADER_GG_PCBANG_UPDATE; + packet.ulPCBangID = PCBangID; + + P2P_MANAGER::instance().Send(&packet, sizeof(TPacketPCBangUpdate)); + +} + +ACMD(do_pcbang_check) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (CPCBangManager::instance().IsPCBangIP(arg1) == true) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s is a PCBang IP", arg1); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s is not a PCBang IP", arg1); + } +} + +ACMD(do_stun) +{ + Command_ApplyAffect(ch, argument, "stun", COMMANDAFFECT_STUN); +} + +ACMD(do_slow) +{ + Command_ApplyAffect(ch, argument, "slow", COMMANDAFFECT_SLOW); +} + +ACMD(do_transfer) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: transfer "); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (pkCCI) + { + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Target is in %d channel (my channel %d)", pkCCI->bChannel, g_bChannel); + return; + } + + TPacketGGTransfer pgg; + + pgg.bHeader = HEADER_GG_TRANSFER; + strlcpy(pgg.szName, arg1, sizeof(pgg.szName)); + pgg.lX = ch->GetX(); + pgg.lY = ch->GetY(); + + P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGTransfer)); + ch->ChatPacket(CHAT_TYPE_INFO, "Transfer requested."); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no character(%s) by that name", arg1); + sys_log(0, "There is no character(%s) by that name", arg1); + } + + return; + } + + if (ch == tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Transfer me?!?"); + return; + } + + //tch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ()); + tch->WarpSet(ch->GetX(), ch->GetY(), ch->GetMapIndex()); +} + +// LUA_ADD_GOTO_INFO +struct GotoInfo +{ + std::string st_name; + + BYTE empire; + int mapIndex; + DWORD x, y; + + GotoInfo() + { + st_name = ""; + empire = 0; + mapIndex = 0; + + x = 0; + y = 0; + } + GotoInfo(const GotoInfo& c_src) + { + __copy__(c_src); + } + void operator = (const GotoInfo& c_src) + { + __copy__(c_src); + } + void __copy__(const GotoInfo& c_src) + { + st_name = c_src.st_name; + empire = c_src.empire; + mapIndex = c_src.mapIndex; + + x = c_src.x; + y = c_src.y; + } +}; + +static std::vector gs_vec_gotoInfo; + +void CHARACTER_AddGotoInfo(const std::string& c_st_name, BYTE empire, int mapIndex, DWORD x, DWORD y) +{ + GotoInfo newGotoInfo; + newGotoInfo.st_name = c_st_name; + newGotoInfo.empire = empire; + newGotoInfo.mapIndex = mapIndex; + newGotoInfo.x = x; + newGotoInfo.y = y; + gs_vec_gotoInfo.push_back(newGotoInfo); + + sys_log(0, "AddGotoInfo(name=%s, empire=%d, mapIndex=%d, pos=(%d, %d))", c_st_name.c_str(), empire, mapIndex, x, y); +} + +bool FindInString(const char * c_pszFind, const char * c_pszIn) +{ + const char * c = c_pszIn; + const char * p; + + p = strchr(c, '|'); + + if (!p) + return (0 == strncasecmp(c_pszFind, c_pszIn, strlen(c_pszFind))); + else + { + char sz[64 + 1]; + + do + { + strlcpy(sz, c, MIN(sizeof(sz), (p - c) + 1)); + + if (!strncasecmp(c_pszFind, sz, strlen(c_pszFind))) + return true; + + c = p + 1; + } while ((p = strchr(c, '|'))); + + strlcpy(sz, c, sizeof(sz)); + + if (!strncasecmp(c_pszFind, sz, strlen(c_pszFind))) + return true; + } + + return false; +} + +bool CHARACTER_GoToName(LPCHARACTER ch, BYTE empire, int mapIndex, const char* gotoName) +{ + std::vector::iterator i; + for (i = gs_vec_gotoInfo.begin(); i != gs_vec_gotoInfo.end(); ++i) + { + const GotoInfo& c_eachGotoInfo = *i; + + if (mapIndex != 0) + { + if (mapIndex != c_eachGotoInfo.mapIndex) + continue; + } + else if (!FindInString(gotoName, c_eachGotoInfo.st_name.c_str())) + continue; + + if (c_eachGotoInfo.empire == 0 || c_eachGotoInfo.empire == empire) + { + int x = c_eachGotoInfo.x * 100; + int y = c_eachGotoInfo.y * 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); + ch->Stop(); + return true; + } + } + return false; +} + +// END_OF_LUA_ADD_GOTO_INFO + +/* + = { + { "A1|", 0, 1, 4693, 9642 }, + { "A3|ھ", 0, 3, 3608, 8776 }, + + { "B1|", 0, 21, 557, 1579 }, + { "B3|", 0, 23, 1385, 2349 }, + + { "C1|", 0, 41, 9696, 2784 }, + { "C3|ڶ", 0, 43, 8731, 2426 }, + +// Snow +{ "Snow|ѻ", 1, 61, 4342, 2906 }, +{ "Snow|ѻ", 2, 61, 3752, 1749 }, +{ "Snow|ѻ", 3, 61, 4918, 1736 }, + +// Flame +{ "Flame|ȭ|ȭ", 1, 62, 5994, 7563 }, +{ "Flame|ȭ|ȭ", 2, 62, 5978, 6222 }, +{ "Flame|ȭ|ȭ", 3, 62, 7307, 6898 }, + +// Desert +{ "Desert|縷|縷", 1, 63, 2178, 6272 }, +{ "Desert|縷|縷", 2, 63, 2219, 5027 }, +{ "Desert|縷|縷", 3, 63, 3440, 5025 }, + +// Threeway +{ "Three|·", 1, 64, 4021, 6739 }, +{ "Three|·", 2, 64, 2704, 7399 }, +{ "Three|·", 3, 64, 3213, 8080 }, + +// б +{ "Milgyo|б", 1, 65, 5536, 1436 }, +{ "Milgyo|б", 2, 65, 5536, 1436 }, +{ "Milgyo|б", 3, 65, 5536, 1436 }, + +// ŸԱ +{ "ŸԱ", 1, 65, 5905, 1108 }, +{ "ŸԱ", 2, 65, 5905, 1108 }, +{ "ŸԱ", 3, 65, 5905, 1108 }, + +{ NULL, 0, 0, 0, 0 }, +}; + */ + + +ACMD(do_goto) +{ + char arg1[256], arg2[256]; + int x = 0, y = 0, z = 0; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 && !*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: goto "); + return; + } + + if (isnhdigit(*arg1) && isnhdigit(*arg2)) + { + str_to_number(x, arg1); + str_to_number(y, arg2); + + PIXEL_POSITION p; + + if (SECTREE_MANAGER::instance().GetMapBasePosition(ch->GetX(), ch->GetY(), p)) + { + x += p.x / 100; + y += p.y / 100; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "You goto ( %d, %d )", x, y); + } + else + { + int mapIndex = 0; + BYTE empire = 0; + + if (*arg1 == '#') + str_to_number(mapIndex, (arg1 + 1)); + + if (*arg2 && isnhdigit(*arg2)) + { + str_to_number(empire, arg2); + empire = MINMAX(1, empire, 3); + } + else + empire = ch->GetEmpire(); + + if (CHARACTER_GoToName(ch, empire, mapIndex, arg1)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Cannot find map command syntax: /goto [empire]"); + return; + } + + return; + + /* + int iMapIndex = 0; + for (int i = 0; aWarpInfo[i].c_pszName != NULL; ++i) + { + if (iMapIndex != 0) + { + if (iMapIndex != aWarpInfo[i].iMapIndex) + continue; + } + else if (!FindInString(arg1, aWarpInfo[i].c_pszName)) + continue; + + if (aWarpInfo[i].bEmpire == 0 || aWarpInfo[i].bEmpire == bEmpire) + { + x = aWarpInfo[i].x * 100; + y = aWarpInfo[i].y * 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); + ch->Stop(); + return; + } + } + */ + + } + + x *= 100; + y *= 100; + + ch->Show(ch->GetMapIndex(), x, y, z); + ch->Stop(); +} + +ACMD(do_warp) +{ + char arg1[256], arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: warp | "); + return; + } + + int x = 0, y = 0; + + if (isnhdigit(*arg1) && isnhdigit(*arg2)) + { + str_to_number(x, arg1); + str_to_number(y, arg2); + } + else + { + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (NULL == tch) + { + const CCI* pkCCI = P2P_MANAGER::instance().Find(arg1); + + if (NULL != pkCCI) + { + if (pkCCI->bChannel != g_bChannel) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Target is in %d channel (my channel %d)", pkCCI->bChannel, g_bChannel); + return; + } + + ch->WarpToPID( pkCCI->dwPID ); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no one by that name"); + } + + return; + } + else + { + x = tch->GetX() / 100; + y = tch->GetY() / 100; + } + } + + x *= 100; + y *= 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "You warp to ( %d, %d )", x, y); + ch->WarpSet(x, y); + ch->Stop(); +} + +ACMD(do_item) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: item "); + return; + } + + int iCount = 1; + + if (*arg2) + { + str_to_number(iCount, arg2); + iCount = MINMAX(1, iCount, ITEM_MAX_COUNT); + } + + DWORD dwVnum; + + if (isnhdigit(*arg1)) + str_to_number(dwVnum, arg1); + else + { + if (!ITEM_MANAGER::instance().GetVnum(arg1, dwVnum)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%u item not exist by that vnum.", dwVnum); + return; + } + } + + LPITEM item = ITEM_MANAGER::instance().CreateItem(dwVnum, iCount, 0, true); + + if (item) + { + if (item->IsDragonSoul()) + { + int iEmptyPos = ch->GetEmptyDragonSoulInventory(item); + + if (iEmptyPos != -1) + { + item->AddToCharacter(ch, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyPos)); + LogManager::instance().ItemLog(ch, item, "GM", item->GetName()); + } + else + { + M2_DESTROY_ITEM(item); + if (!ch->DragonSoul_IsQualified()) + { + ch->ChatPacket(CHAT_TYPE_INFO, "κ Ȱȭ ."); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space."); + } + } + else + { + int iEmptyPos = ch->GetEmptyInventory(item->GetSize()); + + if (iEmptyPos != -1) + { + item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); + LogManager::instance().ItemLog(ch, item, "GM", item->GetName()); + } + else + { + M2_DESTROY_ITEM(item); + ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space."); + } + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%u item not exist by that vnum.", dwVnum); + } +} + +ACMD(do_group_random) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: grrandom "); + return; + } + + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + CHARACTER_MANAGER::instance().SpawnGroupGroup(dwVnum, ch->GetMapIndex(), ch->GetX() - 500, ch->GetY() - 500, ch->GetX() + 500, ch->GetY() + 500); +} + +ACMD(do_group) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: group "); + return; + } + + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + if (test_server) + sys_log(0, "COMMAND GROUP SPAWN %u at %u %u %u", dwVnum, ch->GetMapIndex(), ch->GetX(), ch->GetY()); + + CHARACTER_MANAGER::instance().SpawnGroup(dwVnum, ch->GetMapIndex(), ch->GetX() - 500, ch->GetY() - 500, ch->GetX() + 500, ch->GetY() + 500); +} + +ACMD(do_mob_coward) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + LPCHARACTER tch; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mc "); + return; + } + + const CMob * pkMob; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob by that vnum"); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + iCount = MIN(20, iCount); + + while (iCount--) + { + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + if (tch) + tch->SetCoward(); + } +} + +ACMD(do_mob_map) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: mm "); + return; + } + + DWORD vnum = 0; + str_to_number(vnum, arg1); + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRandomPosition(vnum, ch->GetMapIndex()); + + if (tch) + ch->ChatPacket(CHAT_TYPE_INFO, "%s spawned in %dx%d", tch->GetName(), tch->GetX(), tch->GetY()); + else + ch->ChatPacket(CHAT_TYPE_INFO, "Spawn failed."); +} + +ACMD(do_mob_aggresive) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + LPCHARACTER tch; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob * pkMob; + + if (isdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob by that vnum"); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + iCount = MIN(20, iCount); + + while (iCount--) + { + tch = CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + if (tch) + tch->SetAggressive(); + } +} + +ACMD(do_mob) +{ + char arg1[256], arg2[256]; + DWORD vnum = 0; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob* pkMob = NULL; + + if (isnhdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob by that vnum"); + return; + } + + int iCount = 0; + + if (*arg2) + str_to_number(iCount, arg2); + else + iCount = 1; + + if (test_server) + iCount = MIN(40, iCount); + else + iCount = MIN(20, iCount); + + while (iCount--) + { + CHARACTER_MANAGER::instance().SpawnMobRange(vnum, + ch->GetMapIndex(), + ch->GetX() - number(200, 750), + ch->GetY() - number(200, 750), + ch->GetX() + number(200, 750), + ch->GetY() + number(200, 750), + true, + pkMob->m_table.bType == CHAR_TYPE_STONE); + } +} + +ACMD(do_mob_ld) +{ + char arg1[256], arg2[256], arg3[256], arg4[256]; + DWORD vnum = 0; + + two_arguments(two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3), arg4, sizeof(arg4)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: mob "); + return; + } + + const CMob* pkMob = NULL; + + if (isnhdigit(*arg1)) + { + str_to_number(vnum, arg1); + + if ((pkMob = CMobManager::instance().Get(vnum)) == NULL) + vnum = 0; + } + else + { + pkMob = CMobManager::Instance().Get(arg1, true); + + if (pkMob) + vnum = pkMob->m_table.dwVnum; + } + + if (vnum == 0) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such mob by that vnum"); + return; + } + + int dir = 1; + long x, y; + + if (*arg2) + str_to_number(x, arg2); + if (*arg3) + str_to_number(y, arg3); + if (*arg4) + str_to_number(dir, arg4); + + + CHARACTER_MANAGER::instance().SpawnMob(vnum, + ch->GetMapIndex(), + x*100, + y*100, + ch->GetZ(), + pkMob->m_table.bType == CHAR_TYPE_STONE, + dir); +} + +struct FuncPurge +{ + LPCHARACTER m_pkGM; + bool m_bAll; + + FuncPurge(LPCHARACTER ch) : m_pkGM(ch), m_bAll(false) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY()); + + if (!m_bAll && iDist >= 1000) // 10 ̻ ִ ͵ purge ʴ´. + return; + + sys_log(0, "PURGE: %s %d", pkChr->GetName(), iDist); + + if (pkChr->IsNPC() && !pkChr->IsPet() && pkChr->GetRider() == NULL) + { + M2_DESTROY_CHARACTER(pkChr); + } + } +}; + +ACMD(do_purge) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + FuncPurge func(ch); + + if (*arg1 && !strcmp(arg1, "all")) + func.m_bAll = true; + + LPSECTREE sectree = ch->GetSectree(); + if (sectree) // #431 + sectree->ForEachAround(func); + else + sys_err("PURGE_ERROR.NULL_SECTREE(mapIndex=%d, pos=(%d, %d)", ch->GetMapIndex(), ch->GetX(), ch->GetY()); +} + +ACMD(do_item_purge) +{ + int i; + LPITEM item; + + for (i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i) + { + if ((item = ch->GetInventoryItem(i))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, i, 255); + } + } + for (i = 0; i < DRAGON_SOUL_INVENTORY_MAX_NUM; ++i) + { + if ((item = ch->GetItem(TItemPos(DRAGON_SOUL_INVENTORY, i )))) + { + ITEM_MANAGER::instance().RemoveItem(item, "PURGE"); + } + } +} + +ACMD(do_state) +{ + char arg1[256]; + LPCHARACTER tch; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + if (arg1[0] == '#') + { + tch = CHARACTER_MANAGER::instance().Find(strtoul(arg1+1, NULL, 10)); + } + else + { + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + + if (!d) + tch = NULL; + else + tch = d->GetCharacter(); + } + } + else + tch = ch; + + if (!tch) + return; + + char buf[256]; + + snprintf(buf, sizeof(buf), "%s's State: ", tch->GetName()); + + if (tch->IsPosition(POS_FIGHTING)) + strlcat(buf, "Battle", sizeof(buf)); + else if (tch->IsPosition(POS_DEAD)) + strlcat(buf, "Dead", sizeof(buf)); + else + strlcat(buf, "Standing", sizeof(buf)); + + if (ch->GetShop()) + strlcat(buf, ", Shop", sizeof(buf)); + + if (ch->GetExchange()) + strlcat(buf, ", Exchange", sizeof(buf)); + + ch->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + + int len; + len = snprintf(buf, sizeof(buf), "Coordinate %ldx%ld (%ldx%ld)", + tch->GetX(), tch->GetY(), tch->GetX() / 100, tch->GetY() / 100); + + if (len < 0 || len >= (int) sizeof(buf)) + len = sizeof(buf) - 1; + + LPSECTREE pSec = SECTREE_MANAGER::instance().Get(tch->GetMapIndex(), tch->GetX(), tch->GetY()); + + if (pSec) + { + TMapSetting& map_setting = SECTREE_MANAGER::instance().GetMap(tch->GetMapIndex())->m_setting; + snprintf(buf + len, sizeof(buf) - len, " MapIndex %ld Attribute %08X Local Position (%ld x %ld)", + tch->GetMapIndex(), pSec->GetAttribute(tch->GetX(), tch->GetY()), (tch->GetX() - map_setting.iBaseX)/100, (tch->GetY() - map_setting.iBaseY)/100); + } + + ch->ChatPacket(CHAT_TYPE_INFO, "%s", buf); + + ch->ChatPacket(CHAT_TYPE_INFO, "LEV %d", tch->GetLevel()); + ch->ChatPacket(CHAT_TYPE_INFO, "HP %d/%d", tch->GetHP(), tch->GetMaxHP()); + ch->ChatPacket(CHAT_TYPE_INFO, "SP %d/%d", tch->GetSP(), tch->GetMaxSP()); + ch->ChatPacket(CHAT_TYPE_INFO, "ATT %d MAGIC_ATT %d SPD %d CRIT %d%% PENE %d%% ATT_BONUS %d%%", + tch->GetPoint(POINT_ATT_GRADE), + tch->GetPoint(POINT_MAGIC_ATT_GRADE), + tch->GetPoint(POINT_ATT_SPEED), + tch->GetPoint(POINT_CRITICAL_PCT), + tch->GetPoint(POINT_PENETRATE_PCT), + tch->GetPoint(POINT_ATT_BONUS)); + ch->ChatPacket(CHAT_TYPE_INFO, "DEF %d MAGIC_DEF %d BLOCK %d%% DODGE %d%% DEF_BONUS %d%%", + tch->GetPoint(POINT_DEF_GRADE), + tch->GetPoint(POINT_MAGIC_DEF_GRADE), + tch->GetPoint(POINT_BLOCK), + tch->GetPoint(POINT_DODGE), + tch->GetPoint(POINT_DEF_BONUS)); + ch->ChatPacket(CHAT_TYPE_INFO, "RESISTANCES:"); + ch->ChatPacket(CHAT_TYPE_INFO, " WARR:%3d%% ASAS:%3d%% SURA:%3d%% SHAM:%3d%%", + tch->GetPoint(POINT_RESIST_WARRIOR), + tch->GetPoint(POINT_RESIST_ASSASSIN), + tch->GetPoint(POINT_RESIST_SURA), + tch->GetPoint(POINT_RESIST_SHAMAN)); + ch->ChatPacket(CHAT_TYPE_INFO, " SWORD:%3d%% THSWORD:%3d%% DAGGER:%3d%% BELL:%3d%% FAN:%3d%% BOW:%3d%%", + tch->GetPoint(POINT_RESIST_SWORD), + tch->GetPoint(POINT_RESIST_TWOHAND), + tch->GetPoint(POINT_RESIST_DAGGER), + tch->GetPoint(POINT_RESIST_BELL), + tch->GetPoint(POINT_RESIST_FAN), + tch->GetPoint(POINT_RESIST_BOW)); + ch->ChatPacket(CHAT_TYPE_INFO, " FIRE:%3d%% ELEC:%3d%% MAGIC:%3d%% WIND:%3d%% CRIT:%3d%% PENE:%3d%%", + tch->GetPoint(POINT_RESIST_FIRE), + tch->GetPoint(POINT_RESIST_ELEC), + tch->GetPoint(POINT_RESIST_MAGIC), + tch->GetPoint(POINT_RESIST_WIND), + tch->GetPoint(POINT_RESIST_CRITICAL), + tch->GetPoint(POINT_RESIST_PENETRATE)); + ch->ChatPacket(CHAT_TYPE_INFO, " ICE:%3d%% EARTH:%3d%% DARK:%3d%%", + tch->GetPoint(POINT_RESIST_ICE), + tch->GetPoint(POINT_RESIST_EARTH), + tch->GetPoint(POINT_RESIST_DARK)); + + ch->ChatPacket(CHAT_TYPE_INFO, "MALL:"); + ch->ChatPacket(CHAT_TYPE_INFO, " ATT:%3d%% DEF:%3d%% EXP:%3d%% ITEMx%d GOLDx%d", + tch->GetPoint(POINT_MALL_ATTBONUS), + tch->GetPoint(POINT_MALL_DEFBONUS), + tch->GetPoint(POINT_MALL_EXPBONUS), + tch->GetPoint(POINT_MALL_ITEMBONUS) / 10, + tch->GetPoint(POINT_MALL_GOLDBONUS) / 10); + + ch->ChatPacket(CHAT_TYPE_INFO, "BONUS:"); + ch->ChatPacket(CHAT_TYPE_INFO, " SKILL:%3d%% NORMAL:%3d%% SKILL_DEF:%3d%% NORMAL_DEF:%3d%%", + tch->GetPoint(POINT_SKILL_DAMAGE_BONUS), + tch->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS), + tch->GetPoint(POINT_SKILL_DEFEND_BONUS), + tch->GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS)); + + ch->ChatPacket(CHAT_TYPE_INFO, " HUMAN:%3d%% ANIMAL:%3d%% ORC:%3d%% MILGYO:%3d%% UNDEAD:%3d%%", + tch->GetPoint(POINT_ATTBONUS_HUMAN), + tch->GetPoint(POINT_ATTBONUS_ANIMAL), + tch->GetPoint(POINT_ATTBONUS_ORC), + tch->GetPoint(POINT_ATTBONUS_MILGYO), + tch->GetPoint(POINT_ATTBONUS_UNDEAD)); + + ch->ChatPacket(CHAT_TYPE_INFO, " DEVIL:%3d%% INSECT:%3d%% FIRE:%3d%% ICE:%3d%% DESERT:%3d%%", + tch->GetPoint(POINT_ATTBONUS_DEVIL), + tch->GetPoint(POINT_ATTBONUS_INSECT), + tch->GetPoint(POINT_ATTBONUS_FIRE), + tch->GetPoint(POINT_ATTBONUS_ICE), + tch->GetPoint(POINT_ATTBONUS_DESERT)); + + ch->ChatPacket(CHAT_TYPE_INFO, " TREE:%3d%% MONSTER:%3d%%", + tch->GetPoint(POINT_ATTBONUS_TREE), + tch->GetPoint(POINT_ATTBONUS_MONSTER)); + + ch->ChatPacket(CHAT_TYPE_INFO, " WARR:%3d%% ASAS:%3d%% SURA:%3d%% SHAM:%3d%%", + tch->GetPoint(POINT_ATTBONUS_WARRIOR), + tch->GetPoint(POINT_ATTBONUS_ASSASSIN), + tch->GetPoint(POINT_ATTBONUS_SURA), + tch->GetPoint(POINT_ATTBONUS_SHAMAN)); + + for (int i = 0; i < MAX_PRIV_NUM; ++i) + if (CPrivManager::instance().GetPriv(tch, i)) + { + int iByEmpire = CPrivManager::instance().GetPrivByEmpire(tch->GetEmpire(), i); + int iByGuild = 0; + + if (tch->GetGuild()) + iByGuild = CPrivManager::instance().GetPrivByGuild(tch->GetGuild()->GetID(), i); + + int iByPlayer = CPrivManager::instance().GetPrivByCharacter(tch->GetPlayerID(), i); + + if (iByEmpire) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for empire : %d", LC_TEXT(c_apszPrivNames[i]), iByEmpire); + + if (iByGuild) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for guild : %d", LC_TEXT(c_apszPrivNames[i]), iByGuild); + + if (iByPlayer) + ch->ChatPacket(CHAT_TYPE_INFO, "%s for player : %d", LC_TEXT(c_apszPrivNames[i]), iByPlayer); + } +} + +struct notice_packet_func +{ + const char * m_str; + + notice_packet_func(const char * str) : m_str(str) + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); + } +}; + +struct monarch_notice_packet_func +{ + const char * m_str; + BYTE m_bEmpire; + + monarch_notice_packet_func(BYTE bEmpire, const char * str) : m_str(str), m_bEmpire(bEmpire) + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + if (m_bEmpire == d->GetCharacter()->GetEmpire()) + { + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); + } + } +}; + + +void SendNotice(const char * c_pszBuf) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), notice_packet_func(c_pszBuf)); +} + +void SendMonarchNotice(BYTE bEmpire, const char* c_pszBuf) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), monarch_notice_packet_func(bEmpire, c_pszBuf)); +} + +struct notice_map_packet_func +{ + const char* m_str; + int m_mapIndex; + bool m_bBigFont; + + notice_map_packet_func(const char* str, int idx, bool bBigFont) : m_str(str), m_mapIndex(idx), m_bBigFont(bBigFont) + { + } + + void operator() (LPDESC d) + { + if (d->GetCharacter() == NULL) return; + if (d->GetCharacter()->GetMapIndex() != m_mapIndex) return; + + d->GetCharacter()->ChatPacket(m_bBigFont == true ? CHAT_TYPE_BIG_NOTICE : CHAT_TYPE_NOTICE, "%s", m_str); + } +}; + +void SendNoticeMap(const char* c_pszBuf, int nMapIndex, bool bBigFont) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), notice_map_packet_func(c_pszBuf, nMapIndex, bBigFont)); +} + +struct log_packet_func +{ + const char * m_str; + + log_packet_func(const char * str) : m_str(str) + { + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + if (d->GetCharacter()->GetGMLevel() > GM_PLAYER) + d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, "%s", m_str); + } +}; + + +void SendLog(const char * c_pszBuf) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + std::for_each(c_ref_set.begin(), c_ref_set.end(), log_packet_func(c_pszBuf)); +} + +void BroadcastNotice(const char * c_pszBuf) +{ + TPacketGGNotice p; + p.bHeader = HEADER_GG_NOTICE; + p.lSize = strlen(c_pszBuf) + 1; + + TEMP_BUFFER buf; + buf.write(&p, sizeof(p)); + buf.write(c_pszBuf, p.lSize); + + P2P_MANAGER::instance().Send(buf.read_peek(), buf.size()); // HEADER_GG_NOTICE + + SendNotice(c_pszBuf); +} + +void BroadcastMonarchNotice(BYTE bEmpire, const char * c_pszBuf) +{ + TPacketGGMonarchNotice p; + p.bHeader = HEADER_GG_MONARCH_NOTICE; + p.bEmpire = bEmpire; + p.lSize = strlen(c_pszBuf) + 1; + + TEMP_BUFFER buf; + buf.write(&p, sizeof(p)); + buf.write(c_pszBuf, p.lSize); + + P2P_MANAGER::instance().Send(buf.read_peek(), buf.size()); + + SendMonarchNotice(bEmpire, c_pszBuf); +} + +ACMD(do_notice) +{ + BroadcastNotice(argument); +} + +ACMD(do_map_notice) +{ + SendNoticeMap(argument, ch->GetMapIndex(), false); +} + +ACMD(do_big_notice) +{ + ch->ChatPacket(CHAT_TYPE_BIG_NOTICE, "%s", argument); +} + +ACMD(do_monarch_notice) +{ + if (ch->IsMonarch() == true) + { + BroadcastMonarchNotice(ch->GetEmpire(), argument); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ָ Դϴ")); + } +} + +ACMD(do_who) +{ + int iTotal; + int * paiEmpireUserCount; + int iLocal; + + DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal); + + ch->ChatPacket(CHAT_TYPE_INFO, "Total [%d] %d / %d / %d (this server %d)", + iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal); +} + +class user_func +{ + public: + LPCHARACTER m_ch; + static int count; + static char str[128]; + static int str_len; + + user_func() + : m_ch(NULL) + {} + + void initialize(LPCHARACTER ch) + { + m_ch = ch; + str_len = 0; + count = 0; + str[0] = '\0'; + } + + void operator () (LPDESC d) + { + if (!d->GetCharacter()) + return; + + int len = snprintf(str + str_len, sizeof(str) - str_len, "%-16s ", d->GetCharacter()->GetName()); + + if (len < 0 || len >= (int) sizeof(str) - str_len) + len = (sizeof(str) - str_len) - 1; + + str_len += len; + ++count; + + if (!(count % 4)) + { + m_ch->ChatPacket(CHAT_TYPE_INFO, str); + + str[0] = '\0'; + str_len = 0; + } + } +}; + +int user_func::count = 0; +char user_func::str[128] = { 0, }; +int user_func::str_len = 0; + +ACMD(do_user) +{ + const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet(); + user_func func; + + func.initialize(ch); + std::for_each(c_ref_set.begin(), c_ref_set.end(), func); + + if (func.count % 4) + ch->ChatPacket(CHAT_TYPE_INFO, func.str); + + ch->ChatPacket(CHAT_TYPE_INFO, "Total %d", func.count); +} + +ACMD(do_disconnect) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /dc "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player.", arg1); + return; + } + + if (tch == ch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "cannot disconnect myself"); + return; + } + + DESC_MANAGER::instance().DestroyDesc(d); +} + +ACMD(do_kill) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "ex) /kill "); + return; + } + + LPDESC d = DESC_MANAGER::instance().FindByCharacterName(arg1); + LPCHARACTER tch = d ? d->GetCharacter() : NULL; + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s: no such a player", arg1); + return; + } + + tch->Dead(); +} + +#define MISC 0 +#define BINARY 1 +#define NUMBER 2 + +const struct set_struct +{ + const char *cmd; + const char type; +} set_fields[] = { + { "gold", NUMBER }, + { "race", BINARY }, + { "sex", BINARY }, + { "exp", NUMBER }, + { "max_hp", NUMBER }, + { "max_sp", NUMBER }, + { "skill", NUMBER }, + { "alignment", NUMBER }, + { "align", NUMBER }, + { "\n", MISC } +}; + +ACMD(do_set) +{ + char arg1[256], arg2[256], arg3[256]; + + LPCHARACTER tch = NULL; + + int i, len; + const char* line; + + line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(line, arg3, sizeof(arg3)); + + if (!*arg1 || !*arg2 || !*arg3) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: set "); + return; + } + + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s not exist", arg1); + return; + } + + len = strlen(arg2); + + for (i = 0; *(set_fields[i].cmd) != '\n'; i++) + if (!strncmp(arg2, set_fields[i].cmd, len)) + break; + + switch (i) + { + case 0: // gold + { + int gold = 0; + str_to_number(gold, arg3); + DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 3, gold); + int before_gold = tch->GetGold(); + tch->PointChange(POINT_GOLD, gold, true); + int after_gold = tch->GetGold(); + if (0 == after_gold && 0 != before_gold) + { + LogManager::instance().CharLog(tch, gold, "ZERO_GOLD", "GM"); + } + } + break; + + case 1: // race + break; + + case 2: // sex + break; + + case 3: // exp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_EXP, amount, true); + } + break; + + case 4: // max_hp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_MAX_HP, amount, true); + } + break; + + case 5: // max_sp + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_MAX_SP, amount, true); + } + break; + + case 6: // active skill point + { + int amount = 0; + str_to_number(amount, arg3); + tch->PointChange(POINT_SKILL, amount, true); + } + break; + + case 7: // alignment + case 8: // alignment + { + int amount = 0; + str_to_number(amount, arg3); + tch->UpdateAlignment(amount - ch->GetRealAlignment()); + } + break; + } + + if (set_fields[i].type == NUMBER) + { + int amount = 0; + str_to_number(amount, arg3); + ch->ChatPacket(CHAT_TYPE_INFO, "%s's %s set to [%d]", tch->GetName(), set_fields[i].cmd, amount); + } +} + +ACMD(do_reset) +{ + ch->PointChange(POINT_HP, ch->GetMaxHP() - ch->GetHP()); + ch->PointChange(POINT_SP, ch->GetMaxSP() - ch->GetSP()); + ch->Save(); +} + +ACMD(do_advance) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: advance "); + return; + } + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "%s not exist", arg1); + return; + } + + int level = 0; + str_to_number(level, arg2); + + tch->ResetPoint(MINMAX(0, level, PLAYER_MAX_LEVEL_CONST)); +} + +ACMD(do_respawn) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1 && !strcasecmp(arg1, "all")) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Respaw everywhere"); + regen_reset(0, 0); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "Respaw around"); + regen_reset(ch->GetX(), ch->GetY()); + } +} + +ACMD(do_safebox_size) +{ + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int size = 0; + + if (*arg1) + str_to_number(size, arg1); + + if (size > 3 || size < 0) + size = 0; + + ch->ChatPacket(CHAT_TYPE_INFO, "Safebox size set to %d", size); + ch->ChangeSafeboxSize(size); +} + +ACMD(do_makeguild) +{ + if (ch->GetGuild()) + return; + + CGuildManager& gm = CGuildManager::instance(); + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + TGuildCreateParameter cp; + memset(&cp, 0, sizeof(cp)); + + cp.master = ch; + strlcpy(cp.name, arg1, sizeof(cp.name)); + + if (!check_name(cp.name)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT(" ̸ Դϴ.")); + return; + } + + gm.CreateGuild(cp); + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) 尡 Ǿϴ. [ӽ]"), cp.name); +} + +ACMD(do_deleteguild) +{ + if (ch->GetGuild()) + ch->GetGuild()->RequestDisband(ch->GetPlayerID()); +} + +ACMD(do_greset) +{ + if (ch->GetGuild()) + ch->GetGuild()->Reset(); +} + +// REFINE_ROD_HACK_BUG_FIX +ACMD(do_refine_rod) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + fishing::RealRefineRod(ch, item); +} +// END_OF_REFINE_ROD_HACK_BUG_FIX + +// REFINE_PICK +ACMD(do_refine_pick) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + { + mining::CHEAT_MAX_PICK(ch, item); + mining::RealRefinePick(ch, item); + } +} + +ACMD(do_max_pick) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + BYTE cell = 0; + str_to_number(cell, arg1); + LPITEM item = ch->GetInventoryItem(cell); + if (item) + { + mining::CHEAT_MAX_PICK(ch, item); + } +} +// END_OF_REFINE_PICK + + +ACMD(do_fishing_simul) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + two_arguments(argument, arg2, sizeof(arg2), arg3, sizeof(arg3)); + + int count = 1000; + int prob_idx = 0; + int level = 100; + + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: fishing_simul "); + + if (*arg1) + str_to_number(level, arg1); + + if (*arg2) + str_to_number(prob_idx, arg2); + + if (*arg3) + str_to_number(count, arg3); + + fishing::Simulation(level, count, prob_idx, ch); +} + +ACMD(do_invisibility) +{ + if (ch->IsAffectFlag(AFF_INVISIBILITY)) + { + ch->RemoveAffect(AFFECT_INVISIBILITY); + } + else + { + ch->AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, INFINITE_AFFECT_DURATION, 0, true); + } +} + +ACMD(do_event_flag) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!(*arg1) || !(*arg2)) + return; + + int value = 0; + str_to_number(value, arg2); + + if (!strcmp(arg1, "mob_item") || + !strcmp(arg1, "mob_exp") || + !strcmp(arg1, "mob_gold") || + !strcmp(arg1, "mob_dam") || + !strcmp(arg1, "mob_gold_pct") || + !strcmp(arg1, "mob_item_buyer") || + !strcmp(arg1, "mob_exp_buyer") || + !strcmp(arg1, "mob_gold_buyer") || + !strcmp(arg1, "mob_gold_pct_buyer") + ) + value = MINMAX(0, value, 1000); + + //quest::CQuestManager::instance().SetEventFlag(arg1, atoi(arg2)); + quest::CQuestManager::instance().RequestSetEventFlag(arg1, value); + ch->ChatPacket(CHAT_TYPE_INFO, "RequestSetEventFlag %s %d", arg1, value); + sys_log(0, "RequestSetEventFlag %s %d", arg1, value); +} + +ACMD(do_get_event_flag) +{ + quest::CQuestManager::instance().SendEventFlagList(ch); +} + +ACMD(do_private) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: private "); + return; + } + + long lMapIndex; + long map_index = 0; + str_to_number(map_index, arg1); + if ((lMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(map_index))) + { + ch->SaveExitLocation(); + + LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex); + ch->WarpSet(pkSectreeMap->m_setting.posSpawn.x, pkSectreeMap->m_setting.posSpawn.y, lMapIndex); + } + else + ch->ChatPacket(CHAT_TYPE_INFO, "Can't find map by index %d", map_index); +} + +ACMD(do_qf) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + return; + + quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID()); + std::string questname = pPC->GetCurrentQuestName(); + + if (!questname.empty()) + { + int value = quest::CQuestManager::Instance().GetQuestStateIndex(questname, arg1); + + pPC->SetFlag(questname + ".__status", value); + pPC->ClearTimer(); + + quest::PC::QuestInfoIterator it = pPC->quest_begin(); + unsigned int questindex = quest::CQuestManager::instance().GetQuestIndexByName(questname); + + while (it!= pPC->quest_end()) + { + if (it->first == questindex) + { + it->second.st = value; + break; + } + + ++it; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag %s %s %d", questname.c_str(), arg1, value); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag failed"); + } +} + +LPCHARACTER chHori, chForge, chLib, chTemple, chTraining, chTree, chPortal, chBall; + +ACMD(do_b1) +{ + //ȣ 478 579 + chHori = CHARACTER_MANAGER::instance().SpawnMobRange(14017, ch->GetMapIndex(), 304222, 742858, 304222, 742858, true, false); + chHori->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_CONSTRUCTION_SMALL, 65535, 0, true); + chHori->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + + for (int i = 0; i < 30; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 800, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(number(701, 706), + ch->GetMapIndex(), + 304222 + (int)fx, + 742858 + (int)fy, + 304222 + (int)fx, + 742858 + (int)fy, + true, + false); + tch->SetAggressive(); + } + + for (int i = 0; i < 5; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 800, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(8009, + ch->GetMapIndex(), + 304222 + (int)fx, + 742858 + (int)fy, + 304222 + (int)fx, + 742858 + (int)fy, + true, + false); + tch->SetAggressive(); + } +} + +ACMD(do_b2) +{ + chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); +} + +ACMD(do_b3) +{ + // 492 547 + chForge = CHARACTER_MANAGER::instance().SpawnMobRange(14003, ch->GetMapIndex(), 307500, 746300, 307500, 746300, true, false); + chForge->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + //ž 509 589 -> + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14007, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + // 513 606 -> ǽ + chTemple = CHARACTER_MANAGER::instance().SpawnMobRange(14004, ch->GetMapIndex(), 307700, 741600, 307700, 741600, true, false); + chTemple->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + // 490 625 + chTraining= CHARACTER_MANAGER::instance().SpawnMobRange(14010, ch->GetMapIndex(), 307100, 739500, 307100, 739500, true, false); + chTraining->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + // 466 614 + chTree= CHARACTER_MANAGER::instance().SpawnMobRange(14013, ch->GetMapIndex(), 300800, 741600, 300800, 741600, true, false); + chTree->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + //Ż 439 615 + chPortal= CHARACTER_MANAGER::instance().SpawnMobRange(14001, ch->GetMapIndex(), 300900, 744500, 300900, 744500, true, false); + chPortal->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); + // 436 600 + chBall = CHARACTER_MANAGER::instance().SpawnMobRange(14012, ch->GetMapIndex(), 302500, 746600, 302500, 746600, true, false); + chBall->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_b4) +{ + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_UPGRADE, 65535, 0, true); + + for (int i = 0; i < 30; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 1200, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(number(701, 706), + ch->GetMapIndex(), + 307900 + (int)fx, + 744500 + (int)fy, + 307900 + (int)fx, + 744500 + (int)fy, + true, + false); + tch->SetAggressive(); + } + + for (int i = 0; i < 5; ++i) + { + int rot = number(0, 359); + float fx, fy; + GetDeltaByDegree(rot, 1200, &fx, &fy); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().SpawnMobRange(8009, + ch->GetMapIndex(), + 307900 + (int)fx, + 744500 + (int)fy, + 307900 + (int)fx, + 744500 + (int)fy, + true, + false); + tch->SetAggressive(); + } + +} + +ACMD(do_b5) +{ + M2_DESTROY_CHARACTER(chLib); + //chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14008, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_b6) +{ + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_BUILDING_UPGRADE, 65535, 0, true); +} +ACMD(do_b7) +{ + M2_DESTROY_CHARACTER(chLib); + //chHori->RemoveAffect(AFFECT_DUNGEON_UNIQUE); + chLib = CHARACTER_MANAGER::instance().SpawnMobRange(14009, ch->GetMapIndex(), 307900, 744500, 307900, 744500, true, false); + chLib->AddAffect(AFFECT_DUNGEON_UNIQUE, POINT_NONE, 0, AFF_DUNGEON_UNIQUE, 65535, 0, true); +} + +ACMD(do_book) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + CSkillProto * pkProto; + + if (isnhdigit(*arg1)) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + pkProto = CSkillManager::instance().Get(vnum); + } + else + pkProto = CSkillManager::instance().Get(arg1); + + if (!pkProto) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such a skill."); + return; + } + + LPITEM item = ch->AutoGiveItem(50300); + item->SetSocket(0, pkProto->dwVnum); +} + +ACMD(do_setskillother) +{ + char arg1[256], arg2[256], arg3[256]; + argument = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + one_argument(argument, arg3, sizeof(arg3)); + + if (!*arg1 || !*arg2 || !*arg3 || !isdigit(*arg3)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setskillother "); + return; + } + + LPCHARACTER tch; + + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + CSkillProto * pk; + + if (isdigit(*arg2)) + { + DWORD vnum = 0; + str_to_number(vnum, arg2); + pk = CSkillManager::instance().Get(vnum); + } + else + pk = CSkillManager::instance().Get(arg2); + + if (!pk) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such a skill by that name."); + return; + } + + BYTE level = 0; + str_to_number(level, arg3); + tch->SetSkillLevel(pk->dwVnum, level); + tch->ComputePoints(); + tch->SkillLevelPacket(); +} + +ACMD(do_setskill) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2 || !isdigit(*arg2)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setskill "); + return; + } + + CSkillProto * pk; + + if (isdigit(*arg1)) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + pk = CSkillManager::instance().Get(vnum); + } + + else + pk = CSkillManager::instance().Get(arg1); + + if (!pk) + { + ch->ChatPacket(CHAT_TYPE_INFO, "No such a skill by that name."); + return; + } + + BYTE level = 0; + str_to_number(level, arg2); + ch->SetSkillLevel(pk->dwVnum, level); + ch->ComputePoints(); + ch->SkillLevelPacket(); +} + +ACMD(do_set_skill_point) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int skill_point = 0; + if (*arg1) + str_to_number(skill_point, arg1); + + ch->SetRealPoint(POINT_SKILL, skill_point); + ch->SetPoint(POINT_SKILL, ch->GetRealPoint(POINT_SKILL)); + ch->PointChange(POINT_SKILL, 0); +} + +ACMD(do_set_skill_group) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int skill_group = 0; + if (*arg1) + str_to_number(skill_group, arg1); + + ch->SetSkillGroup(skill_group); + + ch->ClearSkill(); + ch->ChatPacket(CHAT_TYPE_INFO, "skill group to %d.", skill_group); +} + +ACMD(do_reload) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + switch (LOWER(*arg1)) + { + case 'u': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading state_user_count."); + LoadStateUserCount(); + break; + + case 'p': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading prototype tables,"); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); + break; + + case 's': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading notice string."); + DBManager::instance().LoadDBString(); + break; + + case 'q': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading quest."); + quest::CQuestManager::instance().Reload(); + break; + + case 'f': + fishing::Initialize(); + break; + + //RELOAD_ADMIN + case 'a': + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading Admin infomation."); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0); + sys_log(0, "Reloading admin infomation."); + break; + //END_RELOAD_ADMIN + case 'c': // cube + // μ Ѵ. + Cube_init (); + break; + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading state_user_count."); + LoadStateUserCount(); + + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading prototype tables,"); + db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); + + ch->ChatPacket(CHAT_TYPE_INFO, "Reloading notice string."); + DBManager::instance().LoadDBString(); + } +} + +ACMD(do_cooltime) +{ + ch->DisableCooltime(); +} + +ACMD(do_level) +{ + char arg2[256]; + one_argument(argument, arg2, sizeof(arg2)); + + if (!*arg2) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: level "); + return; + } + + int level = 0; + str_to_number(level, arg2); + ch->ResetPoint(MINMAX(1, level, PLAYER_MAX_LEVEL_CONST)); + + ch->ClearSkill(); + ch->ClearSubSkill(); +} + +ACMD(do_gwlist) +{ + ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT(" Դϴ")); + CGuildManager::instance().ShowGuildWarList(ch); +} + +ACMD(do_stop_guild_war) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + int id1 = 0, id2 = 0; + + str_to_number(id1, arg1); + str_to_number(id2, arg2); + + if (!id1 || !id2) + return; + + if (id1 > id2) + { + std::swap(id1, id2); + } + + ch->ChatPacket(CHAT_TYPE_TALKING, "%d %d", id1, id2); + CGuildManager::instance().RequestEndWar(id1, id2); +} + +ACMD(do_cancel_guild_war) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + int id1 = 0, id2 = 0; + str_to_number(id1, arg1); + str_to_number(id2, arg2); + + if (id1 > id2) + std::swap(id1, id2); + + CGuildManager::instance().RequestCancelWar(id1, id2); +} + +ACMD(do_guild_state) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + CGuild* pGuild = CGuildManager::instance().FindGuildByName(arg1); + if (pGuild != NULL) + { + ch->ChatPacket(CHAT_TYPE_INFO, "GuildID: %d", pGuild->GetID()); + ch->ChatPacket(CHAT_TYPE_INFO, "GuildMasterPID: %d", pGuild->GetMasterPID()); + ch->ChatPacket(CHAT_TYPE_INFO, "IsInWar: %d", pGuild->UnderAnyWar()); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s: ʴ Դϴ."), arg1); + } +} + +struct FuncWeaken +{ + LPCHARACTER m_pkGM; + bool m_bAll; + + FuncWeaken(LPCHARACTER ch) : m_pkGM(ch), m_bAll(false) + { + } + + void operator () (LPENTITY ent) + { + if (!ent->IsType(ENTITY_CHARACTER)) + return; + + LPCHARACTER pkChr = (LPCHARACTER) ent; + + int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY()); + + if (!m_bAll && iDist >= 1000) // 10 ̻ ִ ͵ purge ʴ´. + return; + + if (pkChr->IsNPC()) + pkChr->PointChange(POINT_HP, (10 - pkChr->GetHP())); + } +}; + +ACMD(do_weaken) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + FuncWeaken func(ch); + + if (*arg1 && !strcmp(arg1, "all")) + func.m_bAll = true; + + ch->GetSectree()->ForEachAround(func); +} + +ACMD(do_getqf) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + LPCHARACTER tch; + + if (!*arg1) + tch = ch; + else + { + tch = CHARACTER_MANAGER::instance().FindPC(arg1); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + pPC->SendFlagList(ch); +} + +ACMD(do_set_state) +{ + char arg1[256]; + char arg2[256]; + + //argument = one_argument(argument, arg1, sizeof(arg1)); + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + return; + + quest::PC* pPC = quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID()); + std::string questname = arg1; + std::string statename = arg2; + + if (!questname.empty()) + { + int value = quest::CQuestManager::Instance().GetQuestStateIndex(questname, statename); + + pPC->SetFlag(questname + ".__status", value); + pPC->ClearTimer(); + + quest::PC::QuestInfoIterator it = pPC->quest_begin(); + unsigned int questindex = quest::CQuestManager::instance().GetQuestIndexByName(questname); + + while (it!= pPC->quest_end()) + { + if (it->first == questindex) + { + it->second.st = value; + break; + } + + ++it; + } + + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag %s %s %d", questname.c_str(), arg1, value); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "setting quest state flag failed"); + } +} + +ACMD(do_setqf) +{ + char arg1[256]; + char arg2[256]; + char arg3[256]; + + one_argument(two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)), arg3, sizeof(arg3)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: setqf []"); + return; + } + + LPCHARACTER tch = ch; + + if (*arg3) + tch = CHARACTER_MANAGER::instance().FindPC(arg3); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + { + int value = 0; + str_to_number(value, arg2); + pPC->SetFlag(arg1, value); + ch->ChatPacket(CHAT_TYPE_INFO, "Quest flag set: %s %d", arg1, value); + } +} + +ACMD(do_delqf) +{ + char arg1[256]; + char arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Syntax: delqf []"); + return; + } + + LPCHARACTER tch = ch; + + if (*arg2) + tch = CHARACTER_MANAGER::instance().FindPC(arg2); + + if (!tch) + { + ch->ChatPacket(CHAT_TYPE_INFO, "There is no such character."); + return; + } + + quest::PC* pPC = quest::CQuestManager::instance().GetPC(tch->GetPlayerID()); + + if (pPC) + { + if (pPC->DeleteFlag(arg1)) + ch->ChatPacket(CHAT_TYPE_INFO, "Delete success."); + else + ch->ChatPacket(CHAT_TYPE_INFO, "Delete failed. Quest flag does not exist."); + } +} + +ACMD(do_forgetme) +{ + ch->ForgetMyAttacker(); +} + +ACMD(do_aggregate) +{ + ch->AggregateMonster(); +} + +ACMD(do_attract_ranger) +{ + ch->AttractRanger(); +} + +ACMD(do_pull_monster) +{ + ch->PullMonster(); +} + +ACMD(do_polymorph) +{ + char arg1[256], arg2[256]; + + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + bool bMaintainStat = false; + if (*arg2) + { + int value = 0; + str_to_number(value, arg2); + bMaintainStat = (value>0); + } + + ch->SetPolymorph(dwVnum, bMaintainStat); + } +} + +ACMD(do_polymorph_item) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + LPITEM item = ITEM_MANAGER::instance().CreateItem(70104, 1, 0, true); + if (item) + { + item->SetSocket(0, dwVnum); + int iEmptyPos = ch->GetEmptyInventory(item->GetSize()); + + if (iEmptyPos != -1) + { + item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyPos)); + LogManager::instance().ItemLog(ch, item, "GM", item->GetName()); + } + else + { + M2_DESTROY_ITEM(item); + ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space."); + } + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d item not exist by that vnum.", 70103); + } + //ch->SetPolymorph(dwVnum, bMaintainStat); + } +} + +ACMD(do_priv_empire) +{ + char arg1[256] = {0}; + char arg2[256] = {0}; + char arg3[256] = {0}; + char arg4[256] = {0}; + int empire = 0; + int type = 0; + int value = 0; + int duration = 0; + + const char* line = two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (!*arg1 || !*arg2) + goto USAGE; + + if (!line) + goto USAGE; + + two_arguments(line, arg3, sizeof(arg3), arg4, sizeof(arg4)); + + if (!*arg3 || !*arg4) + goto USAGE; + + str_to_number(empire, arg1); + str_to_number(type, arg2); + str_to_number(value, arg3); + value = MINMAX(0, value, 1000); + str_to_number(duration, arg4); + + if (empire < 0 || 3 < empire) + goto USAGE; + + if (type < 1 || 4 < type) + goto USAGE; + + if (value < 0) + goto USAGE; + + if (duration < 0) + goto USAGE; + + // ð + duration = duration * (60*60); + + sys_log(0, "_give_empire_privileage(empire=%d, type=%d, value=%d, duration=%d) by command", + empire, type, value, duration); + CPrivManager::instance().RequestGiveEmpirePriv(empire, type, value, duration); + return; + +USAGE: + ch->ChatPacket(CHAT_TYPE_INFO, "usage : priv_empire "); + ch->ChatPacket(CHAT_TYPE_INFO, " 0 - 3 (0==all)"); + ch->ChatPacket(CHAT_TYPE_INFO, " 1:item_drop, 2:gold_drop, 3:gold10_drop, 4:exp"); + ch->ChatPacket(CHAT_TYPE_INFO, " percent"); + ch->ChatPacket(CHAT_TYPE_INFO, " hour"); +} + +/** + * @version 05/06/08 Bang2ni - ʽ Ʈ ȵǴ .(ũƮ ۼȵ.) + * quest/priv_guild.quest ũƮ о + */ +ACMD(do_priv_guild) +{ + static const char msg[] = { '\0' }; + + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + CGuild * g = CGuildManager::instance().FindGuildByName(arg1); + + if (!g) + { + DWORD guild_id = 0; + str_to_number(guild_id, arg1); + g = CGuildManager::instance().FindGuild(guild_id); + } + + if (!g) + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ̸ Ǵ ȣ 尡 ϴ.")); + else + { + char buf[1024+1]; + snprintf(buf, sizeof(buf), msg, g->GetID()); + + using namespace quest; + PC * pc = CQuestManager::instance().GetPC(ch->GetPlayerID()); + QuestState qs = CQuestManager::instance().OpenState("ADMIN_QUEST", QUEST_FISH_REFINE_STATE_INDEX); + luaL_loadbuffer(qs.co, buf, strlen(buf), "ADMIN_QUEST"); + pc->SetQuest("ADMIN_QUEST", qs); + + QuestState & rqs = *pc->GetRunningQuestState(); + + if (!CQuestManager::instance().RunState(rqs)) + { + CQuestManager::instance().CloseState(rqs); + pc->EndRunning(); + return; + } + } + } +} + +ACMD(do_mount_test) +{ + char arg1[256]; + + one_argument(argument, arg1, sizeof(arg1)); + + if (*arg1) + { + DWORD vnum = 0; + str_to_number(vnum, arg1); + ch->MountVnum(vnum); + } +} + +ACMD(do_observer) +{ + ch->SetObserverMode(!ch->IsObserverMode()); +} + +ACMD(do_socket_item) +{ + char arg1[256], arg2[256]; + two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); + + if (*arg1) + { + DWORD dwVnum = 0; + str_to_number(dwVnum, arg1); + + int iSocketCount = 0; + str_to_number(iSocketCount, arg2); + + if (!iSocketCount || iSocketCount >= ITEM_SOCKET_MAX_NUM) + iSocketCount = 3; + + if (!dwVnum) + { + if (!ITEM_MANAGER::instance().GetVnum(arg1, dwVnum)) + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d item not exist by that vnum.", dwVnum); + return; + } + } + + LPITEM item = ch->AutoGiveItem(dwVnum); + + if (item) + { + for (int i = 0; i < iSocketCount; ++i) + item->SetSocket(i, 1); + } + else + { + ch->ChatPacket(CHAT_TYPE_INFO, "#%d cannot create item.", dwVnum); + } + } +} + +ACMD(do_xmas) +{ + char arg1[256]; + one_argument(argument, arg1, sizeof(arg1)); + + int flag = 0; + + if (*arg1) + str_to_number(flag, arg1); + + switch (subcmd) + { + case SCMD_XMAS_SNOW: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_snow", flag); + break; + + case SCMD_XMAS_BOOM: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_boom", flag); + break; + + case SCMD_XMAS_SANTA: + quest::CQuestManager::instance().RequestSetEventFlag("xmas_santa", flag); + break; + } +} + + +// BLOCK_CHAT +ACMD(do_block_chat_list) +{ + // GM ƴϰų block_chat_privilege ɾ Ұ + if (!ch || (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + DBManager::instance().ReturnQuery(QID_BLOCK_CHAT_LIST, ch->GetPlayerID(), NULL, + "SELECT p.name, a.lDuration FROM affect%s as a, player%s as p WHERE a.bType = %d AND a.dwPID = p.id", + get_table_postfix(), get_table_postfix(), AFFECT_BLOCK_CHAT); +} + +ACMD(do_vote_block_chat) +{ + return; + + char arg1[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: vote_block_chat "); + return; + } + + const char* name = arg1; + long lBlockDuration = 10; + sys_log(0, "vote_block_chat %s %d", name, lBlockDuration); + + LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(name); + + if (!tch) + { + CCI * pkCCI = P2P_MANAGER::instance().Find(name); + + if (pkCCI) + { + TPacketGGBlockChat p; + + p.bHeader = HEADER_GG_BLOCK_CHAT; + strlcpy(p.szName, name, sizeof(p.szName)); + p.lBlockDuration = lBlockDuration; + P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGBlockChat)); + } + else + { + TPacketBlockChat p; + + strlcpy(p.szName, name, sizeof(p.szName)); + p.lDuration = lBlockDuration; + db_clientdesc->DBPacket(HEADER_GD_BLOCK_CHAT, ch ? ch->GetDesc()->GetHandle() : 0, &p, sizeof(p)); + + } + + if (ch) + ch->ChatPacket(CHAT_TYPE_INFO, "Chat block requested."); + + return; + } + + if (tch && ch != tch) + tch->AddAffect(AFFECT_BLOCK_CHAT, POINT_NONE, 0, AFF_NONE, lBlockDuration, 0, true); +} + +ACMD(do_block_chat) +{ + // GM ƴϰų block_chat_privilege ɾ Ұ + if (ch && (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0)) + { + ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("׷ ɾ ϴ")); + return; + } + + char arg1[256]; + argument = one_argument(argument, arg1, sizeof(arg1)); + + if (!*arg1) + { + if (ch) + ch->ChatPacket(CHAT_TYPE_INFO, "Usage: block_chat