Compare commits

...

23 Commits

Author SHA1 Message Date
Exynox 042414a499 Merge pull request 'encoding' (#20) from Tr0n/server:encoding into nightly
Reviewed-on: #20
2024-04-07 08:59:36 +03:00
Tr0n 68c7c4bc4f
fix config comment 2024-04-06 18:04:29 +02:00
Tr0n eafbee616f
english locale as base 2024-04-06 17:58:03 +02:00
Tr0n 026cb861a8
fix missing quote mark 2024-04-06 17:57:30 +02:00
Tr0n 1ba21fb1f9
translate remaining SPDLOG calls 2024-04-06 17:57:14 +02:00
Tr0n 8fede9a1df
manual translation of non-localized strings 2024-04-06 17:28:22 +02:00
Tr0n dfb3d08998
manual translation of non-localized strings 2024-04-06 12:50:34 +02:00
Tr0n 326c134f9e
manual translation of non-localized strings 2024-04-06 11:13:11 +02:00
Tr0n 9b7536ee9a
Initial pass of converting string literals to english 2024-04-06 10:31:54 +02:00
Tr0n 9056f6c6c6
convert some more comments to utf8 2024-04-06 10:24:46 +02:00
Tr0n 8685b02fdc
libthecore encoding conversion 2024-04-06 10:20:42 +02:00
Tr0n 341bef9aba
Merge branch 'nightly' into comment-encoding 2024-04-06 01:09:03 +02:00
sdgmt2 8a1acefeeb
move encoding tool to separate repo 2024-04-06 00:42:30 +02:00
sdgmt2 eacc808366
convert comments to utf-8 2024-04-05 23:34:45 +02:00
sdgmt2 4a1460f36e
convert korean characters in string literals to string encoded bytes 2024-04-05 22:24:46 +02:00
sdgmt2 aae9b169df
encoding fix script 2024-04-05 21:57:49 +02:00
sdgmt2 dd74eafc24
fix db cpp encoding 2024-04-01 14:20:00 +02:00
sdgmt2 d471d99a24
fix libthecore header encoding 2024-04-01 12:48:58 +02:00
sdgmt2 350fb0d424
fix libsql header encoding 2024-04-01 12:48:16 +02:00
sdgmt2 869720f9e6
fix libgame header encoding 2024-04-01 12:46:52 +02:00
sdgmt2 4fc612552a
fix common header encoding 2024-04-01 12:45:35 +02:00
sdgmt2 248205ae01
fix db header encoding 2024-04-01 12:43:31 +02:00
sdgmt2 82317e6c4d
fix game header encoding 2024-04-01 12:39:34 +02:00
224 changed files with 6073 additions and 6069 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cmake.configureOnOpen": true
}

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,15 @@
#define __HEADER_VNUM_HELPER__
/**
,
(=VNum)
,
(=VNum)
( ) .
( ) .
* PCH에
cpp파일에서 include .
* PCH에
cpp파일에서 include .
* cpp에서 ~ common에 . (game, db프로젝트 )
* cpp에서 ~ common에 . (game, db프로젝트 )
@date 2011. 8. 29.
*/
@ -19,35 +19,35 @@
class CItemVnumHelper
{
public:
/// 독일 DVD용 불사조 소환권
static const bool IsPhoenix(DWORD vnum) { return 53001 == vnum; } // NOTE: 불사조 소환 아이템은 53001 이지만 mob-vnum은 34001 입니다.
/// 독일 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용 불사조 몹 번호
/// 독일 DVD용 불사조 몹 번호
static bool IsPhoenix(DWORD vnum) { return 34001 == vnum; }
static bool IsIcePhoenix(DWORD vnum) { return 34003 == vnum; }
/// PetSystem이 관리하는 펫인가?
/// PetSystem이 관리하는 펫인가?
static bool IsPetUsingPetSystem(DWORD vnum) { return (IsPhoenix(vnum) || IsReindeerYoung(vnum)) || IsIcePhoenix(vnum); }
/// 2011년 크리스마스 이벤트용 펫 (아기 순록)
/// 2011년 크리스마스 이벤트용 펫 (아기 순록)
static bool IsReindeerYoung(DWORD vnum) { return 34002 == vnum; }
/// 라마단 이벤트 보상용 흑마(20119) .. 할로윈 이벤트용 라마단 흑마 클론(스펙은 같음, 20219)
/// 라마단 이벤트 보상용 흑마(20119) .. 할로윈 이벤트용 라마단 흑마 클론(스펙은 같음, 20219)
static bool IsRamadanBlackHorse(DWORD vnum) { return 20119 == vnum || 20219 == vnum || 22022 == vnum; }
};

View File

@ -26,7 +26,7 @@ public:
int get_price () { return offer_price; }
} TAuctionSimpleItemInfo;
// 각 auction 정보들.
// 각 auction 정보들.
// primary key (item_id)
typedef struct _auction : public _base_auction
{
@ -49,8 +49,8 @@ public:
empire = _empire;
}
// 이 메소드들은 어떤 변수가 auction에서 어떤 역할을 하는지 까먹을 까봐
// 만들어놓았다.
// 이 메소드들은 어떤 변수가 auction에서 어떤 역할을 하는지 까먹을 까봐
// 만들어놓았다.
// by rtsummit
DWORD get_item_id () { return item_id; }
DWORD get_bidder_id () { return bidder_id; }
@ -89,7 +89,7 @@ typedef struct _sale : public _base_auction
} TSaleItemInfo;
// wish는 실제하는 아이템은 없다.
// wish는 실제하는 아이템은 없다.
// primary key (item_num, wisher_id)
typedef struct _wish : public _base_auction
{
@ -118,9 +118,9 @@ enum AuctionCmd {OPEN_AUCTION, OPEN_WISH_AUCTION, OPEN_MY_AUCTION, OPEN_MY_WISH_
AUCTION_REBID, AUCTION_BID_CANCEL,
};
// 반드시 FAIL 앞에, 실패 류 들이 와야한다.
// 왜냐, <= AUCTION_FAIL 이런 CHECK을 할 거거든
// 반대로 SUCCESS 뒤에, 성공 류 들이 와야한다. 근데 성공류가 있긴 하려나...
// 반드시 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 };
@ -218,7 +218,7 @@ typedef struct command_auction
cmd = AUCTION_CHANGING_MONEY;
price1 = _money;
}
// bid랑 cmd만 다르다.
// bid랑 cmd만 다르다.
void rebid (DWORD _item_id, int _bidPrice)
{
cmd = AUCTION_REBID;
@ -322,7 +322,7 @@ typedef struct auction_impur : public command_auction
// auction_type;
// start_idx;
// size;
// conditions; 정렬은 승철님께 조언을 구해보자.ㅇㅇ
// conditions; 정렬은 승철님께 조언을 구해보자.ㅇㅇ
//}
//
//get_auction_detail_item_info

View File

@ -41,8 +41,8 @@ namespace building
int lNPCX;
int lNPCY;
DWORD dwGroupVnum; // 같은 그룹은 하나만 건설가능
DWORD dwDependOnGroupVnum; // 지어져 있어야하는 그룹
DWORD dwGroupVnum; // 같은 그룹은 하나만 건설가능
DWORD dwDependOnGroupVnum; // 지어져 있어야하는 그룹
} TObjectProto;
typedef struct SObject

View File

@ -31,8 +31,8 @@ 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,
@ -42,18 +42,18 @@ enum EItemUniqueSockets
enum EItemTypes
{
ITEM_NONE, //0
ITEM_WEAPON, //1//무기
ITEM_ARMOR, //2//갑옷
ITEM_USE, //3//아이템 사용
ITEM_WEAPON, //1//무기
ITEM_ARMOR, //2//갑옷
ITEM_USE, //3//아이템 사용
ITEM_AUTOUSE, //4
ITEM_MATERIAL, //5
ITEM_SPECIAL, //6 //스페셜 아이템
ITEM_SPECIAL, //6 //스페셜 아이템
ITEM_TOOL, //7
ITEM_LOTTERY, //8//복권
ITEM_ELK, //9//돈
ITEM_LOTTERY, //8//복권
ITEM_ELK, //9//돈
ITEM_METIN, //10
ITEM_CONTAINER, //11
ITEM_FISH, //12//낚시
ITEM_FISH, //12//낚시
ITEM_ROD, //13
ITEM_RESOURCE, //14
ITEM_CAMPFIRE, //15
@ -61,21 +61,21 @@ enum EItemTypes
ITEM_SKILLBOOK, //17
ITEM_QUEST, //18
ITEM_POLYMORPH, //19
ITEM_TREASURE_BOX, //20//보물상자
ITEM_TREASURE_KEY, //21//보물상자 열쇠
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 벨트
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
@ -111,8 +111,8 @@ enum EArmorSubTypes
enum ECostumeSubTypes
{
COSTUME_BODY = ARMOR_BODY, // [중요!!] ECostumeSubTypes enum value는 종류별로 EArmorSubTypes의 그것과 같아야 함.
COSTUME_HAIR = ARMOR_HEAD, // 이는 코스츔 아이템에 추가 속성을 붙이겠다는 사업부의 요청에 따라서 기존 로직을 활용하기 위함임.
COSTUME_BODY = ARMOR_BODY, // [중요!!] ECostumeSubTypes enum value는 종류별로 EArmorSubTypes의 그것과 같아야 함.
COSTUME_HAIR = ARMOR_HEAD, // 이는 코스츔 아이템에 추가 속성을 붙이겠다는 사업부의 요청에 따라서 기존 로직을 활용하기 위함임.
COSTUME_NUM_TYPES,
};
@ -215,8 +215,8 @@ enum EUseSubTypes
USE_UNBIND,
USE_TIME_CHARGE_PER,
USE_TIME_CHARGE_FIX, // 28
USE_PUT_INTO_BELT_SOCKET, // 29 벨트 소켓에 사용할 수 있는 아이템
USE_PUT_INTO_RING_SOCKET, // 30 반지 소켓에 사용할 수 있는 아이템 (유니크 반지 말고, 새로 추가된 반지 슬롯)
USE_PUT_INTO_BELT_SOCKET, // 29 벨트 소켓에 사용할 수 있는 아이템
USE_PUT_INTO_RING_SOCKET, // 30 반지 소켓에 사용할 수 있는 아이템 (유니크 반지 말고, 새로 추가된 반지 슬롯)
};
enum EExtractSubTypes
@ -270,7 +270,7 @@ enum EItemFlag
{
ITEM_FLAG_REFINEABLE = (1 << 0),
ITEM_FLAG_SAVE = (1 << 1),
ITEM_FLAG_STACKABLE = (1 << 2), // 여러개 합칠 수 있음
ITEM_FLAG_STACKABLE = (1 << 2), // 여러개 합칠 수 있음
ITEM_FLAG_COUNT_PER_1GOLD = (1 << 3),
ITEM_FLAG_SLOW_QUERY = (1 << 4),
ITEM_FLAG_UNUSED01 = (1 << 5), // UNUSED
@ -287,24 +287,24 @@ enum EItemFlag
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), // 창고에 넣을 수 없음
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
@ -337,16 +337,16 @@ enum ELimitTypes
// TODO: Remove this and re-check the validity of item_proto afterwards
LIMIT_PCBANG,
/// 착용 여부와 상관 없이 실시간으로 시간 차감 (socket0에 소멸 시간이 박힘: unix_timestamp 타입)
/// 착용 여부와 상관 없이 실시간으로 시간 차감 (socket0에 소멸 시간이 박힘: unix_timestamp 타입)
LIMIT_REAL_TIME,
/// 아이템을 맨 처음 사용(혹은 착용) 한 순간부터 리얼타임 타이머 시작
/// 최초 사용 전에는 socket0에 사용가능시간(초단위, 0이면 프로토의 limit value값 사용) 값이 쓰여있다가
/// 아이템 사용시 socket1에 사용 횟수가 박히고 socket0에 unix_timestamp 타입의 소멸시간이 박힘.
/// 아이템을 맨 처음 사용(혹은 착용) 한 순간부터 리얼타임 타이머 시작
/// 최초 사용 전에는 socket0에 사용가능시간(초단위, 0이면 프로토의 limit value값 사용) 값이 쓰여있다가
/// 아이템 사용시 socket1에 사용 횟수가 박히고 socket0에 unix_timestamp 타입의 소멸시간이 박힘.
LIMIT_REAL_TIME_START_FIRST_USE,
/// 아이템을 착용 중일 때만 사용 시간이 차감되는 아이템
/// socket0에 남은 시간이 초단위로 박힘. (아이템 최초 사용시 해당 값이 0이면 프로토의 limit value값을 socket0에 복사)
/// 아이템을 착용 중일 때만 사용 시간이 차감되는 아이템
/// socket0에 남은 시간이 초단위로 박힘. (아이템 최초 사용시 해당 값이 0이면 프로토의 limit value값을 socket0에 복사)
LIMIT_TIMER_BASED_ON_WEAR,
LIMIT_MAX_NUM

View File

@ -21,10 +21,10 @@ enum EMisc
GUILD_NAME_MAX_LEN = 12,
SHOP_HOST_ITEM_MAX_NUM = 40, /* 호스트의 최대 아이템 개수 */
SHOP_GUEST_ITEM_MAX_NUM = 18, /* 게스트의 최대 아이템 개수 */
SHOP_HOST_ITEM_MAX_NUM = 40, /* 호스트의 최대 아이템 개수 */
SHOP_GUEST_ITEM_MAX_NUM = 18, /* 게스트의 최대 아이템 개수 */
SHOP_PRICELIST_MAX_NUM = 40, ///< 개인상점 가격정보 리스트에서 유지할 가격정보의 최대 갯수
SHOP_PRICELIST_MAX_NUM = 40, ///< 개인상점 가격정보 리스트에서 유지할 가격정보의 최대 갯수
CHAT_MAX_LEN = 512,
@ -78,19 +78,19 @@ enum EMisc
/**
**** (DB상 Item Position) ****
**** (DB상 Item Position) ****
+------------------------------------------------------+ 0
| (45 * 2) 90 |
| (45 * 2) 90 |
+------------------------------------------------------+ 90 = INVENTORY_MAX_NUM(90)
| ( ) 32 |
| ( ) 32 |
+------------------------------------------------------+ 122 = INVENTORY_MAX_NUM(90) + WEAR_MAX_NUM(32)
| ( ) 12 |
| ( ) 12 |
+------------------------------------------------------+ 134 = 122 + DS_SLOT_MAX(6) * DRAGON_SOUL_DECK_MAX_NUM(2)
| ( ) 18 |
| ( ) 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
| |
| |
+------------------------------------------------------+ ??
*/
};
@ -125,10 +125,10 @@ enum EWearPositions
WEAR_COSTUME_BODY, // 19
WEAR_COSTUME_HAIR, // 20
WEAR_RING1, // 21 : 신규 반지슬롯1 (왼쪽)
WEAR_RING2, // 22 : 신규 반지슬롯2 (오른쪽)
WEAR_RING1, // 21 : 신규 반지슬롯1 (왼쪽)
WEAR_RING2, // 22 : 신규 반지슬롯2 (오른쪽)
WEAR_BELT, // 23 : 신규 벨트슬롯
WEAR_BELT, // 23 : 신규 벨트슬롯
WEAR_MAX = 32 //
};
@ -139,7 +139,7 @@ enum EDragonSoulDeckType
DRAGON_SOUL_DECK_1,
DRAGON_SOUL_DECK_MAX_NUM = 2,
DRAGON_SOUL_DECK_RESERVED_MAX_NUM = 3, // NOTE: 중요! 아직 사용중이진 않지만, 3페이지 분량을 예약 해 둠. DS DECK을 늘릴 경우 반드시 그 수만큼 RESERVED에서 차감해야 함!
DRAGON_SOUL_DECK_RESERVED_MAX_NUM = 3, // NOTE: 중요! 아직 사용중이진 않지만, 3페이지 분량을 예약 해 둠. DS DECK을 늘릴 경우 반드시 그 수만큼 RESERVED에서 차감해야 함!
};
enum ESex
@ -161,7 +161,7 @@ enum EDirection
DIR_MAX_NUM
};
#define ABILITY_MAX_LEVEL 10 /* 기술 최대 레벨 */
#define ABILITY_MAX_LEVEL 10 /* 기술 최대 레벨 */
enum EAbilityDifficulty
{
@ -174,9 +174,9 @@ enum EAbilityDifficulty
enum EAbilityCategory
{
CATEGORY_PHYSICAL, /* 신체적 어빌리티 */
CATEGORY_MENTAL, /* 정신적 어빌리티 */
CATEGORY_ATTRIBUTE, /* 능력 어빌리티 */
CATEGORY_PHYSICAL, /* 신체적 어빌리티 */
CATEGORY_MENTAL, /* 정신적 어빌리티 */
CATEGORY_ATTRIBUTE, /* 능력 어빌리티 */
CATEGORY_NUM_TYPES
};
@ -246,13 +246,13 @@ enum EParts
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_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,
@ -395,38 +395,38 @@ enum EApplyTypes
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_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_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_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_RESIST_ICE, // 87 냉기 저항
APPLY_RESIST_EARTH, // 88 대지 저항
APPLY_RESIST_DARK, // 89 어둠 저항
APPLY_ANTI_CRITICAL_PCT, //90 크리티컬 저항
APPLY_ANTI_PENETRATE_PCT, //91 관통타격 저항
APPLY_ANTI_CRITICAL_PCT, //90 크리티컬 저항
APPLY_ANTI_PENETRATE_PCT, //91 관통타격 저항
MAX_APPLY_NUM, //
@ -578,7 +578,7 @@ enum EGuildWarState
GUILD_WAR_OVER,
GUILD_WAR_RESERVE,
GUILD_WAR_DURATION = 30*60, // 1시간
GUILD_WAR_DURATION = 30*60, // 1시간
GUILD_WAR_WIN_POINT = 1000,
GUILD_WAR_LADDER_HALF_PENALTY_TIME = 12*60*60,
};
@ -622,13 +622,13 @@ enum EMoneyLogType
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_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
};
@ -658,10 +658,10 @@ enum SPECIAL_EFFECT
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) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
SE_EQUIP_RAMADAN_RING, // 라마단 초승달의 반지(71135) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
SE_EQUIP_HALLOWEEN_CANDY, // 할로윈 사탕을 착용(-_-;)한 순간에 발동하는 이펙트
SE_EQUIP_HAPPINESS_RING, // 크리스마스 행복의 반지(71143) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
SE_EQUIP_LOVE_PENDANT, // 발렌타인 사랑의 팬던트(71145) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
} ;
enum ETeenFlags
@ -676,10 +676,10 @@ enum ETeenFlags
#include "item_length.h"
// inventory의 position을 나타내는 구조체
// int와의 암시적 형변환이 있는 이유는,
// 인벤 관련된 모든 함수가 window_type은 받지 않고, cell 하나만 받았기 때문에,(기존에는 인벤이 하나 뿐이어서 inventory type이란게 필요없었기 때문에,)
// 인벤 관련 모든 함수 호출부분을 수정하는 것이 난감하기 ??문이다.
// inventory의 position을 나타내는 구조체
// int와의 암시적 형변환이 있는 이유는,
// 인벤 관련된 모든 함수가 window_type은 받지 않고, cell 하나만 받았기 때문에,(기존에는 인벤이 하나 뿐이어서 inventory type이란게 필요없었기 때문에,)
// 인벤 관련 모든 함수 호출부분을 수정하는 것이 난감하기 ??문이다.
enum EDragonSoulRefineWindowSize
{
@ -728,7 +728,7 @@ typedef struct SItemPos
return cell < INVENTORY_AND_EQUIP_SLOT_MAX;
case DRAGON_SOUL_INVENTORY:
return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM);
// 동적으로 크기가 정해지는 window는 valid 체크를 할 수가 없다.
// 동적으로 크기가 정해지는 window는 valid 체크를 할 수가 없다.
case SAFEBOX:
case MALL:
return false;

View File

@ -6,7 +6,7 @@
typedef DWORD IDENT;
/**
* @version 05/06/10 Bang2ni - Myshop Pricelist HEADER_XX_MYSHOP_PRICELIST_XXX
* @version 05/06/10 Bang2ni - Myshop Pricelist HEADER_XX_MYSHOP_PRICELIST_XXX
*/
enum
{
@ -99,22 +99,22 @@ enum
HEADER_GD_LOGIN_BY_KEY = 101,
HEADER_GD_MALL_LOAD = 107,
HEADER_GD_MYSHOP_PRICELIST_UPDATE = 108, ///< 가격정보 갱신 요청
HEADER_GD_MYSHOP_PRICELIST_REQ = 109, ///< 가격정보 리스트 요청
HEADER_GD_MYSHOP_PRICELIST_UPDATE = 108, ///< 가격정보 갱신 요청
HEADER_GD_MYSHOP_PRICELIST_REQ = 109, ///< 가격정보 리스트 요청
HEADER_GD_BLOCK_CHAT = 110,
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_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,
@ -126,7 +126,7 @@ enum
HEADER_GD_UPDATE_HORSE_NAME = 131,
HEADER_GD_REQ_HORSE_NAME = 132,
HEADER_GD_DC = 133, // Login Key를 지움
HEADER_GD_DC = 133, // Login Key를 지움
HEADER_GD_VALID_LOGOUT = 134,
@ -238,17 +238,17 @@ enum
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_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,
@ -343,7 +343,7 @@ typedef struct SPlayerItem
DWORD count;
DWORD vnum;
LONG alSockets[ITEM_SOCKET_MAX_NUM]; // 소켓번호
LONG alSockets[ITEM_SOCKET_MAX_NUM]; // 소켓번호
TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM];
@ -550,9 +550,9 @@ 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 상점에만 이용, 보일 위치.
TItemPos pos; // PC 상점에만 이용
DWORD price; // PC, shop_table_ex.txt 상점에만 이용
BYTE display_pos; // PC, shop_table_ex.txt 상점에만 이용, 보일 위치.
} TShopItemTable;
typedef struct SShopTable
@ -616,12 +616,12 @@ typedef struct SItemTable : public SEntityTable
BYTE bSpecular;
BYTE bGainSocketPct;
WORD sAddonType; // 기본 속성
WORD 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)
// 아래 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;
@ -659,7 +659,7 @@ typedef struct SPlayerLoadPacket
{
DWORD account_id;
DWORD player_id;
BYTE account_index; /* account 에서의 위치 */
BYTE account_index; /* account 에서의 위치 */
} TPlayerLoadPacket;
typedef struct SPlayerCreatePacket
@ -736,9 +736,9 @@ typedef struct SEmpireSelectPacket
typedef struct SPacketGDSetup
{
char szPublicIP[16]; // Public IP which listen to users
BYTE bChannel; // 채널
WORD wListenPort; // 클라이언트가 접속하는 포트 번호
WORD wP2PPort; // 서버끼리 연결 시키는 P2P 포트 번호
BYTE bChannel; // 채널
WORD wListenPort; // 클라이언트가 접속하는 포트 번호
WORD wP2PPort; // 서버끼리 연결 시키는 P2P 포트 번호
LONG alMaps[MAP_ALLOW_MAX_LEN];
DWORD dwLoginCount;
BYTE bAuthServer;
@ -908,8 +908,8 @@ typedef struct SPacketGuildWar
LONG lInitialScore;
} TPacketGuildWar;
// Game -> DB : 상대적 변화값
// DB -> Game : 토탈된 최종값
// Game -> DB : 상대적 변화값
// DB -> Game : 토탈된 최종값
typedef struct SPacketGuildWarScore
{
DWORD dwGuildGainPoint;
@ -930,8 +930,8 @@ typedef struct SRefineTable
//DWORD result_vnum;
DWORD id;
BYTE material_count;
DWORD cost; // 소요 비용
DWORD prob; // 확률
DWORD cost; // 소요 비용
DWORD prob; // 확률
TRefineMaterial materials[REFINE_MATERIAL_MAX_NUM];
} TRefineTable;
@ -1006,14 +1006,14 @@ typedef struct SPacketGDLoginByKey
} TPacketGDLoginByKey;
/**
* @version 05/06/08 Bang2ni -
* @version 05/06/08 Bang2ni -
*/
typedef struct SPacketGiveGuildPriv
{
BYTE type;
DWORD value;
DWORD guild_id;
time_t duration_sec; ///< 지속시간
time_t duration_sec; ///< 지속시간
} TPacketGiveGuildPriv;
typedef struct SPacketGiveEmpirePriv
{
@ -1048,7 +1048,7 @@ typedef struct SPacketDGChangeCharacterPriv
} TPacketDGChangeCharacterPriv;
/**
* @version 05/06/08 Bang2ni -
* @version 05/06/08 Bang2ni -
*/
typedef struct SPacketDGChangeGuildPriv
{
@ -1056,7 +1056,7 @@ typedef struct SPacketDGChangeGuildPriv
DWORD value;
DWORD guild_id;
BYTE bLog;
time_t end_time_sec; ///< 지속시간
time_t end_time_sec; ///< 지속시간
} TPacketDGChangeGuildPriv;
typedef struct SPacketDGChangeEmpirePriv
@ -1208,27 +1208,27 @@ typedef struct
DWORD dwPID2;
} TPacketWeddingEnd;
/// 개인상점 가격정보의 헤더. 가변 패킷으로 이 뒤에 byCount 만큼의 TItemPriceInfo 가 온다.
/// 개인상점 가격정보의 헤더. 가변 패킷으로 이 뒤에 byCount 만큼의 TItemPriceInfo 가 온다.
typedef struct SPacketMyshopPricelistHeader
{
DWORD dwOwnerID; ///< 가격정보를 가진 플레이어 ID
BYTE byCount; ///< 가격정보 갯수
DWORD dwOwnerID; ///< 가격정보를 가진 플레이어 ID
BYTE byCount; ///< 가격정보 갯수
} TPacketMyshopPricelistHeader;
/// 개인상점의 단일 아이템에 대한 가격정보
/// 개인상점의 단일 아이템에 대한 가격정보
typedef struct SItemPriceInfo
{
DWORD dwVnum; ///< 아이템 vnum
DWORD dwPrice; ///< 가격
DWORD dwVnum; ///< 아이템 vnum
DWORD dwPrice; ///< 가격
} TItemPriceInfo;
/// 개인상점 아이템 가격정보 리스트 테이블
/// 개인상점 아이템 가격정보 리스트 테이블
typedef struct SItemPriceListTable
{
DWORD dwOwnerID; ///< 가격정보를 가진 플레이어 ID
BYTE byCount; ///< 가격정보 리스트의 갯수
DWORD dwOwnerID; ///< 가격정보를 가진 플레이어 ID
BYTE byCount; ///< 가격정보 리스트의 갯수
TItemPriceInfo aPriceInfo[SHOP_PRICELIST_MAX_NUM]; ///< 가격정보 리스트
TItemPriceInfo aPriceInfo[SHOP_PRICELIST_MAX_NUM]; ///< 가격정보 리스트
} TItemPriceListTable;
typedef struct
@ -1240,12 +1240,12 @@ typedef struct
//ADMIN_MANAGER
typedef struct TAdminInfo
{
DWORD m_ID; //고유ID
char m_szAccount[32]; //계정
char m_szName[32]; //캐릭터이름
char m_szContactIP[16]; //접근아이피
char m_szServerIP[16]; //서버아이피
DWORD m_Authority; //권한
DWORD m_ID; //고유ID
char m_szAccount[32]; //계정
char m_szName[32]; //캐릭터이름
char m_szContactIP[16]; //접근아이피
char m_szServerIP[16]; //서버아이피
DWORD m_Authority; //권한
} tAdminInfo;
//END_ADMIN_MANAGER
@ -1266,20 +1266,20 @@ typedef struct SPacketReloadAdmin
typedef struct TMonarchInfo
{
DWORD pid[4]; // 군주의 PID
int64_t money[4]; // 군주의 별개 돈
char name[4][32]; // 군주의 이름
char date[4][32]; // 군주 등록 날짜
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]; // 투표 날짜
DWORD pid; // 투표 한사람 PID
DWORD selectedpid; // 투표 당한 PID ( 군주 참가자 )
char date[32]; // 투표 날짜
} MonarchElectionInfo;
// 군주 출마자
// 군주 출마자
typedef struct tMonarchCandidacy
{
DWORD pid;
@ -1331,14 +1331,14 @@ typedef struct tNeedLoginLogInfo
DWORD dwPlayerID;
} TPacketNeedLoginLogInfo;
//독일 선물 알림 기능 테스트용 패킷 정보
//독일 선물 알림 기능 테스트용 패킷 정보
typedef struct tItemAwardInformer
{
char login[LOGIN_MAX_LEN + 1];
char command[20]; //명령어
DWORD vnum; //아이템
char command[20]; //명령어
DWORD vnum; //아이템
} TPacketItemAwardInfromer;
// 선물 알림 기능 삭제용 패킷 정보
// 선물 알림 기능 삭제용 패킷 정보
typedef struct tDeleteAwardID
{
DWORD dwID;

View File

@ -135,7 +135,7 @@ void AuctionManager::LoadAuctionItem()
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
return;
}
@ -182,7 +182,7 @@ void AuctionManager::LoadAuctionInfo()
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
return;
}
@ -226,7 +226,7 @@ void AuctionManager::LoadSaleInfo()
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
return;
}
@ -269,7 +269,7 @@ void AuctionManager::LoadWishInfo()
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
return;
}
@ -311,7 +311,7 @@ void AuctionManager::LoadMyBidInfo ()
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
return;
}
@ -518,7 +518,7 @@ AuctionResult AuctionManager::Impur(DWORD purchaser_id, const char* purchaser_na
return AUCTION_EXPIRED;
}
// 즉구 해버렸으므로, 경매는 끝났다.
// 즉구 해버렸으므로, 경매는 끝났다.
item_info->expired_time = 0;
item_info->bidder_id = purchaser_id;
item_info->set_bidder_name (purchaser_name);

View File

@ -243,7 +243,7 @@ private:
TItemInfoCacheMap item_cache_map;
};
// pc가 입찰에 참여했던 경매를 관리.
// pc가 입찰에 참여했던 경매를 관리.
class MyBidBoard
{
public:
@ -255,7 +255,7 @@ public:
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:
@ -267,11 +267,11 @@ private:
class AuctionManager : public singleton <AuctionManager>
{
private:
// auction에 등록된 아이템들.
// auction에 등록된 아이템들.
typedef std::unordered_map<DWORD, CItemCache *> TItemCacheMap;
TItemCacheMap auction_item_cache_map;
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
AuctionBoard Auction;
SaleBoard Sale;
WishBoard Wish;

View File

@ -29,12 +29,12 @@ CItemCache::~CItemCache()
{
}
// 이거 이상한데...
// Delete를 했으면, Cache도 해제해야 하는것 아닌가???
// 근데 Cache를 해제하는 부분이 없어.
// 못 찾은 건가?
// 이렇게 해놓으면, 계속 시간이 될 때마다 아이템을 계속 지워...
// 이미 사라진 아이템인데... 확인사살??????
// 이거 이상한데...
// Delete를 했으면, Cache도 해제해야 하는것 아닌가???
// 근데 Cache를 해제하는 부분이 없어.
// 못 찾은 건가?
// 이렇게 해놓으면, 계속 시간이 될 때마다 아이템을 계속 지워...
// 이미 사라진 아이템인데... 확인사살??????
// fixme
// by rtsummit
void CItemCache::Delete()
@ -52,12 +52,12 @@ void CItemCache::Delete()
OnFlush();
//m_bNeedQuery = false;
//m_lastUpdateTime = time(0) - m_expireTime; // 바로 타임아웃 되도록 하자.
//m_lastUpdateTime = time(0) - m_expireTime; // 바로 타임아웃 되도록 하자.
}
void CItemCache::OnFlush()
{
if (m_data.vnum == 0) // vnum이 0이면 삭제하라고 표시된 것이다.
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);
@ -186,7 +186,7 @@ CItemPriceListTableCache::CItemPriceListTableCache()
void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList)
{
//
// 이미 캐싱된 아이템과 중복된 아이템을 찾고 중복되지 않는 이전 정보는 tmpvec 에 넣는다.
// 이미 캐싱된 아이템과 중복된 아이템을 찾고 중복되지 않는 이전 정보는 tmpvec 에 넣는다.
//
std::vector<TItemPriceInfo> tmpvec;
@ -202,7 +202,7 @@ void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList
}
//
// pUpdateList 를 m_data 에 복사하고 남은 공간을 tmpvec 의 앞에서 부터 남은 만큼 복사한다.
// pUpdateList 를 m_data 에 복사하고 남은 공간을 tmpvec 의 앞에서 부터 남은 만큼 복사한다.
//
if (pUpdateList->byCount > SHOP_PRICELIST_MAX_NUM)
@ -215,7 +215,7 @@ void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList
memcpy(m_data.aPriceInfo, pUpdateList->aPriceInfo, sizeof(TItemPriceInfo) * pUpdateList->byCount);
int nDeletedNum; // 삭제된 가격정보의 갯수
int nDeletedNum; // 삭제된 가격정보의 갯수
if (pUpdateList->byCount < SHOP_PRICELIST_MAX_NUM)
{
@ -244,14 +244,14 @@ void CItemPriceListTableCache::OnFlush()
char szQuery[QUERY_MAX_LEN];
//
// 이 캐시의 소유자에 대한 기존에 DB 에 저장된 아이템 가격정보를 모두 삭제한다.
// 이 캐시의 소유자에 대한 기존에 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);
//
// 캐시의 내용을 모두 DB 에 쓴다.
// 캐시의 내용을 모두 DB 에 쓴다.
//
for (int idx = 0; idx < m_data.byCount; ++idx)

View File

@ -29,7 +29,7 @@ class CPlayerTableCache : public cache<TPlayerTable>
// MYSHOP_PRICE_LIST
/**
* @class CItemPriceListTableCache
* @brief class
* @brief class
* @version 05/06/10 Bang2ni - First release.
*/
class CItemPriceListTableCache : public cache< TItemPriceListTable >
@ -38,20 +38,20 @@ class CItemPriceListTableCache : public cache< TItemPriceListTable >
/// Constructor
/**
* .
* .
*/
CItemPriceListTableCache(void);
/// 리스트 갱신
/// 리스트 갱신
/**
* @param [in] pUpdateList
* @param [in] pUpdateList
*
* .
* .
* .
* .
*/
void UpdateList(const TItemPriceListTable* pUpdateList);
/// 가격정보를 DB 에 기록한다.
/// 가격정보를 DB 에 기록한다.
virtual void OnFlush(void);
private:

View File

@ -246,7 +246,7 @@ bool CClientManager::Initialize()
LoadEventFlag();
// database character-set을 강제로 맞춤
// database character-set을 강제로 맞춤
if (g_stLocale == "big5" || g_stLocale == "sjis")
CDBManager::instance().QueryLocaleSet();
@ -259,7 +259,7 @@ void CClientManager::MainLoop()
SPDLOG_DEBUG("ClientManager pointer is {}", (void*) this);
// 메인루프
// 메인루프
while (!m_bShutdowned)
{
while ((tmp = CDBManager::instance().PopResult()))
@ -273,7 +273,7 @@ void CClientManager::MainLoop()
}
//
// 메인루프 종료처리
// 메인루프 종료처리
//
SPDLOG_DEBUG("MainLoop exited, Starting cache flushing");
@ -281,7 +281,7 @@ void CClientManager::MainLoop()
itertype(m_map_playerCache) it = m_map_playerCache.begin();
//플레이어 테이블 캐쉬 플러쉬
//플레이어 테이블 캐쉬 플러쉬
while (it != m_map_playerCache.end())
{
CPlayerTableCache * c = (it++)->second;
@ -293,7 +293,7 @@ void CClientManager::MainLoop()
itertype(m_map_itemCache) it2 = m_map_itemCache.begin();
//아이템 플러쉬
//아이템 플러쉬
while (it2 != m_map_itemCache.end())
{
CItemCache * c = (it2++)->second;
@ -305,7 +305,7 @@ void CClientManager::MainLoop()
// MYSHOP_PRICE_LIST
//
// 개인상점 아이템 가격 리스트 Flush
// 개인상점 아이템 가격 리스트 Flush
//
for (itertype(m_mapItemPriceListCache) itPriceList = m_mapItemPriceListCache.begin(); itPriceList != m_mapItemPriceListCache.end(); ++itPriceList)
{
@ -325,7 +325,7 @@ void CClientManager::Quit()
void CClientManager::QUERY_BOOT(CPeer* peer, TPacketGDBoot * p)
{
const BYTE bPacketVersion = 6; // BOOT 패킷이 바뀔때마다 번호를 올리도록 한다.
const BYTE bPacketVersion = 6; // BOOT 패킷이 바뀔때마다 번호를 올리도록 한다.
std::vector<tAdminInfo> vAdmin;
std::vector<std::string> vHost;
@ -582,9 +582,9 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData;
DWORD dwHandle = pi->dwHandle;
// 여기에서 사용하는 account_index는 쿼리 순서를 말한다.
// 첫번째 패스워드 알아내기 위해 하는 쿼리가 0
// 두번째 실제 데이터를 얻어놓는 쿼리가 1
// 여기에서 사용하는 account_index는 쿼리 순서를 말한다.
// 첫번째 패스워드 알아내기 위해 하는 쿼리가 0
// 두번째 실제 데이터를 얻어놓는 쿼리가 1
if (pi->account_index == 0)
{
@ -609,7 +609,7 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
{
MYSQL_ROW row = mysql_fetch_row(res->pSQLResult);
// 비밀번호가 틀리면..
// 비밀번호가 틀리면..
if (((!row[2] || !*row[2]) && strcmp("000000", szSafeboxPassword)) ||
((row[2] && *row[2]) && strcmp(row[2], szSafeboxPassword)))
{
@ -675,8 +675,8 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
}
// 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼
// 보이기 때문에 창고가 아얘 안열리는게 나음
// 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼
// 보이기 때문에 창고가 아얘 안열리는게 나음
if (!msg->Get()->pSQLResult)
{
SPDLOG_ERROR("null safebox result");
@ -785,8 +785,8 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
{
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)
@ -898,7 +898,7 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
void CClientManager::QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p)
{
ClientHandleInfo * pi = new ClientHandleInfo(dwHandle);
pi->account_index = p->bSize; // account_index를 사이즈로 임시로 사용
pi->account_index = p->bSize; // account_index를 사이즈로 임시로 사용
char szQuery[QUERY_MAX_LEN];
@ -986,7 +986,7 @@ void CClientManager::RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg)
TItemPricelistReqInfo* pReqInfo = (TItemPricelistReqInfo*)static_cast<CQueryInfo*>(pMsg->pvUserData)->pvData;
//
// DB 에서 로드한 정보를 Cache 에 저장
// DB 에서 로드한 정보를 Cache 에 저장
//
TItemPriceListTable table;
@ -1005,7 +1005,7 @@ void CClientManager::RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg)
PutItemPriceListCache(&table);
//
// 로드한 데이터를 Game server 에 전송
// 로드한 데이터를 Game server 에 전송
//
TPacketMyshopPricelistHeader header;
@ -1029,7 +1029,7 @@ void CClientManager::RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg)
TItemPriceListTable* pUpdateTable = (TItemPriceListTable*)static_cast<CQueryInfo*>(pMsg->pvUserData)->pvData;
//
// DB 에서 로드한 정보를 Cache 에 저장
// DB 에서 로드한 정보를 Cache 에 저장
//
TItemPriceListTable table;
@ -1091,18 +1091,18 @@ void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpire
UINT g_start_map[4] =
{
0, // reserved
1, // 신수국
21, // 천조국
41 // 진노국
1, // 신수국
21, // 천조국
41 // 진노국
};
// FIXME share with game
DWORD g_start_position[4][2]=
{
{ 0, 0 },
{ 469300, 964200 }, // 신수국
{ 55700, 157900 }, // 천조국
{ 969600, 278400 } // 진노국
{ 469300, 964200 }, // 신수국
{ 55700, 157900 }, // 천조국
{ 969600, 278400 } // 진노국
};
for (int i = 0; i < 3; ++i)
@ -1152,7 +1152,7 @@ void CClientManager::QUERY_SETUP(CPeer * peer, DWORD dwHandle, const char * c_pD
peer->SetMaps(p->alMaps);
//
// 어떤 맵이 어떤 서버에 있는지 보내기
// 어떤 맵이 어떤 서버에 있는지 보내기
//
TMapLocation kMapLocations;
@ -1259,7 +1259,7 @@ void CClientManager::QUERY_SETUP(CPeer * peer, DWORD dwHandle, const char * c_pD
peer->Encode(&vec_kMapLocations[0], sizeof(TMapLocation) * vec_kMapLocations.size());
//
// 셋업 : 접속한 피어에 다른 피어들이 접속하게 만든다. (P2P 컨넥션 생성)
// 셋업 : 접속한 피어에 다른 피어들이 접속하게 만든다. (P2P 컨넥션 생성)
//
SPDLOG_DEBUG("SETUP: channel {} listen {} p2p {} count {}", peer->GetChannel(), p->wListenPort, p->wP2PPort, bMapCount);
@ -1275,7 +1275,7 @@ void CClientManager::QUERY_SETUP(CPeer * peer, DWORD dwHandle, const char * c_pD
if (tmp == peer)
continue;
// 채널이 0이라면 아직 SETUP 패킷이 오지 않은 피어 또는 auth라고 간주할 수 있음
// 채널이 0이라면 아직 SETUP 패킷이 오지 않은 피어 또는 auth라고 간주할 수 있음
if (0 == tmp->GetChannel())
continue;
@ -1337,8 +1337,8 @@ void CClientManager::QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData)
{
TPlayerItem * p = (TPlayerItem *) c_pData;
// 창고면 캐쉬하지 않고, 캐쉬에 있던 것도 빼버려야 한다.
// auction은 이 루트를 타지 않아야 한다. EnrollInAuction을 타야한다.
// 창고면 캐쉬하지 않고, 캐쉬에 있던 것도 빼버려야 한다.
// auction은 이 루트를 타지 않아야 한다. EnrollInAuction을 타야한다.
if (p->window == SAFEBOX || p->window == MALL)
{
@ -1473,7 +1473,7 @@ void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery)
c = GetItemCache(pNew->id);
// 아이템 새로 생성
// 아이템 새로 생성
if (!c)
{
SPDLOG_TRACE("ITEM_CACHE: PutItemCache ==> New CItemCache id{} vnum{} new owner{}", pNew->id, pNew->vnum, pNew->owner);
@ -1481,15 +1481,15 @@ void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery)
c = new CItemCache;
m_map_itemCache.insert(TItemCacheMap::value_type(pNew->id, c));
}
// 있을시
// 있을시
else
{
SPDLOG_TRACE("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())
@ -1500,7 +1500,7 @@ void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery)
}
}
// 새로운 정보 업데이트
// 새로운 정보 업데이트
c->Put(pNew, bSkipQuery);
TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(c->Get()->owner);
@ -1512,8 +1512,8 @@ void CClientManager::PutItemCache(TPlayerItem * pNew, bool bSkipQuery)
}
else
{
// 현재 소유자가 없으므로 바로 저장해야 다음 접속이 올 때 SQL에 쿼리하여
// 받을 수 있으므로 바로 저장한다.
// 현재 소유자가 없으므로 바로 저장해야 다음 접속이 올 때 SQL에 쿼리하여
// 받을 수 있으므로 바로 저장한다.
SPDLOG_TRACE("ITEM_CACHE: direct save {} id {}", c->Get()->owner, c->Get()->id);
c->OnFlush();
@ -1569,7 +1569,7 @@ void CClientManager::UpdatePlayerCache()
c->Flush();
// Item Cache도 업데이트
// Item Cache도 업데이트
UpdateItemCacheSet(c->Get()->id);
}
else if (c->CheckFlushTimeout())
@ -1595,7 +1595,7 @@ void CClientManager::UpdateItemCache()
{
CItemCache * c = (it++)->second;
// 아이템은 Flush만 한다.
// 아이템은 Flush만 한다.
if (c->CheckFlushTimeout())
{
SPDLOG_TRACE("UpdateItemCache ==> Flush() vnum {} id owner {}", c->Get()->vnum, c->Get()->id, c->Get()->owner);
@ -1640,7 +1640,7 @@ void CClientManager::QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData)
SPDLOG_TRACE("HEADER_GD_ITEM_DESTROY: PID {} ID {}", dwPID, dwID);
if (dwPID == 0) // 아무도 가진 사람이 없었다면, 비동기 쿼리
if (dwPID == 0) // 아무도 가진 사람이 없었다면, 비동기 쿼리
CDBManager::instance().AsyncQuery(szQuery);
else
CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, pkPeer->GetHandle(), NULL);
@ -1702,7 +1702,7 @@ void CClientManager::QUERY_RELOAD_PROTO()
// ADD_GUILD_PRIV_TIME
/**
* @version 05/06/08 Bang2ni -
* @version 05/06/08 Bang2ni -
*/
void CClientManager::AddGuildPriv(TPacketGiveGuildPriv* p)
{
@ -2010,8 +2010,8 @@ void CClientManager::WeddingEnd(TPacketWeddingEnd * p)
}
//
// 캐시에 가격정보가 있으면 캐시를 업데이트 하고 캐시에 가격정보가 없다면
// 우선 기존의 데이터를 로드한 뒤에 기존의 정보로 캐시를 만들고 새로 받은 가격정보를 업데이트 한다.
// 캐시에 가격정보가 있으면 캐시를 업데이트 하고 캐시에 가격정보가 없다면
// 우선 기존의 데이터를 로드한 뒤에 기존의 정보로 캐시를 만들고 새로 받은 가격정보를 업데이트 한다.
//
void CClientManager::MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* pPacket)
{
@ -2052,7 +2052,7 @@ void CClientManager::MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* p
}
// MYSHOP_PRICE_LIST
// 캐시된 가격정보가 있으면 캐시를 읽어 바로 전송하고 캐시에 정보가 없으면 DB 에 쿼리를 한다.
// 캐시된 가격정보가 있으면 캐시를 읽어 바로 전송하고 캐시에 정보가 없으면 DB 에 쿼리를 한다.
//
void CClientManager::MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID)
{
@ -2425,15 +2425,15 @@ void CClientManager::ProcessPackets(CPeer * peer)
ComeToVote(peer, dwHandle, data);
break;
case HEADER_GD_RMCANDIDACY: //< 후보 제거 (운영자)
case HEADER_GD_RMCANDIDACY: //< 후보 제거 (운영자)
RMCandidacy(peer, dwHandle, data);
break;
case HEADER_GD_SETMONARCH: ///<군주설정 (운영자)
case HEADER_GD_SETMONARCH: ///<군주설정 (운영자)
SetMonarch(peer, dwHandle, data);
break;
case HEADER_GD_RMMONARCH: ///<군주삭제
case HEADER_GD_RMMONARCH: ///<군주삭제
RMMonarch(peer, dwHandle, data);
break;
//END_MONARCH
@ -2622,9 +2622,9 @@ CPeer * CClientManager::GetAnyPeer()
return m_peerList.front();
}
// DB 매니저로 부터 받은 결과를 처리한다.
// DB 매니저로 부터 받은 결과를 처리한다.
//
// @version 05/06/10 Bang2ni - 가격정보 관련 쿼리(QID_ITEMPRICE_XXX) 추가
// @version 05/06/10 Bang2ni - 가격정보 관련 쿼리(QID_ITEMPRICE_XXX) 추가
int CClientManager::AnalyzeQueryResult(SQLMsg * msg)
{
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
@ -2737,7 +2737,7 @@ void UsageLog()
char *time_s;
struct tm lt;
int avg = g_dwUsageAvg / 3600; // 60 초 * 60 분
int avg = g_dwUsageAvg / 3600; // 60 초 * 60 분
fp = fopen("usage.txt", "a+");
@ -2770,7 +2770,7 @@ int CClientManager::Process()
++thecore_heart->pulse;
/*
//30분마다 변경
//30분마다 변경
if (((thecore_pulse() % (60 * 30 * 10)) == 0))
{
g_iPlayerCacheFlushSeconds = std::max(60, rand() % 180);
@ -2848,11 +2848,11 @@ int CClientManager::Process()
m_iCacheFlushCount = 0;
//플레이어 플러쉬
//플레이어 플러쉬
UpdatePlayerCache();
//아이템 플러쉬
//아이템 플러쉬
UpdateItemCache();
//로그아웃시 처리- 캐쉬셋 플러쉬
//로그아웃시 처리- 캐쉬셋 플러쉬
UpdateLogoutPlayer();
// MYSHOP_PRICE_LIST
@ -2922,13 +2922,13 @@ int CClientManager::Process()
/////////////////////////////////////////////////////////////////
}
if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 60))) // 60초에 한번
if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 60))) // 60초에 한번
{
// 유니크 아이템을 위한 시간을 보낸다.
// 유니크 아이템을 위한 시간을 보낸다.
CClientManager::instance().SendTime();
}
if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) // 한시간에 한번
if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 3600))) // 한시간에 한번
{
CMoneyLog::instance().Save();
}
@ -2942,7 +2942,7 @@ int CClientManager::Process()
DWORD CClientManager::GetUserCount()
{
// 단순히 로그인 카운트를 센다.. --;
// 단순히 로그인 카운트를 센다.. --;
return m_map_kLogonAccount.size();
}
@ -3002,7 +3002,7 @@ bool CClientManager::InitializeNowItemID()
{
DWORD dwMin, dwMax;
//아이템 ID를 초기화 한다.
//아이템 ID를 초기화 한다.
if (!CConfig::instance().GetTwoValue("ITEM_ID_RANGE", &dwMin, &dwMax))
{
SPDLOG_ERROR("conf.txt: Cannot find ITEM_ID_RANGE [start_item_id] [end_item_id]");
@ -3432,7 +3432,7 @@ bool CClientManager::InitializeLocalization()
bool CClientManager::__GetAdminInfo(const char *szIP, std::vector<tAdminInfo> & rAdminVec)
{
//szIP == NULL 일경우 모든서버에 운영자 권한을 갖는다.
//szIP == NULL 일경우 모든서버에 운영자 권한을 갖는다.
char szQuery[512];
snprintf(szQuery, sizeof(szQuery),
"SELECT mID,mAccount,mName,mContactIP,mServerIP,mAuthority FROM gmlist WHERE mServerIP='ALL' or mServerIP='%s'",
@ -3948,7 +3948,7 @@ void CClientManager::SendSpareItemIDRange(CPeer* peer)
}
//
// Login Key만 맵에서 지운다.
// Login Key만 맵에서 지운다.
//
void CClientManager::DeleteLoginKey(TPacketDC *data)
{
@ -4061,7 +4061,7 @@ void CClientManager::EnrollInAuction (CPeer * peer, DWORD owner_id, AuctionEnrol
SPDLOG_ERROR("Player id {} doesn't have item {}.", owner_id, data->get_item_id());
return;
}
// 현재 시각 + 24시간 후.
// 현재 시각 + 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());
@ -4080,7 +4080,7 @@ void CClientManager::EnrollInAuction (CPeer * peer, DWORD owner_id, AuctionEnrol
}
else
{
// 아이템 케시를 Auction에 등록 했으니 ClientManager에서는 뺀다.
// 아이템 케시를 Auction에 등록 했으니 ClientManager에서는 뺀다.
TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(item->owner);
if (it != m_map_pkItemCacheSetPtr.end())
@ -4133,7 +4133,7 @@ void CClientManager::EnrollInSale (CPeer * peer, DWORD owner_id, AuctionEnrollSa
SPDLOG_ERROR("Player id {} doesn't have item {}.", owner_id, data->get_item_id());
return;
}
// 현재 시각 + 24시간 후.
// 현재 시각 + 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());
@ -4152,7 +4152,7 @@ void CClientManager::EnrollInSale (CPeer * peer, DWORD owner_id, AuctionEnrollSa
}
else
{
// 아이템 케시를 Auction에 등록 했으니 ClientManager에서는 뺀다.
// 아이템 케시를 Auction에 등록 했으니 ClientManager에서는 뺀다.
TItemCacheSetPtrMap::iterator it = m_map_pkItemCacheSetPtr.find(item->owner);
if (it != m_map_pkItemCacheSetPtr.end())
@ -4192,7 +4192,7 @@ void CClientManager::EnrollInWish (CPeer * peer, DWORD wisher_id, AuctionEnrollW
CPlayerTableCache* player_cache = it->second;
TPlayerTable* player = player_cache->Get(false);
// 현재 시각 + 24시간 후.
// 현재 시각 + 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());
@ -4559,11 +4559,11 @@ void CClientManager::AuctionDeleteSaleItem (CPeer * peer, DWORD actor_id, DWORD
AuctionManager::instance().DeleteSaleItem (actor_id, item_id);
}
// ReBid는 이전 입찰금액에 더해서 입찰한다.
// ReBid에선 data->bid_price가 이전 입찰가에 더해져서
// 그 금액으로 rebid하는 것.
// 이렇게 한 이유는 rebid에 실패 했을 때,
// 유저의 호주머니에서 뺀 돈을 돌려주기 편하게 하기 위함이다.
// ReBid는 이전 입찰금액에 더해서 입찰한다.
// ReBid에선 data->bid_price가 이전 입찰가에 더해져서
// 그 금액으로 rebid하는 것.
// 이렇게 한 이유는 rebid에 실패 했을 때,
// 유저의 호주머니에서 뺀 돈을 돌려주기 편하게 하기 위함이다.
void CClientManager::AuctionReBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data)
{
@ -4588,14 +4588,14 @@ void CClientManager::AuctionReBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo
{
SPDLOG_DEBUG("ReBid Success. bidder_id item_id {} {}", bidder_id, data->get_item_id());
}
// 이건 FAIL이 떠서는 안돼.
// FAIL이 뜰 수가 없는게, MyBid에 있는 bidder_id에 대한 컨텐츠는 bidder_id만이 접근 할 수 있거든?
// 그러므로 다른 것이 다 정상적으로 작동한다고 가정 한다면
// 한 게임 서버 내에서 bidder_id로 MyBid를 수정한다 할 지라도, 그건 동기화 문제가 없어.
// 다른 게임 서버에 똑같은 bidder_id를 가진 놈이 있을 수가 없으니까.
// 그러므로 그 게임 서버에서 BidCancel 명령을 db에 날렸다는 것은,
// 이미 그 부분에 대해서는 검사가 완벽하다는 것이야.
// 그래도 혹시나 싶어서, 디버깅을 위해 fail 코드를 남겨둔다.
// 이건 FAIL이 떠서는 안돼.
// FAIL이 뜰 수가 없는게, MyBid에 있는 bidder_id에 대한 컨텐츠는 bidder_id만이 접근 할 수 있거든?
// 그러므로 다른 것이 다 정상적으로 작동한다고 가정 한다면
// 한 게임 서버 내에서 bidder_id로 MyBid를 수정한다 할 지라도, 그건 동기화 문제가 없어.
// 다른 게임 서버에 똑같은 bidder_id를 가진 놈이 있을 수가 없으니까.
// 그러므로 그 게임 서버에서 BidCancel 명령을 db에 날렸다는 것은,
// 이미 그 부분에 대해서는 검사가 완벽하다는 것이야.
// 그래도 혹시나 싶어서, 디버깅을 위해 fail 코드를 남겨둔다.
if (result <= AUCTION_FAIL)
{
TPacketDGResultAuction enroll_result;
@ -4630,14 +4630,14 @@ void CClientManager::AuctionBidCancel (CPeer * peer, DWORD bidder_id, DWORD item
{
AuctionResult result = AuctionManager::instance().BidCancel (bidder_id, item_id);
// 이건 FAIL이 떠서는 안돼.
// FAIL이 뜰 수가 없는게, MyBid에 있는 bidder_id에 대한 컨텐츠는 bidder_id만이 접근 할 수 있거든?
// 그러므로 다른 것이 다 정상적으로 작동한다고 가정 한다면
// 한 게임 서버 내에서 bidder_id로 MyBid를 수정한다 할 지라도, 그건 동기화 문제가 없어.
// 다른 게임 서버에 똑같은 bidder_id를 가진 놈이 있을 수가 없으니까.
// 그러므로 그 게임 서버에서 BidCancel 명령을 db에 날렸다는 것은,
// 이미 그 부분에 대해서는 검사가 완벽하다는 것이야.
// 그래도 혹시나 싶어서, 디버깅을 위해 fail 코드를 남겨둔다.
// 이건 FAIL이 떠서는 안돼.
// FAIL이 뜰 수가 없는게, MyBid에 있는 bidder_id에 대한 컨텐츠는 bidder_id만이 접근 할 수 있거든?
// 그러므로 다른 것이 다 정상적으로 작동한다고 가정 한다면
// 한 게임 서버 내에서 bidder_id로 MyBid를 수정한다 할 지라도, 그건 동기화 문제가 없어.
// 다른 게임 서버에 똑같은 bidder_id를 가진 놈이 있을 수가 없으니까.
// 그러므로 그 게임 서버에서 BidCancel 명령을 db에 날렸다는 것은,
// 이미 그 부분에 대해서는 검사가 완벽하다는 것이야.
// 그래도 혹시나 싶어서, 디버깅을 위해 fail 코드를 남겨둔다.
if (result <= AUCTION_FAIL)
{
TPacketDGResultAuction enroll_result;

View File

@ -48,10 +48,10 @@ class CClientManager : public singleton<CClientManager>
typedef std::unordered_map<short, BYTE> TChannelStatusMap;
// MYSHOP_PRICE_LIST
/// 아이템 가격정보 리스트 요청 정보
/// 아이템 가격정보 리스트 요청 정보
/**
* first: Peer handle
* second: ID
* second: ID
*/
typedef std::pair< DWORD, DWORD > TItemPricelistReqInfo;
// END_OF_MYSHOP_PRICE_LIST
@ -77,7 +77,7 @@ class CClientManager : public singleton<CClientManager>
pAccountTable = NULL;
player_id = dwPID;
};
//독일선물기능용 생성자
//독일선물기능용 생성자
ClientHandleInfo(DWORD argHandle, DWORD dwPID, DWORD accountId)
{
dwHandle = argHandle;
@ -116,7 +116,7 @@ class CClientManager : public singleton<CClientManager>
void SetChinaEventServer(bool flag) { m_bChinaEventServer = flag; }
bool IsChinaEventServer() { return m_bChinaEventServer; }
DWORD GetUserCount(); // 접속된 사용자 수를 리턴 한다.
DWORD GetUserCount(); // 접속된 사용자 수를 리턴 한다.
void SendAllGuildSkillRechargePacket();
void SendTime();
@ -136,23 +136,23 @@ class CClientManager : public singleton<CClientManager>
void UpdateItemCache();
// MYSHOP_PRICE_LIST
/// 가격정보 리스트 캐시를 가져온다.
/// 가격정보 리스트 캐시를 가져온다.
/**
* @param [in] dwID .( ID)
* @return
* @param [in] dwID .( ID)
* @return
*/
CItemPriceListTableCache* GetItemPriceListCache(DWORD dwID);
/// 가격정보 리스트 캐시를 넣는다.
/// 가격정보 리스트 캐시를 넣는다.
/**
* @param [in] pItemPriceList
* @param [in] pItemPriceList
*
* Update replace .
* Update replace .
*/
void PutItemPriceListCache(const TItemPriceListTable* pItemPriceList);
/// Flush 시간이 만료된 아이템 가격정보 리스트 캐시를 Flush 해주고 캐시에서 삭제한다.
/// Flush 시간이 만료된 아이템 가격정보 리스트 캐시를 Flush 해주고 캐시에서 삭제한다.
void UpdateItemPriceListCache(void);
// END_OF_MYSHOP_PRICE_LIST
@ -170,8 +170,8 @@ class CClientManager : public singleton<CClientManager>
void SendNotice(const char * c_pszFormat, ...);
std::string GetCommand(char* str); //독일선물기능에서 명령어 얻는 함수
void ItemAward(CPeer * peer, char* login); //독일 선물 기능
std::string GetCommand(char* str); //독일선물기능에서 명령어 얻는 함수
void ItemAward(CPeer * peer, char* login); //독일 선물 기능
CPeer * AddPeer(bufferevent* bufev, sockaddr* addr);
void RemovePeer(CPeer * pPeer);
@ -199,9 +199,9 @@ class CClientManager : public singleton<CClientManager>
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를 읽어 쓰기 때문에 문제가 발생한다.
// 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();
@ -260,20 +260,20 @@ class CClientManager : public singleton<CClientManager>
// END_PLAYER_INDEX_CREATE_BUG_FIX
// MYSHOP_PRICE_LIST
/// 가격정보 로드 쿼리에 대한 Result 처리
/// 가격정보 로드 쿼리에 대한 Result 처리
/**
* @param peer Game server peer
* @param pMsg Result
* @param peer Game server peer
* @param pMsg Result
*
* peer .
* peer .
*/
void RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg);
/// 가격정보 업데이트를 위한 로드 쿼리에 대한 Result 처리
/// 가격정보 업데이트를 위한 로드 쿼리에 대한 Result 처리
/**
* @param pMsg Result
* @param pMsg Result
*
* .
* .
*/
void RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg);
// END_OF_MYSHOP_PRICE_LIST
@ -343,7 +343,7 @@ class CClientManager : public singleton<CClientManager>
void SetEventFlag(TPacketSetEventFlag* p);
void SendEventFlagsOnSetup(CPeer* peer);
// 결혼
// 결혼
void MarriageAdd(TPacketMarriageAdd * p);
void MarriageUpdate(TPacketMarriageUpdate * p);
void MarriageRemove(TPacketMarriageRemove * p);
@ -353,19 +353,19 @@ class CClientManager : public singleton<CClientManager>
void WeddingEnd(TPacketWeddingEnd * p);
// MYSHOP_PRICE_LIST
// 개인상점 가격정보
// 개인상점 가격정보
/// 아이템 가격정보 리스트 업데이트 패킷(HEADER_GD_MYSHOP_PRICELIST_UPDATE) 처리함수
/// 아이템 가격정보 리스트 업데이트 패킷(HEADER_GD_MYSHOP_PRICELIST_UPDATE) 처리함수
/**
* @param [in] pPacket
* @param [in] pPacket
*/
void MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* pPacket);
/// 아이템 가격정보 리스트 요청 패킷(HEADER_GD_MYSHOP_PRICELIST_REQ) 처리함수
/// 아이템 가격정보 리스트 요청 패킷(HEADER_GD_MYSHOP_PRICELIST_REQ) 처리함수
/**
* @param peer Game server peer
* @param [in] dwHandle peer
* @param [in] dwPlayerID ID
* @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
@ -399,7 +399,7 @@ class CClientManager : public singleton<CClientManager>
typedef std::unordered_map<DWORD, CLoginData *> TLoginDataByAID;
TLoginDataByAID m_map_pkLoginDataByAID;
// Login LoginData pair (실제 로그인 되어있는 계정)
// Login LoginData pair (실제 로그인 되어있는 계정)
typedef std::unordered_map<std::string, CLoginData *> TLogonAccountMap;
TLogonAccountMap m_map_kLogonAccount;
@ -429,14 +429,14 @@ class CClientManager : public singleton<CClientManager>
bool m_bShutdowned;
TPlayerTableCacheMap m_map_playerCache; // 플레이어 id가 key
TPlayerTableCacheMap m_map_playerCache; // 플레이어 id가 key
TItemCacheMap m_map_itemCache; // 아이템 id가 key
TItemCacheSetPtrMap m_map_pkItemCacheSetPtr; // 플레이어 id가 key, 이 플레이어가 어떤 아이템 캐쉬를 가지고 있나?
TItemCacheMap m_map_itemCache; // 아이템 id가 key
TItemCacheSetPtrMap m_map_pkItemCacheSetPtr; // 플레이어 id가 key, 이 플레이어가 어떤 아이템 캐쉬를 가지고 있나?
// MYSHOP_PRICE_LIST
/// 플레이어별 아이템 가격정보 리스트 map. key: 플레이어 ID, value: 가격정보 리스트 캐시
TItemPriceListCacheMap m_mapItemPriceListCache; ///< 플레이어별 아이템 가격정보 리스트
/// 플레이어별 아이템 가격정보 리스트 map. key: 플레이어 ID, value: 가격정보 리스트 캐시
TItemPriceListCacheMap m_mapItemPriceListCache; ///< 플레이어별 아이템 가격정보 리스트
// END_OF_MYSHOP_PRICE_LIST
TChannelStatusMap m_mChannelStatus;
@ -474,7 +474,7 @@ class CClientManager : public singleton<CClientManager>
//BOOT_LOCALIZATION
public:
/* 로컬 정보 초기화
/* 로컬 정보 초기화
**/
bool InitializeLocalization();

View File

@ -171,42 +171,42 @@ class FCompareVnum
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_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 맵 생성>
// 1) 'mob_names.txt' 파일을 읽어서 (a)[localMap] 맵을 만든다.
//<(a)localMap 맵 생성>
map<int,const char*> localMap;
bool isNameFile = true;
//<파일 읽기>
//<파일 읽기>
cCsvTable nameData;
if(!nameData.Load("mob_names.txt",'\t'))
{
SPDLOG_ERROR("mob_names.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("mob_names.txt Failed to read the file");
isNameFile = false;
} else {
nameData.Next(); //설명row 생략.
nameData.Next(); //설명row 생략.
while(nameData.Next()) {
localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1);
}
@ -215,35 +215,35 @@ bool CClientManager::InitializeMobTable()
//===============================================//
// 2) 'mob_proto_test.txt'파일과 (a)localMap 맵으로
// (b)[test_map_mobTableByVnum](vnum:TMobTable) 맵을 생성한다.
// 2) 'mob_proto_test.txt'파일과 (a)localMap 맵으로
// (b)[test_map_mobTableByVnum](vnum:TMobTable) 맵을 생성한다.
//0.
set<int> vnumSet; //테스트용 파일 데이터중, 신규여부 확인에 사용.
//1. 파일 읽어오기
set<int> vnumSet; //테스트용 파일 데이터중, 신규여부 확인에 사용.
//1. 파일 읽어오기
bool isTestFile = true;
cCsvTable test_data;
if(!test_data.Load("mob_proto_test.txt",'\t'))
{
SPDLOG_ERROR("테스트 파일이 없습니다. 그대로 진행합니다.");
SPDLOG_ERROR("No test file exists, proceed as is.");
isTestFile = false;
}
//2. (c)[test_map_mobTableByVnum](vnum:TMobTable) 맵 생성.
//2. (c)[test_map_mobTableByVnum](vnum:TMobTable) 맵 생성.
map<DWORD, TMobTable *> test_map_mobTableByVnum;
if (isTestFile) {
test_data.Next(); //설명 로우 넘어가기.
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))
{
SPDLOG_ERROR("몹 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Mob proto table setup failed.");
}
test_map_mobTableByVnum.insert(std::map<DWORD, TMobTable *>::value_type(test_mob_table->dwVnum, test_mob_table));
@ -254,22 +254,22 @@ bool CClientManager::InitializeMobTable()
}
// 3) 'mob_proto.txt' 파일과 (a)[localMap] 맵으로
// (!)[mob_table] 테이블을 만든다.
// <참고>
// 각 row 들 중,
// (b)[test_map_mobTableByVnum],(!)[mob_table] 모두에 있는 row는
// (b)[test_map_mobTableByVnum]의 것을 사용한다.
// 3) 'mob_proto.txt' 파일과 (a)[localMap] 맵으로
// (!)[mob_table] 테이블을 만든다.
// <참고>
// 각 row 들 중,
// (b)[test_map_mobTableByVnum],(!)[mob_table] 모두에 있는 row는
// (b)[test_map_mobTableByVnum]의 것을 사용한다.
//1. 파일 읽기.
//1. 파일 읽기.
cCsvTable data;
if(!data.Load("mob_proto.txt",'\t')) {
SPDLOG_ERROR("mob_proto.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("mob_proto.txt Failed to read the file");
return false;
}
data.Next(); //설명 row 넘어가기
//2. (!)[mob_table] 생성하기
//2.1 새로 추가되는 갯수를 파악
data.Next(); //설명 row 넘어가기
//2. (!)[mob_table] 생성하기
//2.1 새로 추가되는 갯수를 파악
int addNumber = 0;
while(data.Next()) {
int vnum = atoi(data.AsStringByIndex(0));
@ -279,15 +279,15 @@ bool CClientManager::InitializeMobTable()
addNumber++;
}
}
//data를 다시 첫줄로 옮긴다.(다시 읽어온다;;)
//data를 다시 첫줄로 옮긴다.(다시 읽어온다;;)
data.Destroy();
if(!data.Load("mob_proto.txt",'\t'))
{
SPDLOG_ERROR("mob_proto.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("mob_proto.txt Failed to read the file");
return false;
}
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
//2.2 크기에 맞게 mob_table 생성
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
//2.2 크기에 맞게 mob_table 생성
if (!m_vec_mobTable.empty())
{
SPDLOG_DEBUG("RELOAD: mob_proto");
@ -296,18 +296,18 @@ bool CClientManager::InitializeMobTable()
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 데이터 채우기
//2.3 데이터 채우기
while (data.Next())
{
int col = 0;
//(b)[test_map_mobTableByVnum]에 같은 row가 있는지 조사.
//(b)[test_map_mobTableByVnum]에 같은 row가 있는지 조사.
bool isSameRow = true;
std::map<DWORD, TMobTable *>::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)에서 읽어온다.
//같은 row 가 있으면 (b)에서 읽어온다.
if(isSameRow) {
TMobTable *tempTable = it_map_mobTable->second;
@ -378,13 +378,13 @@ bool CClientManager::InitializeMobTable()
if (!Set_Proto_Mob_Table(mob_table, data, localMap))
{
SPDLOG_ERROR("몹 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Mob proto table setup failed.");
}
}
//셋에 vnum 추가
//셋에 vnum 추가
vnumSet.insert(mob_table->dwVnum);
@ -395,22 +395,22 @@ bool CClientManager::InitializeMobTable()
//_____________________________________________________//
// 4) (b)[test_map_mobTableByVnum]의 row중, (!)[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'))
{
SPDLOG_ERROR("테스트 파일이 없습니다. 그대로 진행합니다.");
SPDLOG_ERROR("No test file exists, proceed as is.");
isTestFile = false;
}
if(isTestFile) {
test_data.Next(); //설명 로우 넘어가기.
test_data.Next(); //설명 로우 넘어가기.
while (test_data.Next()) //테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다.
while (test_data.Next()) //테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다.
{
//중복되는 부분이면 넘어간다.
//중복되는 부분이면 넘어간다.
set<int>::iterator itVnum;
itVnum=vnumSet.find(atoi(test_data.AsStringByIndex(0)));
if (itVnum != vnumSet.end()) {
@ -419,7 +419,7 @@ bool CClientManager::InitializeMobTable()
if (!Set_Proto_Mob_Table(mob_table, test_data, localMap))
{
SPDLOG_ERROR("몹 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Mob proto table setup failed.");
}
SPDLOG_DEBUG("MOB #{:<5} {:24} {:24} level: {:<3} rank: {} empire: {}", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire);
@ -447,8 +447,8 @@ bool CClientManager::InitializeShopTable()
std::unique_ptr<SQLMsg> pkMsg2(CDBManager::instance().DirectQuery(s_szQuery));
// shop의 vnum은 있는데 shop_item 이 없을경우... 실패로 처리되니 주의 요망.
// 고처야할부분
// shop의 vnum은 있는데 shop_item 이 없을경우... 실패로 처리되니 주의 요망.
// 고처야할부분
SQLResult * pRes2 = pkMsg2->Get();
if (!pRes2->uiNumRows)
@ -487,7 +487,7 @@ bool CClientManager::InitializeShopTable()
str_to_number(shop_table->dwNPCVnum, data[col++]);
if (!data[col]) // 아이템이 하나도 없으면 NULL이 리턴 되므로..
if (!data[col]) // 아이템이 하나도 없으면 NULL이 리턴 되므로..
continue;
TShopItemTable * pItem = &shop_table->items[shop_table->byItemCount];
@ -560,7 +560,7 @@ bool CClientManager::InitializeQuestItemTable()
continue;
}
tbl.bType = ITEM_QUEST; // quest_item_proto 테이블에 있는 것들은 모두 ITEM_QUEST 유형
tbl.bType = ITEM_QUEST; // quest_item_proto 테이블에 있는 것들은 모두 ITEM_QUEST 유형
tbl.bSize = 1;
m_vec_itemTable.push_back(tbl);
@ -571,39 +571,39 @@ bool CClientManager::InitializeQuestItemTable()
bool CClientManager::InitializeItemTable()
{
//================== 함수 설명 ==================//
//1. 요약 : 'item_proto.txt', 'item_proto_test.txt', 'item_names.txt' 파일을 읽고,
// <item_table>(TItemTable), <m_map_itemTableByVnum> 오브젝트를 생성한다.
//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], <m_map_itemTableByVnum>을 만든다.
// <참고>
// 각 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_proto.txt', 'item_proto_test.txt', 'item_names.txt' 파일을 읽고,
// <item_table>(TItemTable), <m_map_itemTableByVnum> 오브젝트를 생성한다.
//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], <m_map_itemTableByVnum>을 만든다.
// <참고>
// 각 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) 맵을 만든다.
// 1) 'item_names.txt' 파일을 읽어서 (a)[localMap](vnum:name) 맵을 만든다.
//=================================================================================//
bool isNameFile = true;
map<int,const char*> localMap;
cCsvTable nameData;
if(!nameData.Load("item_names.txt",'\t'))
{
SPDLOG_ERROR("item_names.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("item_names.txt Failed to read the file");
isNameFile = false;
} else {
nameData.Next();
@ -614,32 +614,32 @@ bool CClientManager::InitializeItemTable()
//_________________________________________________________________//
//=================================================================//
// 2) 'item_proto_text.txt'파일과 (a)[localMap] 맵으로
// (b)[test_map_itemTableByVnum](vnum:TItemTable) 맵을 생성한다.
// 2) 'item_proto_text.txt'파일과 (a)[localMap] 맵으로
// (b)[test_map_itemTableByVnum](vnum:TItemTable) 맵을 생성한다.
//=================================================================//
map<DWORD, TItemTable *> test_map_itemTableByVnum;
//1. 파일 읽어오기.
//1. 파일 읽어오기.
cCsvTable test_data;
if(!test_data.Load("item_proto_test.txt",'\t'))
{
SPDLOG_ERROR("item_proto_test.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("item_proto_test.txt Failed to read the file");
//return false;
} else {
test_data.Next(); //설명 로우 넘어가기.
test_data.Next(); //설명 로우 넘어가기.
//2. 테스트 아이템 테이블 생성.
//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. 테스트 아이템 테이블에 값을 넣고, 맵에까지 넣기.
//3. 테스트 아이템 테이블에 값을 넣고, 맵에까지 넣기.
while(test_data.Next()) {
if (!Set_Proto_Item_Table(test_item_table, test_data, localMap))
{
SPDLOG_ERROR("아이템 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Item proto table setup failed.");
}
test_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(test_item_table->dwVnum, test_item_table));
@ -651,25 +651,25 @@ bool CClientManager::InitializeItemTable()
//========================================================================//
// 3) 'item_proto.txt' 파일과 (a)[localMap] 맵으로
// (!)[item_table], <m_map_itemTableByVnum>을 만든다.
// <참고>
// 각 row 들 중,
// (b)[test_map_itemTableByVnum],(!)[mob_table] 모두에 있는 row는
// (b)[test_map_itemTableByVnum]의 것을 사용한다.
// 3) 'item_proto.txt' 파일과 (a)[localMap] 맵으로
// (!)[item_table], <m_map_itemTableByVnum>을 만든다.
// <참고>
// 각 row 들 중,
// (b)[test_map_itemTableByVnum],(!)[mob_table] 모두에 있는 row는
// (b)[test_map_itemTableByVnum]의 것을 사용한다.
//========================================================================//
//vnum들을 저장할 셋. 새로운 테스트 아이템을 판별할때 사용된다.
//vnum들을 저장할 셋. 새로운 테스트 아이템을 판별할때 사용된다.
set<int> vnumSet;
//파일 읽어오기.
//파일 읽어오기.
cCsvTable data;
if(!data.Load("item_proto.txt",'\t'))
{
SPDLOG_ERROR("item_proto.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("item_proto.txt Failed to read the file");
return false;
}
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
if (!m_vec_itemTable.empty())
{
@ -678,8 +678,8 @@ bool CClientManager::InitializeItemTable()
m_map_itemTableByVnum.clear();
}
//===== 아이템 테이블 생성 =====//
//새로 추가되는 갯수를 파악한다.
//===== 아이템 테이블 생성 =====//
//새로 추가되는 갯수를 파악한다.
int addNumber = 0;
while(data.Next()) {
int vnum = atoi(data.AsStringByIndex(0));
@ -689,14 +689,14 @@ bool CClientManager::InitializeItemTable()
addNumber++;
}
}
//data를 다시 첫줄로 옮긴다.(다시 읽어온다;;)
//data를 다시 첫줄로 옮긴다.(다시 읽어온다;;)
data.Destroy();
if(!data.Load("item_proto.txt",'\t'))
{
SPDLOG_ERROR("item_proto.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("item_proto.txt Failed to read the file");
return false;
}
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
data.Next(); //맨 윗줄 제외 (아이템 칼럼을 설명하는 부분)
m_vec_itemTable.resize(data.m_File.GetRowCount() - 1 + addNumber);
memset(&m_vec_itemTable[0], 0, sizeof(TItemTable) * m_vec_itemTable.size());
@ -711,16 +711,16 @@ bool CClientManager::InitializeItemTable()
std::map<DWORD, TItemTable *>::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))
{
SPDLOG_ERROR("아이템 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Item proto table setup failed.");
}
} else { //$$$$$$$$$$$$$$$$$$$$$$$ 테스트 아이템 정보가 있다!
} else { //$$$$$$$$$$$$$$$$$$$$$$$ 테스트 아이템 정보가 있다!
TItemTable *tempTable = it_map_itemTable->second;
item_table->dwVnum = tempTable->dwVnum;
@ -777,19 +777,19 @@ bool CClientManager::InitializeItemTable()
//_______________________________________________________________________//
//========================================================================//
// 4) (b)[test_map_itemTableByVnum]의 row중, (!)[item_table]에 없는 것을 추가한다.
// 4) (b)[test_map_itemTableByVnum]의 row중, (!)[item_table]에 없는 것을 추가한다.
//========================================================================//
test_data.Destroy();
if(!test_data.Load("item_proto_test.txt",'\t'))
{
SPDLOG_ERROR("item_proto_test.txt 파일을 읽어오지 못했습니다");
SPDLOG_ERROR("item_proto_test.txt Failed to read the file");
//return false;
} else {
test_data.Next(); //설명 로우 넘어가기.
test_data.Next(); //설명 로우 넘어가기.
while (test_data.Next()) //테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다.
while (test_data.Next()) //테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다.
{
//중복되는 부분이면 넘어간다.
//중복되는 부분이면 넘어간다.
set<int>::iterator itVnum;
itVnum=vnumSet.find(atoi(test_data.AsStringByIndex(0)));
if (itVnum != vnumSet.end()) {
@ -798,7 +798,7 @@ bool CClientManager::InitializeItemTable()
if (!Set_Proto_Item_Table(item_table, test_data, localMap))
{
SPDLOG_ERROR("아이템 프로토 테이블 셋팅 실패.");
SPDLOG_ERROR("Item proto table setup failed.");
}

View File

@ -126,13 +126,13 @@ const char* __GetWarType(int n)
switch (n)
{
case 0 :
return "패왕";
return "\xEF\xBF\xBD\xD0\xBF\xEF\xBF\xBD"; // 패왕
case 1 :
return "맹장";
return "\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD"; // 맹장
case 2 :
return "수호";
return "\xEF\xBF\xBD\xEF\xBF\xBD\xC8\xA3"; // 수호
default :
return "없는 번호";
return "\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\x20\xEF\xBF\xBD\xEF\xBF\xBD\xC8\xA3"; // 없는 번호
}
}
@ -161,7 +161,7 @@ void CClientManager::GuildWar(CPeer* peer, TPacketGuildWar* p)
case GUILD_WAR_WAIT_START:
SPDLOG_DEBUG("GuildWar: GUILD_WAR_WAIT_START type({}) guild({} - {})", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
case GUILD_WAR_RESERVE: // 길드전 예약
case GUILD_WAR_RESERVE: // 길드전 예약
if (p->bWar != GUILD_WAR_WAIT_START)
SPDLOG_DEBUG("GuildWar: GUILD_WAR_RESERVE type({}) guild({} - {})", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo);
@ -173,21 +173,21 @@ void CClientManager::GuildWar(CPeer* peer, TPacketGuildWar* p)
break;
case GUILD_WAR_ON_WAR: // 길드전을 시작 시킨다. (필드전은 바로 시작 됨)
case GUILD_WAR_ON_WAR: // 길드전을 시작 시킨다. (필드전은 바로 시작 됨)
SPDLOG_DEBUG("GuildWar: GUILD_WAR_ON_WAR type({}) guild({} - {})", __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: // 길드전 정상 종료
case GUILD_WAR_OVER: // 길드전 정상 종료
SPDLOG_DEBUG("GuildWar: GUILD_WAR_OVER type({}) guild({} - {})", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
CGuildManager::instance().RecvWarOver(p->dwGuildFrom, p->dwGuildTo, p->bType, p->lWarPrice);
break;
case GUILD_WAR_END: // 길드전 비정상 종료
case GUILD_WAR_END: // 길드전 비정상 종료
SPDLOG_DEBUG("GuildWar: GUILD_WAR_END type({}) guild({} - {})", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
CGuildManager::instance().RecvWarEnd(p->dwGuildFrom, p->dwGuildTo);
return; // NOTE: RecvWarEnd에서 패킷을 보내므로 따로 브로드캐스팅 하지 않는다.
return; // NOTE: RecvWarEnd에서 패킷을 보내므로 따로 브로드캐스팅 하지 않는다.
case GUILD_WAR_CANCEL :
SPDLOG_DEBUG("GuildWar: GUILD_WAR_CANCEL type({}) guild({} - {})", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);

View File

@ -229,7 +229,7 @@ TAccountTable * CreateAccountTableFromRes(MYSQL_RES * res)
TAccountTable * pkTab = new TAccountTable;
memset(pkTab, 0, sizeof(TAccountTable));
// 첫번째 컬럼 것만 참고 한다 (JOIN QUERY를 위한 것 임)
// 첫번째 컬럼 것만 참고 한다 (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));
@ -353,7 +353,7 @@ void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg)
if (info->account_index == 0)
{
// 계정이 없네?
// 계정이 없네?
if (msg->Get()->uiNumRows == 0)
{
SPDLOG_DEBUG("RESULT_LOGIN: no account");
@ -395,14 +395,14 @@ void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg)
}
else
{
if (!info->pAccountTable) // 이럴리는 없겠지만;;
if (!info->pAccountTable) // 이럴리는 없겠지만;;
{
peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle);
delete info;
return;
}
// 다른 컨넥션이 이미 로그인 해버렸다면.. 이미 접속했다고 보내야 한다.
// 다른 컨넥션이 이미 로그인 해버렸다면.. 이미 접속했다고 보내야 한다.
if (!InsertLogonAccount(info->pAccountTable->login, peer->GetHandle(), info->ip))
{
SPDLOG_DEBUG("RESULT_LOGIN: already logon {}", info->pAccountTable->login);

View File

@ -28,7 +28,7 @@ bool CreateItemTableFromRes(MYSQL_RES * res, std::vector<TPlayerItem> * pVec, DW
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
pVec->clear();
return true;
@ -155,7 +155,7 @@ size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * p
pkTab->horse.sStamina,
pkTab->horse_skill_point);
// Binary 로 바꾸기 위한 임시 공간
// Binary 로 바꾸기 위한 임시 공간
char text[8192 + 1];
CDBManager::instance().EscapeString(text, pkTab->skills, sizeof(pkTab->skills));
@ -204,7 +204,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
TPlayerTable * pTab;
//
// 한 계정에 속한 모든 캐릭터들 캐쉬처리
// 한 계정에 속한 모든 캐릭터들 캐쉬처리
//
CLoginData * pLoginData = GetLoginDataByAID(packet->account_id);
@ -216,12 +216,12 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
}
//----------------------------------------------------------------
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
// 2. 유저정보가 DBCache 에 없음 : DB에서
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
// 2. 유저정보가 DBCache 에 없음 : DB에서
// ---------------------------------------------------------------
//----------------------------------
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
//----------------------------------
if ((c = GetPlayerCache(packet->player_id)))
{
@ -260,13 +260,13 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
SPDLOG_DEBUG("[PLAYER_LOAD] ID {} pid {} gold {} ", pTab->name, pTab->id, pTab->gold);
//--------------------------------------------
// 아이템 & AFFECT & QUEST 로딩 :
// 아이템 & AFFECT & QUEST 로딩 :
//--------------------------------------------
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
/////////////////////////////////////////////
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
/////////////////////////////////////////////
if (pSet)
{
@ -281,7 +281,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
CItemCache * c = *it++;
TPlayerItem * p = c->Get();
if (p->vnum) // vnum이 없으면 삭제된 아이템이다.
if (p->vnum) // vnum이 없으면 삭제된 아이템이다.
memcpy(&s_items[dwCount++], p, sizeof(TPlayerItem));
}
@ -307,7 +307,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle));
}
/////////////////////////////////////////////
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
/////////////////////////////////////////////
else
{
@ -341,7 +341,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
//return;
}
//----------------------------------
// 2. 유저정보가 DBCache 에 없음 : DB에서
// 2. 유저정보가 DBCache 에 없음 : DB에서
//----------------------------------
else
{
@ -350,7 +350,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
char queryStr[QUERY_MAX_LEN];
//--------------------------------------------------------------
// 캐릭터 정보 얻어오기 : 무조건 DB에서
// 캐릭터 정보 얻어오기 : 무조건 DB에서
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"SELECT "
@ -366,7 +366,7 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
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 "
@ -375,15 +375,15 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
CDBManager::instance().ReturnQuery(queryStr, QID_ITEM, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
//--------------------------------------------------------------
// QUEST 가져오기
// 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도 넘겨준다
//독일 선물 기능에서 item_award테이블에서 login 정보를 얻기위해 account id도 넘겨준다
//--------------------------------------------------------------
// AFFECT 가져오기
// AFFECT 가져오기
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d",
@ -400,21 +400,21 @@ void CClientManager::ItemAward(CPeer * peer,char* login)
std::set<TItemAward *> * pSet = ItemAwardManager::instance().GetByLogin(login_t);
if(pSet == NULL)
return;
typeof(pSet->begin()) it = pSet->begin(); //taken_time이 NULL인것들 읽어옴
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); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
char* whyStr = pItemAward->szWhy; //why 콜룸 읽기
char cmdStr[100] = ""; //why콜룸에서 읽은 값을 임시 문자열에 복사해둠
strcpy(cmdStr,whyStr); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
char command[20] = "";
strcpy(command,GetCommand(cmdStr).c_str()); // command 얻기
if( !(strcmp(command,"GIFT") )) // command 가 GIFT이면
strcpy(command,GetCommand(cmdStr).c_str()); // command 얻기
if( !(strcmp(command,"GIFT") )) // command 가 GIFT이면
{
TPacketItemAwardInfromer giftData;
strcpy(giftData.login, pItemAward->szLogin); //로그인 아이디 복사
strcpy(giftData.command, command); //명령어 복사
giftData.vnum = pItemAward->dwVnum; //아이템 vnum도 복사
strcpy(giftData.login, pItemAward->szLogin); //로그인 아이디 복사
strcpy(giftData.command, command); //명령어 복사
giftData.vnum = pItemAward->dwVnum; //아이템 vnum도 복사
ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer));
}
}
@ -435,7 +435,7 @@ std::string CClientManager::GetCommand(char* str)
bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab)
{
if (mysql_num_rows(res) == 0) // 데이터 없음
if (mysql_num_rows(res) == 0) // 데이터 없음
return false;
memset(pkTab, 0, sizeof(TPlayerTable));
@ -515,11 +515,11 @@ bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab)
int max_point = pkTab->level - 9;
int skill_point =
std::min<int>(20, pkTab->skills[121].bLevel) + // SKILL_LEADERSHIP 통솔력
std::min<int>(20, pkTab->skills[124].bLevel) + // SKILL_MINING 채광
std::min<int>(10, pkTab->skills[131].bLevel) + // SKILL_HORSE_SUMMON 말소환
std::min<int>(20, pkTab->skills[141].bLevel) + // SKILL_ADD_HP HP보강
std::min<int>(20, pkTab->skills[142].bLevel); // SKILL_RESIST_PENETRATE 관통저항
std::min<int>(20, pkTab->skills[121].bLevel) + // SKILL_LEADERSHIP 통솔력
std::min<int>(20, pkTab->skills[124].bLevel) + // SKILL_MINING 채광
std::min<int>(10, pkTab->skills[131].bLevel) + // SKILL_HORSE_SUMMON 말소환
std::min<int>(20, pkTab->skills[141].bLevel) + // SKILL_ADD_HP HP보강
std::min<int>(20, pkTab->skills[142].bLevel); // SKILL_RESIST_PENETRATE 관통저항
pkTab->sub_skill_point = max_point - skill_point;
}
@ -559,13 +559,13 @@ void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD
{
SPDLOG_DEBUG("QID_QUEST {}", info->dwHandle);
RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle, info->player_id);
//aid얻기
//aid얻기
ClientHandleInfo* temp1 = info.get();
if (temp1 == NULL)
break;
CLoginData* pLoginData1 = GetLoginDataByAID(temp1->account_id); //
//독일 선물 기능
//독일 선물 기능
if( pLoginData1->GetAccountRef().login == NULL)
break;
if( pLoginData1 == NULL )
@ -655,14 +655,14 @@ void CClientManager::RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHa
void CClientManager::RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID)
{
static std::vector<TPlayerItem> s_items;
//DB에서 아이템 정보를 읽어온다.
//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을 만든다
//CacheSet을 만든다
CreateItemCacheSet(dwPID);
// ITEM_LOAD_LOG_ATTACH_PID
@ -674,7 +674,7 @@ void CClientManager::RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHa
peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount);
for (DWORD i = 0; i < dwCount; ++i)
PutItemCache(&s_items[i], true); // 로드한 것은 따로 저장할 필요 없으므로, 인자 bSkipQuery에 true를 넣는다.
PutItemCache(&s_items[i], true); // 로드한 것은 따로 저장할 필요 없으므로, 인자 bSkipQuery에 true를 넣는다.
}
}
@ -682,7 +682,7 @@ void CClientManager::RESULT_AFFECT_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dw
{
int iNumRows;
if ((iNumRows = mysql_num_rows(pRes)) == 0) // 데이터 없음
if ((iNumRows = mysql_num_rows(pRes)) == 0) // 데이터 없음
return;
static std::vector<TPacketAffectElement> s_elements;
@ -778,7 +778,7 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
int queryLen;
DWORD player_id;
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
auto it = s_createTimeByAccountID.find(packet->account_id);
if (it != s_createTimeByAccountID.end())
@ -1007,7 +1007,7 @@ void CClientManager::__QUERY_PLAYER_DELETE(CPeer* peer, DWORD dwHandle, TPlayerD
}
//
// @version 05/06/10 Bang2ni - 플레이어 삭제시 가격정보 리스트 삭제 추가.
// @version 05/06/10 Bang2ni - 플레이어 삭제시 가격정보 리스트 삭제 추가.
//
void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
{
@ -1058,14 +1058,14 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
return;
}
// 삭제 성공
// 삭제 성공
SPDLOG_DEBUG("PLAYER_DELETE SUCCESS {}", dwPID);
char account_index_string[16];
snprintf(account_index_string, sizeof(account_index_string), "player_id%d", m_iPlayerIDStart + pi->account_index);
// 플레이어 테이블을 캐쉬에서 삭제한다.
// 플레이어 테이블을 캐쉬에서 삭제한다.
CPlayerTableCache * pkPlayerCache = GetPlayerCache(pi->player_id);
if (pkPlayerCache)
@ -1074,7 +1074,7 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
delete pkPlayerCache;
}
// 아이템들을 캐쉬에서 삭제한다.
// 아이템들을 캐쉬에서 삭제한다.
TItemCacheSet * pSet = GetItemCacheSet(pi->player_id);
if (pSet)
@ -1137,7 +1137,7 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
}
else
{
// 삭제 실패
// 삭제 실패
SPDLOG_DEBUG("PLAYER_DELETE FAIL NO ROW");
peer->EncodeHeader(HEADER_DG_PLAYER_DELETE_FAILED, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
@ -1195,10 +1195,10 @@ void CClientManager::InsertLogoutPlayer(DWORD pid)
{
TLogoutPlayerMap::iterator it = m_map_logout.find(pid);
// 존재하지 않을경우 추가
// 존재하지 않을경우 추가
if (it != m_map_logout.end())
{
// 존재할경우 시간만 갱신
// 존재할경우 시간만 갱신
SPDLOG_TRACE("LOGOUT: Update player time pid({})", pid);
it->second->time = time(0);

View File

@ -67,7 +67,7 @@ bool CConfig::GetWord(FILE *fp, char *tar)
if ((c == ' ' || c == '\t' || c == '\n'))
{
// 텝.
// 텝.
tar[i] = '\0';
return true;
}
@ -144,7 +144,7 @@ bool CConfig::LoadFile(const char* filename)
}
// 파일 닫는 부분.
// 파일 닫는 부분.
fclose(fp);
return true;
}

View File

@ -11,14 +11,14 @@
namespace
{
/// 파싱용 state 열거값
/// 파싱용 state 열거값
enum ParseState
{
STATE_NORMAL = 0, ///< 일반 상태
STATE_QUOTE ///< 따옴표 뒤의 상태
STATE_NORMAL = 0, ///< 일반 상태
STATE_QUOTE ///< 따옴표 뒤의 상태
};
/// 문자열 좌우의 공백을 제거해서 반환한다.
/// 문자열 좌우의 공백을 제거해서 반환한다.
std::string Trim(std::string str)
{
str = str.erase(str.find_last_not_of(" \t\r\n") + 1);
@ -26,7 +26,7 @@ namespace
return str;
}
/// \brief 주어진 문장에 있는 알파벳을 모두 소문자로 바꾼다.
/// \brief 주어진 문장에 있는 알파벳을 모두 소문자로 바꾼다.
std::string Lower(std::string original)
{
std::transform(original.begin(), original.end(), original.begin(), tolower);
@ -35,9 +35,9 @@ namespace
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
/// \param name 셀 이름
/// \param index 셀 인덱스
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
/// \param name 셀 이름
/// \param index 셀 인덱스
////////////////////////////////////////////////////////////////////////////////
void cCsvAlias::AddAlias(const char* name, size_t index)
{
@ -51,7 +51,7 @@ void cCsvAlias::AddAlias(const char* name, size_t index)
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 모든 데이터를 삭제한다.
/// \brief 모든 데이터를 삭제한다.
////////////////////////////////////////////////////////////////////////////////
void cCsvAlias::Destroy()
{
@ -60,9 +60,9 @@ void cCsvAlias::Destroy()
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 숫자 인덱스를 이름으로 변환한다.
/// \param index 숫자 인덱스
/// \return const char* 이름
/// \brief 숫자 인덱스를 이름으로 변환한다.
/// \param index 숫자 인덱스
/// \return const char* 이름
////////////////////////////////////////////////////////////////////////////////
const char* cCsvAlias::operator [] (size_t index) const
{
@ -78,9 +78,9 @@ const char* cCsvAlias::operator [] (size_t index) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 이름을 숫자 인덱스로 변환한다.
/// \param name 이름
/// \return size_t 숫자 인덱스
/// \brief 이름을 숫자 인덱스로 변환한다.
/// \param name 이름
/// \return size_t 숫자 인덱스
////////////////////////////////////////////////////////////////////////////////
size_t cCsvAlias::operator [] (const char* name) const
{
@ -96,11 +96,11 @@ size_t cCsvAlias::operator [] (const char* name) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 지정된 이름의 CSV 파일을 로드한다.
/// \param fileName CSV 파일 이름
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
/// \return bool 무사히 로드했다면 true, 아니라면 false
/// \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)
{
@ -109,7 +109,7 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
std::ifstream file(fileName, std::ios::in);
if (!file) return false;
Destroy(); // 기존의 데이터를 삭제
Destroy(); // 기존의 데이터를 삭제
cCsvRow* row = NULL;
ParseState state = STATE_NORMAL;
@ -124,33 +124,33 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
std::string line(Trim(buf));
if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue;
std::string text = std::string(line) + " "; // 파싱 lookahead 때문에 붙여준다.
std::string text = std::string(line) + " "; // 파싱 lookahead 때문에 붙여준다.
size_t cur = 0;
while (cur < text.size())
{
// 현재 모드가 QUOTE 모드일 때,
// 현재 모드가 QUOTE 모드일 때,
if (state == STATE_QUOTE)
{
// '"' 문자의 종류는 두 가지이다.
// 1. 셀 내부에 특수 문자가 있을 경우 이를 알리는 셀 좌우의 것
// 2. 셀 내부의 '"' 문자가 '"' 2개로 치환된 것
// 이 중 첫번째 경우의 좌측에 있는 것은 CSV 파일이 정상적이라면,
// 무조건 STATE_NORMAL에 걸리게 되어있다.
// 그러므로 여기서 걸리는 것은 1번의 우측 경우나, 2번 경우 뿐이다.
// 2번의 경우에는 무조건 '"' 문자가 2개씩 나타난다. 하지만 1번의
// 우측 경우에는 아니다. 이를 바탕으로 해서 코드를 짜면...
// '"' 문자의 종류는 두 가지이다.
// 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;
@ -161,25 +161,25 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
token += text[cur];
}
}
// 현재 모드가 NORMAL 모드일 때,
// 현재 모드가 NORMAL 모드일 때,
else if (state == STATE_NORMAL)
{
if (row == NULL)
row = new cCsvRow();
// ',' 문자를 만났다면 셀의 끝의 의미한다.
// 토큰으로서 셀 리스트에다가 집어넣고, 토큰을 초기화한다.
// ',' 문자를 만났다면 셀의 끝의 의미한다.
// 토큰으로서 셀 리스트에다가 집어넣고, 토큰을 초기화한다.
if (text[cur] == seperator)
{
row->push_back(token);
token.clear();
}
// '"' 문자를 만났다면, QUOTE 모드로 전환한다.
// '"' 문자를 만났다면, QUOTE 모드로 전환한다.
else if (text[cur] == quote)
{
state = STATE_QUOTE;
}
// 다른 일반 문자라면 현재 토큰에다가 덧붙인다.
// 다른 일반 문자라면 현재 토큰에다가 덧붙인다.
else
{
token += text[cur];
@ -189,8 +189,8 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
++cur;
}
// 마지막 셀은 끝에 ',' 문자가 없기 때문에 여기서 추가해줘야한다.
// 단, 처음에 파싱 lookahead 때문에 붙인 스페이스 문자 두 개를 뗀다.
// 마지막 셀은 끝에 ',' 문자가 없기 때문에 여기서 추가해줘야한다.
// 단, 처음에 파싱 lookahead 때문에 붙인 스페이스 문자 두 개를 뗀다.
if (state == STATE_NORMAL)
{
Assert(row != NULL);
@ -209,49 +209,49 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 가지고 있는 내용을 CSV 파일에다 저장한다.
/// \param fileName CSV 파일 이름
/// \param append true일 경우, 기존의 파일에다 덧붙인다. false인 경우에는
/// 기존의 파일 내용을 삭제하고, 새로 쓴다.
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
/// \return bool 무사히 저장했다면 true, 에러가 생긴 경우에는 false
/// \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를 리턴한다.
// 파일을 열지 못했다면, 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; i<m_Rows.size(); i++)
{
const cCsvRow& row = *((*this)[i]);
std::string line;
// 행 안의 모든 토큰을 횡단하면서...
// 행 안의 모든 토큰을 횡단하면서...
for (size_t j=0; j<row.size(); j++)
{
const std::string& token = row[j];
// 일반적인('"' 또는 ','를 포함하지 않은)
// 토큰이라면 그냥 저장하면 된다.
// 일반적인('"' 또는 ','를 포함하지 않은)
// 토큰이라면 그냥 저장하면 된다.
if (token.find_first_of(special_chars) == std::string::npos)
{
line += token;
}
// 특수문자를 포함한 토큰이라면 문자열 좌우에 '"'를 붙여주고,
// 문자열 내부의 '"'를 두 개로 만들어줘야한다.
// 특수문자를 포함한 토큰이라면 문자열 좌우에 '"'를 붙여주고,
// 문자열 내부의 '"'를 두 개로 만들어줘야한다.
else
{
line += quote;
@ -265,11 +265,11 @@ bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quot
line += quote;
}
// 마지막 셀이 아니라면 ','를 토큰의 뒤에다 붙여줘야한다.
// 마지막 셀이 아니라면 ','를 토큰의 뒤에다 붙여줘야한다.
if (j != row.size() - 1) { line += seperator; }
}
// 라인을 출력한다.
// 라인을 출력한다.
file << line << std::endl;
}
@ -277,7 +277,7 @@ bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quot
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 모든 데이터를 메모리에서 삭제한다.
/// \brief 모든 데이터를 메모리에서 삭제한다.
////////////////////////////////////////////////////////////////////////////////
void cCsvFile::Destroy()
{
@ -288,9 +288,9 @@ void cCsvFile::Destroy()
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \param index 인덱스
/// \return cCsvRow* 해당 행
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \param index 인덱스
/// \return cCsvRow* 해당 행
////////////////////////////////////////////////////////////////////////////////
cCsvRow* cCsvFile::operator [] (size_t index)
{
@ -299,9 +299,9 @@ cCsvRow* cCsvFile::operator [] (size_t index)
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \param index 인덱스
/// \return const cCsvRow* 해당 행
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \param index 인덱스
/// \return const cCsvRow* 해당 행
////////////////////////////////////////////////////////////////////////////////
const cCsvRow* cCsvFile::operator [] (size_t index) const
{
@ -310,7 +310,7 @@ const cCsvRow* cCsvFile::operator [] (size_t index) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 생성자
/// \brief 생성자
////////////////////////////////////////////////////////////////////////////////
cCsvTable::cCsvTable()
: m_CurRow(-1)
@ -318,18 +318,18 @@ cCsvTable::cCsvTable()
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 소멸자
/// \brief 소멸자
////////////////////////////////////////////////////////////////////////////////
cCsvTable::~cCsvTable()
{
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 지정된 이름의 CSV 파일을 로드한다.
/// \param fileName CSV 파일 이름
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
/// \return bool 무사히 로드했다면 true, 아니라면 false
/// \brief 지정된 이름의 CSV 파일을 로드한다.
/// \param fileName CSV 파일 이름
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
/// \return bool 무사히 로드했다면 true, 아니라면 false
////////////////////////////////////////////////////////////////////////////////
bool cCsvTable::Load(const char* fileName, const char seperator, const char quote)
{
@ -338,19 +338,19 @@ bool cCsvTable::Load(const char* fileName, const char seperator, const char quot
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 다음 행으로 넘어간다.
/// \return bool 다음 행으로 무사히 넘어간 경우 true를 반환하고, 더 이상
/// 넘어갈 행이 존재하지 않는 경우에는 false를 반환한다.
/// \brief 다음 행으로 넘어간다.
/// \return bool 다음 행으로 무사히 넘어간 경우 true를 반환하고, 더 이상
/// 넘어갈 행이 존재하지 않는 경우에는 false를 반환한다.
////////////////////////////////////////////////////////////////////////////////
bool cCsvTable::Next()
{
// 20억번 정도 호출하면 오버플로가 일어날텐데...괜찮겠지?
// 20억번 정도 호출하면 오버플로가 일어날텐데...괜찮겠지?
return ++m_CurRow < (int)m_File.GetRowCount() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 현재 행의 셀 숫자를 반환한다.
/// \return size_t 현재 행의 셀 숫자
/// \brief 현재 행의 셀 숫자를 반환한다.
/// \return size_t 현재 행의 셀 숫자
////////////////////////////////////////////////////////////////////////////////
size_t cCsvTable::ColCount() const
{
@ -358,9 +358,9 @@ size_t cCsvTable::ColCount() const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 인덱스를 이용해 int 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return int 셀 값
/// \brief 인덱스를 이용해 int 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return int 셀 값
////////////////////////////////////////////////////////////////////////////////
int cCsvTable::AsInt(size_t index) const
{
@ -371,9 +371,9 @@ int cCsvTable::AsInt(size_t index) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 인덱스를 이용해 double 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return double 셀 값
/// \brief 인덱스를 이용해 double 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return double 셀 값
////////////////////////////////////////////////////////////////////////////////
double cCsvTable::AsDouble(size_t index) const
{
@ -384,9 +384,9 @@ double cCsvTable::AsDouble(size_t index) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 인덱스를 이용해 std::string 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return const char* 셀 값
/// \brief 인덱스를 이용해 std::string 형으로 셀 값을 반환한다.
/// \param index 셀 인덱스
/// \return const char* 셀 값
////////////////////////////////////////////////////////////////////////////////
const char* cCsvTable::AsStringByIndex(size_t index) const
{
@ -397,7 +397,7 @@ const char* cCsvTable::AsStringByIndex(size_t index) const
}
////////////////////////////////////////////////////////////////////////////////
/// \brief alias를 포함해 모든 데이터를 삭제한다.
/// \brief alias를 포함해 모든 데이터를 삭제한다.
////////////////////////////////////////////////////////////////////////////////
void cCsvTable::Destroy()
{
@ -407,10 +407,10 @@ void cCsvTable::Destroy()
}
////////////////////////////////////////////////////////////////////////////////
/// \brief 현재 행을 반환한다.
/// \return const cCsvRow* 액세스가 가능한 현재 행이 존재하는 경우에는 그 행의
/// 포인터를 반환하고, 더 이상 액세스 가능한 행이 없는 경우에는 NULL을
/// 반환한다.
/// \brief 현재 행을 반환한다.
/// \return const cCsvRow* 액세스가 가능한 현재 행이 존재하는 경우에는 그 행의
/// 포인터를 반환하고, 더 이상 액세스 가능한 행이 없는 경우에는 NULL을
/// 반환한다.
////////////////////////////////////////////////////////////////////////////////
const cCsvRow* const cCsvTable::CurRow() const
{

View File

@ -12,28 +12,28 @@
////////////////////////////////////////////////////////////////////////////////
/// \class cCsvAlias
/// \brief CSV 파일을 수정했을 때 발생하는 인덱스 문제를 줄이기 위한
/// 별명 객체.
/// \brief CSV 파일을 수정했을 때 발생하는 인덱스 문제를 줄이기 위한
/// 별명 객체.
///
/// 예를 들어 0번 컬럼이 A에 관한 내용을 포함하고, 1번 컬럼이 B에 관한 내용을
/// 포함하고 있었는데...
/// 예를 들어 0번 컬럼이 A에 관한 내용을 포함하고, 1번 컬럼이 B에 관한 내용을
/// 포함하고 있었는데...
///
/// <pre>
/// int a = row.AsInt(0);
/// int b = row.AsInt(1);
/// </pre>
///
/// 그 사이에 C에 관한 내용을 포함하는 컬럼이 끼어든 경우, 하드코딩되어 있는
/// 1번을 찾아서 고쳐야 하는데, 상당히 에러가 발생하기 쉬운 작업이다.
/// 그 사이에 C에 관한 내용을 포함하는 컬럼이 끼어든 경우, 하드코딩되어 있는
/// 1번을 찾아서 고쳐야 하는데, 상당히 에러가 발생하기 쉬운 작업이다.
///
/// <pre>
/// int a = row.AsInt(0);
/// int c = row.AsInt(1);
/// int b = row.AsInt(2); <-- 이 부분을 일일이 신경써야 한다.
/// int b = row.AsInt(2); <-- 이 부분을 일일이 신경써야 한다.
/// </pre>
///
/// 이 부분을 문자열로 처리하면 유지보수에 들어가는 수고를 약간이나마 줄일 수
/// 있다.
/// 이 부분을 문자열로 처리하면 유지보수에 들어가는 수고를 약간이나마 줄일 수
/// 있다.
////////////////////////////////////////////////////////////////////////////////
class cCsvAlias
@ -47,51 +47,51 @@ private:
typedef std::map<size_t, std::string> INDEX2NAME_MAP;
#endif
NAME2INDEX_MAP m_Name2Index; ///< 셀 인덱스 대신으로 사용하기 위한 이름들
INDEX2NAME_MAP m_Index2Name; ///< 잘못된 alias를 검사하기 위한 추가적인 맵
NAME2INDEX_MAP m_Name2Index; ///< 셀 인덱스 대신으로 사용하기 위한 이름들
INDEX2NAME_MAP m_Index2Name; ///< 잘못된 alias를 검사하기 위한 추가적인 맵
public:
/// \brief 생성자
/// \brief 생성자
cCsvAlias() {}
/// \brief 소멸자
/// \brief 소멸자
virtual ~cCsvAlias() {}
public:
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
void AddAlias(const char* name, size_t index);
/// \brief 모든 데이터를 삭제한다.
/// \brief 모든 데이터를 삭제한다.
void Destroy();
/// \brief 숫자 인덱스를 이름으로 변환한다.
/// \brief 숫자 인덱스를 이름으로 변환한다.
const char* operator [] (size_t index) const;
/// \brief 이름을 숫자 인덱스로 변환한다.
/// \brief 이름을 숫자 인덱스로 변환한다.
size_t operator [] (const char* name) const;
private:
/// \brief 복사 생성자 금지
/// \brief 복사 생성자 금지
cCsvAlias(const cCsvAlias&) {}
/// \brief 대입 연산자 금지
/// \brief 대입 연산자 금지
const cCsvAlias& operator = (const cCsvAlias&) { return *this; }
};
////////////////////////////////////////////////////////////////////////////////
/// \class cCsvRow
/// \brief CSV 파일의 한 행을 캡슐화한 클래스
/// \brief CSV 파일의 한 행을 캡슐화한 클래스
///
/// CSV의 기본 포맷은 엑셀에서 보이는 하나의 셀을 ',' 문자로 구분한 것이다.
/// 하지만, 셀 안에 특수 문자로 쓰이는 ',' 문자나 '"' 문자가 들어갈 경우,
/// 모양이 약간 이상하게 변한다. 다음은 그 변화의 예이다.
/// CSV의 기본 포맷은 엑셀에서 보이는 하나의 셀을 ',' 문자로 구분한 것이다.
/// 하지만, 셀 안에 특수 문자로 쓰이는 ',' 문자나 '"' 문자가 들어갈 경우,
/// 모양이 약간 이상하게 변한다. 다음은 그 변화의 예이다.
///
/// <pre>
/// 엑셀에서 보이는 모양 | 실제 CSV 파일에 들어가있는 모양
/// 엑셀에서 보이는 모양 | 실제 CSV 파일에 들어가있는 모양
/// ---------------------+----------------------------------------------------
/// ItemPrice | ItemPrice
/// Item,Price | "Item,Price"
@ -101,9 +101,9 @@ private:
/// Item",Price | "Item"",Price"
/// </pre>
///
/// 이 예로서 다음과 같은 사항을 알 수 있다.
/// - 셀 내부에 ',' 또는 '"' 문자가 들어갈 경우, 셀 좌우에 '"' 문자가 생긴다.
/// - 셀 내부의 '"' 문자는 2개로 치환된다.
/// 이 예로서 다음과 같은 사항을 알 수 있다.
/// - 셀 내부에 ',' 또는 '"' 문자가 들어갈 경우, 셀 좌우에 '"' 문자가 생긴다.
/// - 셀 내부의 '"' 문자는 2개로 치환된다.
///
/// \sa cCsvFile
////////////////////////////////////////////////////////////////////////////////
@ -111,51 +111,51 @@ private:
class cCsvRow : public std::vector<std::string>
{
public:
/// \brief 기본 생성자
/// \brief 기본 생성자
cCsvRow() {}
/// \brief 소멸자
/// \brief 소멸자
~cCsvRow() {}
public:
/// \brief 해당 셀의 데이터를 int 형으로 반환한다.
/// \brief 해당 셀의 데이터를 int 형으로 반환한다.
int AsInt(size_t index) const { return atoi(at(index).c_str()); }
/// \brief 해당 셀의 데이터를 double 형으로 반환한다.
/// \brief 해당 셀의 데이터를 double 형으로 반환한다.
double AsDouble(size_t index) const { return atof(at(index).c_str()); }
/// \brief 해당 셀의 데이터를 문자열로 반환한다.
/// \brief 해당 셀의 데이터를 문자열로 반환한다.
const char* AsString(size_t index) const { return at(index).c_str(); }
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
int AsInt(const char* name, const cCsvAlias& alias) const {
return atoi( at(alias[name]).c_str() );
}
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
double AsDouble(const char* name, const cCsvAlias& alias) const {
return atof( at(alias[name]).c_str() );
}
/// \brief 해당하는 이름의 셀 데이터를 문자열로 반환한다.
/// \brief 해당하는 이름의 셀 데이터를 문자열로 반환한다.
const char* AsString(const char* name, const cCsvAlias& alias) const {
return at(alias[name]).c_str();
}
private:
/// \brief 복사 생성자 금지
/// \brief 복사 생성자 금지
cCsvRow(const cCsvRow&) {}
/// \brief 대입 연산자 금지
/// \brief 대입 연산자 금지
const cCsvRow& operator = (const cCsvRow&) { return *this; }
};
////////////////////////////////////////////////////////////////////////////////
/// \class cCsvFile
/// \brief CSV(Comma Seperated Values) 파일을 read/write하기 위한 클래스
/// \brief CSV(Comma Seperated Values) 파일을 read/write하기 위한 클래스
///
/// <b>sample</b>
/// <pre>
@ -179,8 +179,8 @@ private:
/// file.save("test.csv", false);
/// </pre>
///
/// \todo 파일에서만 읽어들일 것이 아니라, 메모리 소스로부터 읽는 함수도
/// 있어야 할 듯 하다.
/// \todo 파일에서만 읽어들일 것이 아니라, 메모리 소스로부터 읽는 함수도
/// 있어야 할 듯 하다.
////////////////////////////////////////////////////////////////////////////////
class cCsvFile
@ -188,55 +188,55 @@ class cCsvFile
private:
typedef std::vector<cCsvRow*> ROWS;
ROWS m_Rows; ///< 행 컬렉션
ROWS m_Rows; ///< 행 컬렉션
public:
/// \brief 생성자
/// \brief 생성자
cCsvFile() {}
/// \brief 소멸자
/// \brief 소멸자
virtual ~cCsvFile() { Destroy(); }
public:
/// \brief 지정된 이름의 CSV 파일을 로드한다.
/// \brief 지정된 이름의 CSV 파일을 로드한다.
bool Load(const char* fileName, const char seperator=',', const char quote='"');
/// \brief 가지고 있는 내용을 CSV 파일에다 저장한다.
/// \brief 가지고 있는 내용을 CSV 파일에다 저장한다.
bool Save(const char* fileName, bool append=false, char seperator=',', char quote='"') const;
/// \brief 모든 데이터를 메모리에서 삭제한다.
/// \brief 모든 데이터를 메모리에서 삭제한다.
void Destroy();
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \brief 해당하는 인덱스의 행을 반환한다.
cCsvRow* operator [] (size_t index);
/// \brief 해당하는 인덱스의 행을 반환한다.
/// \brief 해당하는 인덱스의 행을 반환한다.
const cCsvRow* operator [] (size_t index) const;
/// \brief 행의 갯수를 반환한다.
/// \brief 행의 갯수를 반환한다.
size_t GetRowCount() const { return m_Rows.size(); }
private:
/// \brief 복사 생성자 금지
/// \brief 복사 생성자 금지
cCsvFile(const cCsvFile&) {}
/// \brief 대입 연산자 금지
/// \brief 대입 연산자 금지
const cCsvFile& operator = (const cCsvFile&) { return *this; }
};
////////////////////////////////////////////////////////////////////////////////
/// \class cCsvTable
/// \brief CSV 파일을 이용해 테이블 데이터를 로드하는 경우가 많은데, 이 클래스는
/// 그 작업을 좀 더 쉽게 하기 위해 만든 유틸리티 클래스다.
/// \brief CSV 파일을 이용해 테이블 데이터를 로드하는 경우가 많은데, 이 클래스는
/// 그 작업을 좀 더 쉽게 하기 위해 만든 유틸리티 클래스다.
///
/// CSV 파일을 로드하는 경우, 숫자를 이용해 셀을 액세스해야 하는데, CSV
/// 파일의 포맷이 바뀌는 경우, 이 숫자들을 변경해줘야한다. 이 작업이 꽤
/// 신경 집중을 요구하는 데다가, 에러가 발생하기 쉽다. 그러므로 숫자로
/// 액세스하기보다는 문자열로 액세스하는 것이 약간 느리지만 낫다고 할 수 있다.
/// CSV 파일을 로드하는 경우, 숫자를 이용해 셀을 액세스해야 하는데, CSV
/// 파일의 포맷이 바뀌는 경우, 이 숫자들을 변경해줘야한다. 이 작업이 꽤
/// 신경 집중을 요구하는 데다가, 에러가 발생하기 쉽다. 그러므로 숫자로
/// 액세스하기보다는 문자열로 액세스하는 것이 약간 느리지만 낫다고 할 수 있다.
///
/// <b>sample</b>
/// <pre>
@ -259,63 +259,63 @@ private:
class cCsvTable
{
public :
cCsvFile m_File; ///< CSV 파일 객체
cCsvFile m_File; ///< CSV 파일 객체
private:
cCsvAlias m_Alias; ///< 문자열을 셀 인덱스로 변환하기 위한 객체
int m_CurRow; ///< 현재 횡단 중인 행 번호
cCsvAlias m_Alias; ///< 문자열을 셀 인덱스로 변환하기 위한 객체
int m_CurRow; ///< 현재 횡단 중인 행 번호
public:
/// \brief 생성자
/// \brief 생성자
cCsvTable();
/// \brief 소멸자
/// \brief 소멸자
virtual ~cCsvTable();
public:
/// \brief 지정된 이름의 CSV 파일을 로드한다.
/// \brief 지정된 이름의 CSV 파일을 로드한다.
bool Load(const char* fileName, const char seperator=',', const char quote='"');
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
void AddAlias(const char* name, size_t index) { m_Alias.AddAlias(name, index); }
/// \brief 다음 행으로 넘어간다.
/// \brief 다음 행으로 넘어간다.
bool Next();
/// \brief 현재 행의 셀 숫자를 반환한다.
/// \brief 현재 행의 셀 숫자를 반환한다.
size_t ColCount() const;
/// \brief 인덱스를 이용해 int 형으로 셀값을 반환한다.
/// \brief 인덱스를 이용해 int 형으로 셀값을 반환한다.
int AsInt(size_t index) const;
/// \brief 인덱스를 이용해 double 형으로 셀값을 반환한다.
/// \brief 인덱스를 이용해 double 형으로 셀값을 반환한다.
double AsDouble(size_t index) const;
/// \brief 인덱스를 이용해 std::string 형으로 셀값을 반환한다.
/// \brief 인덱스를 이용해 std::string 형으로 셀값을 반환한다.
const char* AsStringByIndex(size_t index) const;
/// \brief 셀 이름을 이용해 int 형으로 셀값을 반환한다.
/// \brief 셀 이름을 이용해 int 형으로 셀값을 반환한다.
int AsInt(const char* name) const { return AsInt(m_Alias[name]); }
/// \brief 셀 이름을 이용해 double 형으로 셀값을 반환한다.
/// \brief 셀 이름을 이용해 double 형으로 셀값을 반환한다.
double AsDouble(const char* name) const { return AsDouble(m_Alias[name]); }
/// \brief 셀 이름을 이용해 std::string 형으로 셀값을 반환한다.
/// \brief 셀 이름을 이용해 std::string 형으로 셀값을 반환한다.
const char* AsString(const char* name) const { return AsStringByIndex(m_Alias[name]); }
/// \brief alias를 포함해 모든 데이터를 삭제한다.
/// \brief alias를 포함해 모든 데이터를 삭제한다.
void Destroy();
private:
/// \brief 현재 행을 반환한다.
/// \brief 현재 행을 반환한다.
const cCsvRow* const CurRow() const;
/// \brief 복사 생성자 금지
/// \brief 복사 생성자 금지
cCsvTable(const cCsvTable&) {}
/// \brief 대입 연산자 금지
/// \brief 대입 연산자 금지
const cCsvTable& operator = (const cCsvTable&) { return *this; }
};

View File

@ -2,9 +2,9 @@
#ifndef __INC_METIN2_DB_DBMANAGER_H__
#define __INC_METIN2_DB_DBMANAGER_H__
// 디비 커넥션 클래스의 목적은... 디비에 접속해서 쿼리보내고 결과 받아오는
// 모든 일들을 처리한다.
// 코드 by 꼬붕 후로그래머 아노아~ = _=)b
// 디비 커넥션 클래스의 목적은... 디비에 접속해서 쿼리보내고 결과 받아오는
// 모든 일들을 처리한다.
// 코드 by 꼬붕 후로그래머 아노아~ = _=)b
#include <libsql/include/CAsyncSQL.h>
#define SQL_SAFE_LENGTH(size) (size * 2 + 1)

View File

@ -242,7 +242,7 @@ void CGuildManager::ResultRanking(MYSQL_RES * pRes)
void CGuildManager::Update()
{
ProcessReserveWar(); // 예약 전쟁 처리
ProcessReserveWar(); // 예약 전쟁 처리
time_t now = CClientManager::instance().GetCurrentTime();
@ -462,7 +462,7 @@ void CGuildManager::RemoveWar(DWORD GID1, DWORD GID2)
}
//
// 길드전 비정상 종료 및 필드전 종료
// 길드전 비정상 종료 및 필드전 종료
//
void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw)
{
@ -493,7 +493,7 @@ void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw)
bool bDraw = false;
if (!bForceDraw) // 강제 무승부가 아닐 경우에는 점수를 체크한다.
if (!bForceDraw) // 강제 무승부가 아닐 경우에는 점수를 체크한다.
{
if (pData->iScore[0] > pData->iScore[1])
{
@ -508,7 +508,7 @@ void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw)
else
bDraw = true;
}
else // 강제 무승부일 경우에는 무조건 무승부
else // 강제 무승부일 경우에는 무조건 무승부
bDraw = true;
if (bDraw)
@ -516,14 +516,14 @@ void CGuildManager::WarEnd(DWORD GID1, DWORD GID2, bool bForceDraw)
else
ProcessWinLose(win_guild, lose_guild);
// DB 서버에서 자체적으로 끝낼 때도 있기 때문에 따로 패킷을 보내줘야 한다.
// 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, int lWarPrice)
{
@ -571,7 +571,7 @@ void CGuildManager::RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bD
void CGuildManager::RecvWarEnd(DWORD GID1, DWORD GID2)
{
SPDLOG_DEBUG("GuildWar: RecvWarEnded : {} vs {}", GID1, GID2);
WarEnd(GID1, GID2, true); // 무조건 비정상 종료 시켜야 한다.
WarEnd(GID1, GID2, true); // 무조건 비정상 종료 시켜야 한다.
}
void CGuildManager::StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve)
@ -745,7 +745,7 @@ void CGuildManager::ChangeLadderPoint(DWORD GID, int change)
SPDLOG_DEBUG("GuildManager::ChangeLadderPoint {} {}", GID, r.ladder_point);
SPDLOG_DEBUG("{}", buf);
// Packet 보내기
// Packet 보내기
TPacketGuildLadder p;
p.dwGuild = GID;
@ -808,7 +808,7 @@ void CGuildManager::WithdrawMoney(CPeer* peer, DWORD dwGuild, INT iGold)
return;
}
// 돈이있으니 출금하고 올려준다
// 돈이있으니 출금하고 올려준다
if (it->second.gold >= iGold)
{
it->second.gold -= iGold;
@ -839,7 +839,7 @@ void CGuildManager::WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT iGo
}
//
// 예약 길드전(관전자가 배팅할 수 있다)
// 예약 길드전(관전자가 배팅할 수 있다)
//
const int c_aiScoreByLevel[GUILD_MAX_LEVEL+1] =
{
@ -869,7 +869,7 @@ const int c_aiScoreByLevel[GUILD_MAX_LEVEL+1] =
const int c_aiScoreByRanking[GUILD_RANK_MAX_NUM+1] =
{
0,
55000, // 1위
55000, // 1위
50000,
45000,
40000,
@ -878,7 +878,7 @@ const int c_aiScoreByRanking[GUILD_RANK_MAX_NUM+1] =
28000,
24000,
21000,
18000, // 10위
18000, // 10위
15000,
12000,
10000,
@ -888,7 +888,7 @@ const int c_aiScoreByRanking[GUILD_RANK_MAX_NUM+1] =
3000,
2000,
1000,
500 // 20위
500 // 20위
};
void CGuildManager::BootReserveWar()
@ -932,8 +932,8 @@ void CGuildManager::BootReserveWar()
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분 이하 남은 예약 길드전도 무승부 처리한다. (각자의 배팅액을 돌려준다)
// 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)
{
@ -1010,7 +1010,7 @@ bool CGuildManager::ReserveWar(TPacketGuildWar * p)
int lvp, rkp, alv, mc;
// 파워 계산
// 파워 계산
TGuild & k1 = TouchGuild(GID1);
lvp = c_aiScoreByLevel[std::min<size_t>(GUILD_MAX_LEVEL, k1.level)];
@ -1026,7 +1026,7 @@ bool CGuildManager::ReserveWar(TPacketGuildWar * p)
t.lPowerFrom = (int) polyPower.Eval();
SPDLOG_DEBUG("GuildWar: {} lvp {} rkp {} alv {} mc {} power {}", GID1, lvp, rkp, alv, mc, t.lPowerFrom);
// 파워 계산
// 파워 계산
TGuild & k2 = TouchGuild(GID2);
lvp = c_aiScoreByLevel[std::min<size_t>(GUILD_MAX_LEVEL, k2.level)];
@ -1042,7 +1042,7 @@ bool CGuildManager::ReserveWar(TPacketGuildWar * p)
t.lPowerTo = (int) polyPower.Eval();
SPDLOG_DEBUG("GuildWar: {} lvp {} rkp {} alv {} mc {} power {}", GID2, lvp, rkp, alv, mc, t.lPowerTo);
// 핸디캡 계산
// 핸디캡 계산
if (t.lPowerTo > t.lPowerFrom)
{
polyHandicap.SetVar("pA", t.lPowerTo);
@ -1057,7 +1057,7 @@ bool CGuildManager::ReserveWar(TPacketGuildWar * p)
t.lHandicap = (int) polyHandicap.Eval();
SPDLOG_DEBUG("GuildWar: handicap {}", t.lHandicap);
// 쿼리
// 쿼리
char szQuery[512];
snprintf(szQuery, sizeof(szQuery),
@ -1094,7 +1094,7 @@ void CGuildManager::ProcessReserveWar()
CGuildWarReserve * pk = it2->second;
TGuildWarReserve & r = pk->GetDataRef();
if (!r.bStarted && r.dwTime - 1800 <= dwCurTime) // 30분 전부터 알린다.
if (!r.bStarted && r.dwTime - 1800 <= dwCurTime) // 30분 전부터 알린다.
{
int iMin = (int) ceil((int)(r.dwTime - dwCurTime) / 60.0);
@ -1135,9 +1135,9 @@ void CGuildManager::ProcessReserveWar()
pk->SetLastNoticeMin(iMin);
if (!g_stLocale.compare("euckr"))
CClientManager::instance().SendNotice("%s 길드와 %s 길드의 전쟁이 약 %d분 후 시작 됩니다!", r_1.szName, r_2.szName, iMin);
CClientManager::instance().SendNotice("The war between guild %s and guild %s will begin in approximately %d minutes!", 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);
CClientManager::instance().SendNotice("The war between guild %s and guild %s will begin in approximately %d minutes!", r_1.szName, r_2.szName, iMin);
}
}
}
@ -1239,7 +1239,7 @@ void CGuildWarReserve::Initialize()
void CGuildWarReserve::OnSetup(CPeer * peer)
{
if (m_data.bStarted) // 이미 시작된 것은 보내지 않는다.
if (m_data.bStarted) // 이미 시작된 것은 보내지 않는다.
return;
FSendPeerWar(m_data.bType, GUILD_WAR_RESERVE, m_data.dwGuildFrom, m_data.dwGuildTo) (peer);
@ -1325,8 +1325,8 @@ bool CGuildWarReserve::Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild)
}
//
// 무승부 처리: 대부분 승부가 나야 정상이지만, 서버 문제 등 특정 상황일 경우에는
// 무승부 처리가 있어야 한다.
// 무승부 처리: 대부분 승부가 나야 정상이지만, 서버 문제 등 특정 상황일 경우에는
// 무승부 처리가 있어야 한다.
//
void CGuildWarReserve::Draw()
{
@ -1458,7 +1458,7 @@ void CGuildWarReserve::End(int iScoreFrom, int iScoreTo)
double ratio = (double) it->second.second / dwWinnerBet;
// 10% 세금 공제 후 분배
// 10% 세금 공제 후 분배
SPDLOG_DEBUG("WAR_REWARD: {} {} ratio {}", it->first.c_str(), it->second.second, ratio);
DWORD dwGold = (DWORD) (dwTotalBet * ratio * 0.9);

View File

@ -150,7 +150,7 @@ class CGuildWarReserve
void SetLastNoticeMin(int iMin) { m_iLastNoticeMin = iMin; }
private:
CGuildWarReserve(); // 기본 생성자를 사용하지 못하도록 의도적으로 구현하지 않음
CGuildWarReserve(); // 기본 생성자를 사용하지 못하도록 의도적으로 구현하지 않음
TGuildWarReserve m_data;
// <login, <guild, gold>>
@ -235,7 +235,7 @@ class CGuildManager : public singleton<CGuildManager>
std::map<DWORD, TGuild> m_map_kGuild;
std::map<DWORD, std::map<DWORD, time_t> > m_mapGuildWarEndTime;
std::set<TGuildDeclareInfo> m_DeclareMap; // 선전 포고 상태를 저장
std::set<TGuildDeclareInfo> m_DeclareMap; // 선전 포고 상태를 저장
std::map<DWORD, std::map<DWORD, TGuildWarInfo> > m_WarMap;
typedef std::pair<time_t, TGuildWarPQElement *> stPairGuildWar;

View File

@ -54,19 +54,19 @@ void ItemAwardManager::Load(SQLMsg * pMsg)
if (row[col])
{
strlcpy(kData->szWhy, row[col], sizeof(kData->szWhy));
//게임 중에 why콜룸에 변동이 생기면
char* whyStr = kData->szWhy; //why 콜룸 읽기
char cmdStr[100] = ""; //why콜룸에서 읽은 값을 임시 문자열에 복사해둠
strcpy(cmdStr,whyStr); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
//게임 중에 why콜룸에 변동이 생기면
char* whyStr = kData->szWhy; //why 콜룸 읽기
char cmdStr[100] = ""; //why콜룸에서 읽은 값을 임시 문자열에 복사해둠
strcpy(cmdStr,whyStr); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
char command[20] = "";
strcpy(command,CClientManager::instance().GetCommand(cmdStr).c_str()); // command 얻기
strcpy(command,CClientManager::instance().GetCommand(cmdStr).c_str()); // command 얻기
//SPDLOG_ERROR("{}, {}",pItemAward->dwID,command);
if( !(strcmp(command,"GIFT") )) // command 가 GIFT이면
if( !(strcmp(command,"GIFT") )) // command 가 GIFT이면
{
TPacketItemAwardInfromer giftData;
strcpy(giftData.login, kData->szLogin); //로그인 아이디 복사
strcpy(giftData.command, command); //명령어 복사
giftData.vnum = kData->dwVnum; //아이템 vnum도 복사
strcpy(giftData.login, kData->szLogin); //로그인 아이디 복사
strcpy(giftData.command, command); //명령어 복사
giftData.vnum = kData->dwVnum; //아이템 vnum도 복사
CClientManager::instance().ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer));
}
}

View File

@ -28,11 +28,11 @@ std::string g_stPlayerDBName = "";
BOOL g_test_server = false;
//단위 초
//단위 초
int g_iPlayerCacheFlushSeconds = 60*7;
int g_iItemCacheFlushSeconds = 60*5;
//g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다.
//g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다.
int g_iLogoutSeconds = 60*10;
@ -116,13 +116,13 @@ int main()
void emptybeat(LPHEART heart, int pulse)
{
if (!(pulse % heart->passes_per_sec)) // 1초에 한번
if (!(pulse % heart->passes_per_sec)) // 1초에 한번
{
}
}
//
// @version 05/06/13 Bang2ni - 아이템 가격정보 캐시 flush timeout 설정 추가.
// @version 05/06/13 Bang2ni - 아이템 가격정보 캐시 flush timeout 설정 추가.
//
int Start()
{

View File

@ -253,7 +253,7 @@ namespace marriage
void CManager::OnSetup(CPeer* peer)
{
// 결혼한 사람들 보내기
// 결혼한 사람들 보내기
for (itertype(m_Marriages) it = m_Marriages.begin(); it != m_Marriages.end(); ++it)
{
TMarriage* pMarriage = *it;
@ -280,7 +280,7 @@ namespace marriage
}
}
// 결혼식 보내기
// 결혼식 보내기
for (itertype(m_mapRunningWedding) it = m_mapRunningWedding.begin(); it != m_mapRunningWedding.end(); ++it)
{
const TWedding& t = it->second;

View File

@ -49,7 +49,7 @@ namespace marriage
DWORD pid2;
int love_point;
DWORD time;
BYTE is_married; // false : 약혼 상태, true : 결혼 상태
BYTE is_married; // false : 약혼 상태, true : 결혼 상태
std::string name1;
std::string name2;

View File

@ -233,7 +233,7 @@ bool CMonarch::SetMonarch(const char * name)
}
delete pMsg;
//db에 입력
//db에 입력
snprintf(szQuery, sizeof(szQuery),
"REPLACE INTO monarch (empire, name, windate, money) VALUES(%d, %d, now(), %ld)", Empire, p->pid[Empire], p->money[Empire]);

View File

@ -64,9 +64,9 @@ class CPeer : public CPeerBase
BYTE m_bChannel;
DWORD m_dwHandle;
DWORD m_dwUserCount;
WORD m_wListenPort; // 게임서버가 클라이언트를 위해 listen 하는 포트
WORD m_wP2PPort; // 게임서버가 게임서버 P2P 접속을 위해 listen 하는 포트
LONG m_alMaps[MAP_ALLOW_MAX_LEN]; // 어떤 맵을 관장하고 있는가?
WORD m_wListenPort; // 게임서버가 클라이언트를 위해 listen 하는 포트
WORD m_wP2PPort; // 게임서버가 게임서버 P2P 접속을 위해 listen 하는 포트
LONG m_alMaps[MAP_ALLOW_MAX_LEN]; // 어떤 맵을 관장하고 있는가?
TItemIDRangeTable m_itemRange;
TItemIDRangeTable m_itemSpareRange;

View File

@ -20,7 +20,7 @@ CPrivManager::~CPrivManager()
}
//
// @version 05/06/07 Bang2ni - 중복적으로 보너스가 적용 된 길드에 대한 처리
// @version 05/06/07 Bang2ni - 중복적으로 보너스가 적용 된 길드에 대한 처리
//
void CPrivManager::Update()
{
@ -37,8 +37,8 @@ void CPrivManager::Update()
typeof(m_aPrivGuild[p->type].begin()) it = m_aPrivGuild[p->type].find(p->guild_id);
// ADD_GUILD_PRIV_TIME
// 길드에 중복적으로 보너스가 설정되었을 경우 map 의 value 가 갱신(수정) 되었으므로
// TPrivGuildData 의 포인터가 같을때 실제로 삭제해 주고 게임서버들에게 cast 해 준다.
// 길드에 중복적으로 보너스가 설정되었을 경우 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);
@ -113,7 +113,7 @@ void CPrivManager::AddCharPriv(DWORD pid, BYTE type, int value)
}
//
// @version 05/06/07 Bang2ni - 이미 보너스가 적용 된 길드에 보너스 설정
// @version 05/06/07 Bang2ni - 이미 보너스가 적용 된 길드에 보너스 설정
//
void CPrivManager::AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t duration_sec)
{
@ -131,8 +131,8 @@ void CPrivManager::AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t dur
m_pqPrivGuild.push(std::make_pair(end, p));
// ADD_GUILD_PRIV_TIME
// 이미 보너스가 설정되 있다면 map 의 value 를 갱신해 준다.
// 이전 value 의 포인터는 priority queue 에서 삭제될 때 해제된다.
// 이미 보너스가 설정되 있다면 map 의 value 를 갱신해 준다.
// 이전 value 의 포인터는 priority queue 에서 삭제될 때 해제된다.
if (it != m_aPrivGuild[type].end())
it->second = p;
else
@ -158,8 +158,8 @@ void CPrivManager::AddEmpirePriv(BYTE empire, BYTE type, int value, time_t durat
time_t now = CClientManager::instance().GetCurrentTime();
time_t end = now+duration_sec;
// 이전 설정값 무효화
// priority_queue에 들어있는 pointer == m_aaPrivEmpire[type][empire]
// 이전 설정값 무효화
// priority_queue에 들어있는 pointer == m_aaPrivEmpire[type][empire]
{
if (m_aaPrivEmpire[type][empire])
m_aaPrivEmpire[type][empire]->bRemoved = true;
@ -177,7 +177,7 @@ void CPrivManager::AddEmpirePriv(BYTE empire, BYTE type, int value, time_t durat
}
/**
* @version 05/06/08 Bang2ni -
* @version 05/06/08 Bang2ni -
*/
struct FSendChangeGuildPriv
{

View File

@ -23,7 +23,7 @@ struct TPrivEmpireData
};
/**
* @version 05/06/08 Bang2ni -
* @version 05/06/08 Bang2ni -
*/
struct TPrivGuildData
{
@ -33,7 +33,7 @@ struct TPrivGuildData
DWORD guild_id;
// ADD_GUILD_PRIV_TIME
time_t end_time_sec; ///< 지속시간
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 )
@ -53,7 +53,7 @@ struct TPrivCharData
};
/**
* @version 05/06/08 Bang2ni - Guild privilege
* @version 05/06/08 Bang2ni - Guild privilege
*/
class CPrivManager : public singleton<CPrivManager>
{

View File

@ -25,23 +25,23 @@ 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 할변수
int cutAt; //자르는위치
int index = 0; //문자열인덱스
string* strResult = new string[30]; //결과return 할변수
//strTok을찾을때까지반복
//strTok을찾을때까지반복
while ((cutAt = strOrigin.find_first_of(strTok)) != strOrigin.npos)
{
if (cutAt > 0) //자르는위치가0보다크면(성공시)
if (cutAt > 0) //자르는위치가0보다크면(성공시)
{
strResult[index++] = strOrigin.substr(0, cutAt); //결과배열에추가
strResult[index++] = strOrigin.substr(0, cutAt); //결과배열에추가
}
strOrigin = strOrigin.substr(cutAt+1); //원본은자른부분제외한나머지
strOrigin = strOrigin.substr(cutAt+1); //원본은자른부분제외한나머지
}
if(strOrigin.length() > 0) //원본이아직남았으면
if(strOrigin.length() > 0) //원본이아직남았으면
{
strResult[index++] = strOrigin.substr(0, cutAt); //나머지를결과배열에추가
strResult[index++] = strOrigin.substr(0, cutAt); //나머지를결과배열에추가
}
for( int i=0;i<index;i++)
@ -49,7 +49,7 @@ static string* StringSplit(string strOrigin, string strTok)
strResult[i] = trim(strResult[i]);
}
return strResult; //결과return
return strResult; //결과return
}
@ -60,25 +60,25 @@ int get_Item_Type_Value(string inputString)
"ITEM_ARMOR", "ITEM_USE",
"ITEM_AUTOUSE", "ITEM_MATERIAL",
"ITEM_SPECIAL", "ITEM_TOOL",
"ITEM_LOTTERY", "ITEM_ELK", //10개
"ITEM_LOTTERY", "ITEM_ELK", //10개
"ITEM_METIN", "ITEM_CONTAINER",
"ITEM_FISH", "ITEM_ROD",
"ITEM_RESOURCE", "ITEM_CAMPFIRE",
"ITEM_UNIQUE", "ITEM_SKILLBOOK",
"ITEM_QUEST", "ITEM_POLYMORPH", //20개
"ITEM_QUEST", "ITEM_POLYMORPH", //20개
"ITEM_TREASURE_BOX", "ITEM_TREASURE_KEY",
"ITEM_SKILLFORGET", "ITEM_GIFTBOX",
"ITEM_PICK", "ITEM_HAIR",
"ITEM_TOTEM", "ITEM_BLEND",
"ITEM_COSTUME", "ITEM_DS", //30개
"ITEM_COSTUME", "ITEM_DS", //30개
"ITEM_SPECIAL_DS", "ITEM_EXTRACT",
"ITEM_SECONDARY_COIN", //33개
"ITEM_SECONDARY_COIN", //33개
"ITEM_RING",
"ITEM_BELT", //35개 (EItemTypes 값으로 치면 34)
"ITEM_BELT", //35개 (EItemTypes 값으로 치면 34)
};
@ -159,8 +159,8 @@ int get_Item_SubType_Value(int type_value, string inputString)
arSub29, //30
arSub31, //31
0, //32
0, //33 반지
0, //34 벨트
0, //33 반지
0, //34 벨트
};
static int arNumberOfSubtype[_countof(arSubType)] = {
0,
@ -196,21 +196,21 @@ int get_Item_SubType_Value(int type_value, string inputString)
sizeof(arSub29)/sizeof(arSub29[0]),
sizeof(arSub31)/sizeof(arSub31[0]),
0, // 32
0, // 33 반지
0, // 34 벨트
0, // 33 반지
0, // 34 벨트
};
assert(_countof(arSubType) > type_value && "Subtype rule: Out of range!!");
// assert 안 먹히는 듯..
// assert 안 먹히는 듯..
if (_countof(arSubType) <= type_value)
{
SPDLOG_ERROR("SubType : Out of range!! (type_value: {}, count of registered subtype: {}", type_value, _countof(arSubType));
return -1;
}
//아이템 타입의 서브타입 어레이가 존재하는지 알아보고, 없으면 0 리턴
//아이템 타입의 서브타입 어레이가 존재하는지 알아보고, 없으면 0 리턴
if (arSubType[type_value]==0) {
return 0;
}
@ -246,13 +246,13 @@ int get_Item_AntiFlag_Value(string inputString)
int retValue = 0;
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arAntiFlag)/sizeof(arAntiFlag[0]);i++) {
string tempString = arAntiFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -275,13 +275,13 @@ int get_Item_Flag_Value(string inputString)
int retValue = 0;
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arFlag)/sizeof(arFlag[0]);i++) {
string tempString = arFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -303,13 +303,13 @@ int get_Item_WearFlag_Value(string inputString)
int retValue = 0;
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arWearrFlag)/sizeof(arWearrFlag[0]);i++) {
string tempString = arWearrFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -329,13 +329,13 @@ int get_Item_Immune_Value(string inputString)
string arImmune[] = {"PARA","CURSE","STUN","SLEEP","SLOW","POISON","TERROR"};
int retValue = 0;
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arImmune)/sizeof(arImmune[0]);i++) {
string tempString = arImmune[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -414,7 +414,7 @@ int get_Item_ApplyType_Value(string inputString)
}
//몬스터 프로토도 읽는다.
//몬스터 프로토도 읽는다.
int get_Mob_Rank_Value(string inputString)
@ -508,13 +508,13 @@ int get_Mob_AIFlag_Value(string inputString)
int retValue = 0;
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arAIFlag)/sizeof(arAIFlag[0]);i++) {
string tempString = arAIFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -533,13 +533,13 @@ int get_Mob_RaceFlag_Value(string inputString)
"ATT_ELEC","ATT_FIRE","ATT_ICE","ATT_WIND","ATT_EARTH","ATT_DARK"};
int retValue = 0;
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arRaceFlag)/sizeof(arRaceFlag[0]);i++) {
string tempString = arRaceFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -557,13 +557,13 @@ int get_Mob_ImmuneFlag_Value(string inputString)
string arImmuneFlag[] = {"STUN","SLOW","FALL","CURSE","POISON","TERROR", "REFLECT"};
int retValue = 0;
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arImmuneFlag)/sizeof(arImmuneFlag[0]);i++) {
string tempString = arImmuneFlag[i];
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
{
string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
retValue = retValue + pow((float)2,(float)i);
}
@ -581,14 +581,14 @@ int get_Mob_ImmuneFlag_Value(string inputString)
#ifndef __DUMP_PROTO__
//몹 테이블을 셋팅해준다.
//몹 테이블을 셋팅해준다.
bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable,std::map<int,const char*> &nameMap)
{
int col = 0;
str_to_number(mobTable->dwVnum, csvTable.AsStringByIndex(col++));
strlcpy(mobTable->szName, csvTable.AsStringByIndex(col++), sizeof(mobTable->szName));
//3. 지역별 이름 넣어주기.
//3. 지역별 이름 넣어주기.
map<int,const char*>::iterator it;
it = nameMap.find(mobTable->dwVnum);
if (it != nameMap.end()) {
@ -749,11 +749,11 @@ bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<in
col = col + 1;
}
// vnum 및 vnum range 읽기.
// vnum 및 vnum range 읽기.
{
std::string s(csvTable.AsStringByIndex(0));
int pos = s.find("~");
// vnum 필드에 '~'가 없다면 패스
// vnum 필드에 '~'가 없다면 패스
if (std::string::npos == pos)
{
itemTable->dwVnum = dataArray[0];
@ -777,7 +777,7 @@ bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<in
}
strlcpy(itemTable->szName, csvTable.AsStringByIndex(1), sizeof(itemTable->szName));
//지역별 이름 넣어주기.
//지역별 이름 넣어주기.
map<int,const char*>::iterator it;
it = nameMap.find(itemTable->dwVnum);
if (it != nameMap.end()) {

View File

@ -6,8 +6,8 @@
#include "CsvReader.h"
//csv 파일을 읽어와서 아이템 테이블에 넣어준다.
void putItemIntoTable(); //(테이블, 테스트여부)
//csv 파일을 읽어와서 아이템 테이블에 넣어준다.
void putItemIntoTable(); //(테이블, 테스트여부)
int get_Item_Type_Value(std::string inputString);
int get_Item_SubType_Value(int type_value, std::string inputString);
@ -19,7 +19,7 @@ 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);

View File

@ -2,7 +2,7 @@
#define __INC_METIN_II_DB_QID_H__
/**
* @version 05/06/10 Bang2ni - (QID_ITEMPRICE_XXX)
* @version 05/06/10 Bang2ni - (QID_ITEMPRICE_XXX)
*/
enum QID
{
@ -28,10 +28,10 @@ enum QID
QID_GUILD_RANKING, // 19
// MYSHOP_PRICE_LIST
QID_ITEMPRICE_SAVE, ///< 20, 아이템 가격정보 저장 쿼리
QID_ITEMPRICE_DESTROY, ///< 21, 아이템 가격정보 삭제 쿼리
QID_ITEMPRICE_LOAD_FOR_UPDATE, ///< 22, 가격정보 업데이트를 위한 아이템 가격정보 로드 쿼리
QID_ITEMPRICE_LOAD, ///< 22, 아이템 가격정보 로드 쿼리
QID_ITEMPRICE_SAVE, ///< 20, 아이템 가격정보 저장 쿼리
QID_ITEMPRICE_DESTROY, ///< 21, 아이템 가격정보 저장 쿼리
QID_ITEMPRICE_LOAD_FOR_UPDATE, ///< 22, 가격정보 업데이트를 위한 아이템 가격정보 로드 쿼리
QID_ITEMPRICE_LOAD, ///< 22, 아이템 가격정보 로드 쿼리
// END_OF_MYSHOP_PRICE_LIST
};

View File

@ -28,7 +28,7 @@ void CGrid::Clear()
int CGrid::FindBlank(int w, int h)
{
// 크기가 더 크다면 확인할 필요 없이 그냥 리턴
// 크기가 더 크다면 확인할 필요 없이 그냥 리턴
if (w > m_iWidth || h > m_iHeight)
return -1;
@ -86,7 +86,7 @@ bool CGrid::IsEmpty(int iPos, int w, int h)
{
int iRow = iPos / m_iWidth;
// Grid 안쪽인가를 먼저 검사
// Grid 안쪽인가를 먼저 검사
if (iRow + h > m_iHeight)
return false;

View File

@ -102,14 +102,14 @@ EVENTFUNC(battle_arena_event)
case 0:
{
++pInfo->state;
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 5분 남았습니다!!!"));
BroadcastNotice(LC_TEXT("Five minutes until the monsters attack!!!"));
}
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*4);
case 1:
{
++pInfo->state;
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 1분 남았습니다!!!"));
BroadcastNotice(LC_TEXT("One minute left until the monsters attack!!!"));
}
return test_server ? PASSES_PER_SEC(10) : PASSES_PER_SEC(60);
@ -119,7 +119,7 @@ EVENTFUNC(battle_arena_event)
pInfo->wait_count = 0;
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0);
BroadcastNotice(LC_TEXT("몬스터들이 성을 공격하기 시작했습니다."));
BroadcastNotice(LC_TEXT("Monsters have started attacking your castle."));
LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex);
@ -141,7 +141,7 @@ EVENTFUNC(battle_arena_event)
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
{
pInfo->state = 6;
SendNoticeMap(LC_TEXT("중앙 제단에 악의 기운이 모여듭니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("Evil energy gathers at the center altar."), pInfo->nMapIndex, false);
}
else
{
@ -150,7 +150,7 @@ EVENTFUNC(battle_arena_event)
if ( pInfo->wait_count >= 5 )
{
pInfo->state++;
SendNoticeMap(LC_TEXT("몬스터들이 물러갈 조짐을 보입니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("The monsters are showing signs of retreating."), pInfo->nMapIndex, false);
}
else
{
@ -163,8 +163,8 @@ EVENTFUNC(battle_arena_event)
case 4 :
{
pInfo->state++;
SendNoticeMap(LC_TEXT("몬스터들이 물러가기 시작했습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("The monsters began to retreat."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("After a while, you'll return to the village."), pInfo->nMapIndex, false);
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
}
@ -189,8 +189,8 @@ EVENTFUNC(battle_arena_event)
pInfo->state++;
pInfo->wait_count = 0;
SendNoticeMap(LC_TEXT("몬스터들의 대장이 나타났습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("30분 내로 귀목령주를 물리쳐주세요."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("The monster boss has appeared."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("You have 30 minutes to defeat the boss."), pInfo->nMapIndex, false);
CBattleArena::instance().SpawnLastBoss();
}
@ -200,8 +200,8 @@ EVENTFUNC(battle_arena_event)
{
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
{
SendNoticeMap(LC_TEXT("귀목령주와 그의 부하들을 모두 물리쳤습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("You have defeated the boss and all of his minions."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("After a while, you'll return to the village."), pInfo->nMapIndex, false);
pInfo->state = 5;
@ -212,8 +212,8 @@ EVENTFUNC(battle_arena_event)
if ( pInfo->wait_count >= 6 )
{
SendNoticeMap(LC_TEXT("귀목령주가 퇴각하였습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("The monsters have retreated."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("After a while, you'll return to the village."), pInfo->nMapIndex, false);
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
SECTREE_MANAGER::instance().PurgeStonesInMap(pInfo->nMapIndex);
@ -243,9 +243,9 @@ bool CBattleArena::Start(int nEmpire)
m_nEmpire = nEmpire;
char szBuf[1024];
snprintf(szBuf, sizeof(szBuf), LC_TEXT("%s의 성으로 몬스터들이 진군하고 있습니다."), EMPIRE_NAME(m_nEmpire));
snprintf(szBuf, sizeof(szBuf), LC_TEXT("Monsters are marching on %s's castle."), EMPIRE_NAME(m_nEmpire));
BroadcastNotice(szBuf);
BroadcastNotice(LC_TEXT("10분 뒤 성을 공격할 예정입니다."));
BroadcastNotice(LC_TEXT("We will attack the castle in 10 minutes."));
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);

View File

@ -91,7 +91,7 @@ EVENTFUNC( DragonLair_Collapse_Event )
if (0 == pInfo->step)
{
char buf[512];
snprintf(buf, 512, LC_TEXT("용가리가 %d 초만에 죽어써효ㅠㅠ"), pInfo->pLair->GetEstimatedTime());
snprintf(buf, 512, LC_TEXT("Dragon died in %d seconds."), pInfo->pLair->GetEstimatedTime());
SendNoticeMap(buf, pInfo->InstanceMapIndex, true);
pInfo->step++;
@ -146,7 +146,7 @@ DWORD CDragonLair::GetEstimatedTime() const
void CDragonLair::OnDragonDead(LPCHARACTER pDragon)
{
SPDLOG_DEBUG("DragonLair: 도라곤이 죽어써효");
SPDLOG_DEBUG("DragonLair: Dragon is dead and stale");
LogManager::instance().DragonSlayLog( GuildID_, pDragon->GetMobTable().dwVnum, StartTime_, get_global_time() );
}
@ -237,7 +237,7 @@ void CDragonLairManager::OnDragonDead(LPCHARACTER pDragon, DWORD KillerGuildID)
iter->second->OnDragonDead( pDragon );
// 애들 다 집으로 보내고 맵 없애기
// 애들 다 집으로 보내고 맵 없애기
tag_DragonLair_Collapse_EventInfo* info;
info = AllocEventInfo<tag_DragonLair_Collapse_EventInfo>();

View File

@ -31,7 +31,7 @@ int Gamble(std::vector<float>& vec_probs)
return -1;
}
// 가중치 테이블(prob_lst)을 받아 random_set.size()개의 index를 선택하여 random_set을 return
// 가중치 테이블(prob_lst)을 받아 random_set.size()개의 index를 선택하여 random_set을 return
bool MakeDistinctRandomNumberSet(std::list <float> prob_lst, OUT std::vector<int>& random_set)
{
int size = prob_lst.size();
@ -67,11 +67,11 @@ bool MakeDistinctRandomNumberSet(std::list <float> prob_lst, OUT std::vector<int
return true;
}
/* 용혼석 Vnum에 대한 comment
* ITEM VNUM을 10 , FEDCBA라고
* FE : . D :
* C : B :
* A : ...
/* 용혼석 Vnum에 대한 comment
* ITEM VNUM을 10 , FEDCBA라고
* FE : . D :
* C : B :
* A : ...
*/
BYTE GetType(DWORD dwVnum)
@ -169,7 +169,7 @@ bool DSManager::RefreshItemAttributes(LPITEM pDS)
return false;
}
// add_min과 add_max는 더미로 읽음.
// 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))
{
@ -315,14 +315,14 @@ 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("착용 중인 용혼석은 추출할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The Dragon Stone cannot be removed."));
return false;
}
@ -366,7 +366,7 @@ bool DSManager::ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtract
}
LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_FAIL", "");
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용심 추출에 실패하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Remaining duration extraction failed."));
return false;
}
else
@ -392,12 +392,12 @@ bool DSManager::ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtract
std::string s = boost::lexical_cast <std::string> (iCharge);
s += "%s";
LogManager::instance().ItemLog(ch, pItem, "DS_HEART_EXTRACT_SUCCESS", s.c_str());
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용심 추출에 성공하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Dragon Stone remaining duration has been extracted."));
return true;
}
}
// 특정 용혼석을 장비창에서 제거할 때에 성공 여부를 결정하고, 실패시 부산물을 주는 함수.
// 특정 용혼석을 장비창에서 제거할 때에 성공 여부를 결정하고, 실패시 부산물을 주는 함수.
bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM pExtractor)
{
if (NULL == ch || NULL == pItem)
@ -406,13 +406,13 @@ bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM
return false;
}
// 목표 위치가 valid한지 검사 후, valid하지 않다면 임의의 빈 공간을 찾는다.
// 목표 위치가 valid한지 검사 후, valid하지 않다면 임의의 빈 공간을 찾는다.
if (!IsValidCellForThisItem(pItem, DestCell))
{
int iEmptyCell = ch->GetEmptyDragonSoulInventory(pItem);
if (iEmptyCell < 0)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소지품에 빈 공간이 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("There isn't enough space in your inventory."));
return false;
}
else
@ -430,14 +430,14 @@ bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM
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);
@ -454,7 +454,7 @@ bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM
bSuccess = fDice <= (fProb * (100 + iBonus) / 100.f);
}
// 캐릭터의 용혼석 추출 및 추가 혹은 제거. 부산물 제공.
// 캐릭터의 용혼석 추출 및 추가 혹은 제거. 부산물 제공.
{
char buf[128];
@ -469,7 +469,7 @@ bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM
sprintf(buf, "dice(%d) prob(%d)", (int)fDice, (int)fProb);
}
LogManager::instance().ItemLog(ch, pItem, "DS_PULL_OUT_SUCCESS", buf);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 추출에 성공하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Dragon Stone has been removed."));
pItem->AddToCharacter(ch, DestCell);
return true;
}
@ -490,12 +490,12 @@ bool DSManager::PullOut(LPCHARACTER ch, TItemPos DestCell, LPITEM& pItem, LPITEM
{
LPITEM pByProduct = ch->AutoGiveItem(dwByProduct, true);
if (pByProduct)
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 추출에 실패하여 %s를 얻었습니다."), pByProduct->GetName());
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Removal of Dragon Stone failed. But you have received the following: %s"), pByProduct->GetName());
else
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 추출에 실패하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Removal of Dragon Stone failed."));
}
else
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 추출에 실패하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Removal of Dragon Stone failed."));
}
}
@ -519,8 +519,8 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
return false;
}
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
std::set <LPITEM> set_items;
for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++)
{
@ -529,10 +529,10 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
LPITEM pItem = ch->GetItem(aItemPoses[i]);
if (NULL != pItem)
{
// 용혼석이 아닌 아이템이 개량창에 있을 수 없다.
// 용혼석이 아닌 아이템이 개량창에 있을 수 없다.
if (!pItem->IsDragonSoul())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("등급 개량에 필요한 재료가 아닙니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This item is not required for improving the clarity level."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
@ -557,7 +557,7 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
BYTE ds_type, grade_idx, step_idx, strength_idx;
int result_grade;
// 가장 처음 것을 강화의 기준으로 삼는다.
// 가장 처음 것을 강화의 기준으로 삼는다.
std::set <LPITEM>::iterator it = set_items.begin();
{
LPITEM pItem = *it;
@ -566,7 +566,7 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
if (!m_pTable->GetRefineGradeValues(ds_type, grade_idx, need_count, fee, vec_probs))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("등급 개량할 수 없는 용혼석입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This item cannot be advanced this way."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
@ -576,8 +576,8 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
{
LPITEM pItem = *it;
// 클라 ui에서 장착한 아이템은 개량창에 올릴 수 없도록 막았기 때문에,
// 별도의 알림 처리는 안함.
// 클라 ui에서 장착한 아이템은 개량창에 올릴 수 없도록 막았기 때문에,
// 별도의 알림 처리는 안함.
if (pItem->IsEquipped())
{
return false;
@ -585,14 +585,14 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
if (ds_type != GetType(pItem->GetVnum()) || grade_idx != GetGradeIdx(pItem->GetVnum()))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("등급 개량에 필요한 재료가 아닙니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This item is not required for improving the clarity level."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
}
}
// 클라에서 한번 갯수 체크를 하기 때문에 count != need_count라면 invalid 클라일 가능성이 크다.
// 클라에서 한번 갯수 체크를 하기 때문에 count != need_count라면 invalid 클라일 가능성이 크다.
if (count != need_count)
{
SPDLOG_ERROR("Possiblity of invalid client. Name {}", ch->GetName());
@ -603,7 +603,7 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
if (ch->GetGold() < fee)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 돈이 부족합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have enough Yang to use this item."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS);
return false;
}
@ -648,7 +648,7 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
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("등급 개량에 성공했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Refinement up one class was successful."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell()));
return true;
}
@ -657,7 +657,7 @@ bool DSManager::DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL
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("등급 개량에 실패했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Refinement up one class failed."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell()));
return false;
}
@ -679,18 +679,18 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
return false;
}
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
std::set <LPITEM> 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("단계 개량에 필요한 재료가 아닙니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not own the materials required to strengthen the Dragon Stone."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
}
@ -713,7 +713,7 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
BYTE ds_type, grade_idx, step_idx, strength_idx;
int result_step;
// 가장 처음 것을 강화의 기준으로 삼는다.
// 가장 처음 것을 강화의 기준으로 삼는다.
std::set <LPITEM>::iterator it = set_items.begin();
{
LPITEM pItem = *it;
@ -721,7 +721,7 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
if (!m_pTable->GetRefineStepValues(ds_type, step_idx, need_count, fee, vec_probs))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("단계 개량할 수 없는 용혼석입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This item is not required for refinement."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
}
@ -730,21 +730,21 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
while(++it != set_items.end())
{
LPITEM pItem = *it;
// 클라 ui에서 장착한 아이템은 개량창에 올릴 수 없도록 막았기 때문에,
// 별도의 알림 처리는 안함.
// 클라 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("단계 개량에 필요한 재료가 아닙니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not own the materials required to strengthen the Dragon Stone."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
}
}
// 클라에서 한번 갯수 체크를 하기 때문에 count != need_count라면 invalid 클라일 가능성이 크다.
// 클라에서 한번 갯수 체크를 하기 때문에 count != need_count라면 invalid 클라일 가능성이 크다.
if (count != need_count)
{
SPDLOG_ERROR("Possiblity of invalid client. Name {}", ch->GetName());
@ -755,7 +755,7 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
if (ch->GetGold() < fee)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 돈이 부족합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have enough Yang to use this item."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS);
return false;
}
@ -800,7 +800,7 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
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("단계 개량에 성공했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Improvement of the clarity level successful."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_SUCCEED, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell()));
return true;
}
@ -809,7 +809,7 @@ bool DSManager::DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_
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("단계 개량에 실패했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Improvement of the clarity level failed."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL, TItemPos (pResultItem->GetWindow(), pResultItem->GetCell()));
return false;
}
@ -840,8 +840,8 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
return false;
}
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
// 혹시나 모를 중복되는 item pointer 없애기 위해서 set 사용
// 이상한 패킷을 보낼 경우, 중복된 TItemPos가 있을 수도 있고, 잘못된 TItemPos가 있을 수도 있다.
std::set <LPITEM> set_items;
for (int i = 0; i < DRAGON_SOUL_REFINE_GRID_SIZE; i++)
{
@ -863,15 +863,15 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
for (std::set <LPITEM>::iterator it = set_items.begin(); it != set_items.end(); it++)
{
LPITEM pItem = *it;
// 클라 ui에서 장착한 아이템은 개량창에 올릴 수 없도록 막았기 때문에,
// 별도의 알림 처리는 안함.
// 클라 ui에서 장착한 아이템은 개량창에 올릴 수 없도록 막았기 때문에,
// 별도의 알림 처리는 안함.
if (pItem->IsEquipped())
{
return false;
}
// 용혼석과 강화석만이 개량창에 있을 수 있다.
// 그리고 하나씩만 있어야한다.
// 용혼석과 강화석만이 개량창에 있을 수 있다.
// 그리고 하나씩만 있어야한다.
if (pItem->IsDragonSoul())
{
if (pDragonSoul != NULL)
@ -892,7 +892,7 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("강화에 필요한 재료가 아닙니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This item is not required for improving the clarity level."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pItem->GetWindow(), pItem->GetCell()));
return false;
}
@ -912,17 +912,17 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
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("강화할 수 없는 용혼석입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This Dragon Stone cannot be used for strengthening."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell()));
return false;
}
// 강화했을 때 가중치가 0이라면 더 이상 강화되서는 안된다.
// 강화했을 때 가중치가 0이라면 더 이상 강화되서는 안된다.
if (fWeight < FLT_EPSILON)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("강화할 수 없는 용혼석입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This Dragon Stone cannot be used for strengthening."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_MAX_REFINE, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell()));
return false;
}
@ -931,7 +931,7 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
float fProb;
if (!m_pTable->GetRefineStrengthValues(bType, pRefineStone->GetSubType(), bStrength, fee, fProb))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("강화할 수 없는 용혼석입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This Dragon Stone cannot be used for strengthening."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_INVALID_MATERIAL, TItemPos(pDragonSoul->GetWindow(), pDragonSoul->GetCell()));
return false;
@ -939,7 +939,7 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
if (ch->GetGold() < fee)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("개량을 하기 위한 돈이 부족합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have enough Yang to use this item."));
SendRefineResultPacket(ch, DS_SUB_HEADER_REFINE_FAIL_NOT_ENOUGH_MONEY, NPOS);
return false;
}
@ -967,7 +967,7 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
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->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Strengthening was successful."));
ch->AutoGiveItem(pResult, true);
bSubHeader = DS_SUB_HEADER_REFINE_SUCCEED;
}
@ -988,10 +988,10 @@ bool DSManager::DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_S
char buf[128];
sprintf(buf, "STRENGTH : %d -> %d", bStrength, bStrength - 1);
// strength강화는 실패시 깨질 수도 있어, 원본 아이템을 바탕으로 로그를 남김.
// strength강화는 실패시 깨질 수도 있어, 원본 아이템을 바탕으로 로그를 남김.
LogManager::instance().ItemLog(ch, pDragonSoul, "DS_STRENGTH_REFINE_FAIL", buf);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("강화에 실패했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Strengthening failed."));
pDragonSoul->SetCount(pDragonSoul->GetCount() - 1);
pRefineStone->SetCount(pRefineStone->GetCount() - 1);
if (NULL != pResult)
@ -1029,12 +1029,12 @@ int DSManager::LeftTime(LPITEM pItem) const
if (pItem == NULL)
return false;
// 일단은 timer based on wear인 용혼석만 시간 다 되어도 안 없어진다.
// 일단은 timer based on wear인 용혼석만 시간 다 되어도 안 없어진다.
if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0)
{
return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC);
}
// 다른 limit type인 용혼석들은 시간 되면 모두 사라지기 때문에 여기 들어온 아이템은 일단 시간이 남았다고 판단.
// 다른 limit type인 용혼석들은 시간 되면 모두 사라지기 때문에 여기 들어온 아이템은 일단 시간이 남았다고 판단.
else
{
return INT_MAX;
@ -1046,12 +1046,12 @@ bool DSManager::IsTimeLeftDragonSoul(LPITEM pItem) const
if (pItem == NULL)
return false;
// 일단은 timer based on wear인 용혼석만 시간 다 되어도 안 없어진다.
// 일단은 timer based on wear인 용혼석만 시간 다 되어도 안 없어진다.
if (pItem->GetProto()->cLimitTimerBasedOnWearIndex >= 0)
{
return pItem->GetSocket(ITEM_SOCKET_REMAIN_SEC) > 0;
}
// 다른 limit type인 용혼석들은 시간 되면 모두 사라지기 때문에 여기 들어온 아이템은 일단 시간이 남았다고 판단.
// 다른 limit type인 용혼석들은 시간 되면 모두 사라지기 때문에 여기 들어온 아이템은 일단 시간이 남았다고 판단.
else
{
return true;

View File

@ -16,23 +16,23 @@ public:
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로
// 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)%만큼 증가함.
// 부산물은 언제나 자동 추가.
// 특정 용혼석(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]);
@ -47,7 +47,7 @@ public:
private:
void SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos);
// 캐릭터의 용혼석 덱을 살펴보고, 활성화 된 용혼석이 없다면, 캐릭터의 용혼석 활성 상태를 off 시키는 함수.
// 캐릭터의 용혼석 덱을 살펴보고, 활성화 된 용혼석이 없다면, 캐릭터의 용혼석 활성 상태를 off 시키는 함수.
void RefreshDragonSoulState(LPCHARACTER ch);
DWORD MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine);

View File

@ -30,14 +30,14 @@ static Pixel * LoadOldGuildMarkImageFile()
bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
{
// 폴더 생성
// 폴더 생성
#ifndef __WIN32__
mkdir("mark", S_IRWXU);
#else
_mkdir("mark");
#endif
// 인덱스 파일이 있나?
// 인덱스 파일이 있나?
#ifndef __WIN32__
if (0 != access(OLD_MARK_INDEX_FILENAME, F_OK))
#else
@ -45,13 +45,13 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
#endif
return true;
// 인덱스 파일 열기
// 인덱스 파일 열기
FILE* fp = fopen(OLD_MARK_INDEX_FILENAME, "r");
if (NULL == fp)
return false;
// 이미지 파일 열기
// 이미지 파일 열기
Pixel * oldImagePtr = LoadOldGuildMarkImageFile();
if (NULL == oldImagePtr)
@ -61,8 +61,8 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
}
/*
// guild_mark.tga가 실제 targa 파일이 아니고, 512 * 512 * 4 크기의 raw 파일이다.
// 눈으로 확인하기 위해 실제 targa 파일로 만든다.
// 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");
@ -86,7 +86,7 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
continue;
}
// mark id -> 이미지에서의 위치 찾기
// mark id -> 이미지에서의 위치 찾기
uint row = mark_id / 32;
uint col = mark_id % 32;
@ -102,7 +102,7 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
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)
@ -111,7 +111,7 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
src += 512;
}
// 새 길드 마크 시스템에 넣는다.
// 새 길드 마크 시스템에 넣는다.
CGuildMarkManager::instance().SaveMark(guild_id, (BYTE *) mark);
line[0] = '\0';
}
@ -119,7 +119,7 @@ bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
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");

View File

@ -129,10 +129,10 @@ void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * da
ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
// 이미지 = 512x512
// 블럭 = 마크 4 x 4
// 마크 = 16 x 12
// 한 이미지의 블럭 = 8 x 10
// 이미지 = 512x512
// 블럭 = 마크 4 x 4
// 마크 = 16 x 12
// 한 이미지의 블럭 = 8 x 10
// SERVER
bool CGuildMarkImage::SaveMark(DWORD posMark, BYTE * pbImage)
@ -143,14 +143,14 @@ bool CGuildMarkImage::SaveMark(DWORD posMark, BYTE * pbImage)
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;
@ -197,7 +197,7 @@ bool CGuildMarkImage::SaveBlockFromCompressedData(DWORD posBlock, const BYTE * p
return true;
}
void CGuildMarkImage::BuildAllBlocks() // 이미지 전체를 블럭화
void CGuildMarkImage::BuildAllBlocks() // 이미지 전체를 블럭화
{
Pixel apxBuf[SGuildMarkBlock::SIZE];
SPDLOG_INFO("GuildMarkImage::BuildAllBlocks");

View File

@ -16,7 +16,7 @@ struct SGuildMark
};
///////////////////////////////////////////////////////////////////////////////
Pixel m_apxBuf[SIZE]; // 실제 이미지
Pixel m_apxBuf[SIZE]; // 실제 이미지
///////////////////////////////////////////////////////////////////////////////
void Clear();
@ -38,11 +38,11 @@ struct SGuildMarkBlock
};
///////////////////////////////////////////////////////////////////////////////
Pixel m_apxBuf[SIZE]; // 실제 이미지
Pixel m_apxBuf[SIZE]; // 실제 이미지
BYTE m_abCompBuf[MAX_COMP_SIZE]; // 압축된 데이터
lzo_uint m_sizeCompBuf; // 압축된 크기
DWORD m_crc; // 압축된 데이터의 CRC
BYTE m_abCompBuf[MAX_COMP_SIZE]; // 압축된 데이터
lzo_uint m_sizeCompBuf; // 압축된 크기
DWORD m_crc; // 압축된 데이터의 CRC
///////////////////////////////////////////////////////////////////////////////
DWORD GetCRC() const;
@ -87,9 +87,9 @@ class CGuildMarkImage
bool SaveMark(DWORD posMark, BYTE * pbMarkImage);
bool DeleteMark(DWORD posMark);
bool SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize); // 서버 -> 클라이언트
bool SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize); // 서버 -> 클라이언트
DWORD GetEmptyPosition(); // 빈 마크 위치를 얻는다.
DWORD GetEmptyPosition(); // 빈 마크 위치를 얻는다.
void GetBlockCRCList(DWORD * crcList);
void GetDiffBlocks(const DWORD * crcList, std::map<BYTE, const SGuildMarkBlock *> & mapDiffBlocks);

View File

@ -15,7 +15,7 @@ void CGuildMarkManager::__DeleteImage(CGuildMarkImage * pkImgDel)
CGuildMarkManager::CGuildMarkManager()
{
// 남은 mark id 셋을 만든다. (서버용)
// 남은 mark id 셋을 만든다. (서버용)
for (DWORD i = 0; i < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT; ++i)
m_setFreeMarkID.insert(i);
}
@ -44,7 +44,7 @@ void CGuildMarkManager::SetMarkPathPrefix(const char * prefix)
m_pathPrefix = prefix;
}
// 마크 인덱스 불러오기 (서버에서만 사용)
// 마크 인덱스 불러오기 (서버에서만 사용)
bool CGuildMarkManager::LoadMarkIndex()
{
char buf[64];
@ -177,7 +177,7 @@ DWORD CGuildMarkManager::__AllocMarkID(DWORD guildID)
DWORD markID = *it;
DWORD imgIdx = markID / CGuildMarkImage::MARK_TOTAL_COUNT;
CGuildMarkImage * pkImage = __GetImage(imgIdx); // 이미지가 없다면 만들기 위해
CGuildMarkImage * pkImage = __GetImage(imgIdx); // 이미지가 없다면 만들기 위해
if (pkImage && AddMarkIDByGuildID(guildID, markID))
return markID;
@ -263,7 +263,7 @@ void CGuildMarkManager::GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::
{
mapDiffBlocks.clear();
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx))
{
SPDLOG_ERROR("invalid idx {}", imgIdx);
@ -290,7 +290,7 @@ bool CGuildMarkManager::SaveBlockFromCompressedData(DWORD imgIdx, DWORD posBlock
// CLIENT
bool CGuildMarkManager::GetBlockCRCList(DWORD imgIdx, DWORD * crcList)
{
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx))
{
SPDLOG_ERROR("invalid idx {}", imgIdx);

View File

@ -32,11 +32,11 @@ class CGuildMarkManager : public singleton<CGuildMarkManager>
//
void SetMarkPathPrefix(const char * prefix);
bool LoadMarkIndex(); // 마크 인덱스 불러오기 (서버에서만 사용)
bool SaveMarkIndex(); // 마크 인덱스 저장하기
bool LoadMarkIndex(); // 마크 인덱스 불러오기 (서버에서만 사용)
bool SaveMarkIndex(); // 마크 인덱스 저장하기
void LoadMarkImages(); // 모든 마크 이미지를 불러오기
void SaveMarkImage(DWORD imgIdx); // 마크 이미지 저장
void LoadMarkImages(); // 모든 마크 이미지를 불러오기
void SaveMarkImage(DWORD imgIdx); // 마크 이미지 저장
bool GetMarkImageFilename(DWORD imgIdx, std::string & path) const;
bool AddMarkIDByGuildID(DWORD guildID, DWORD markID);

View File

@ -155,11 +155,11 @@ bool COXEventManager::ShowQuizList(LPCHARACTER pkChar)
{
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, "%d %s %s", m_vec_quiz[i][j].level, m_vec_quiz[i][j].Quiz, m_vec_quiz[i][j].answer ? LC_TEXT("TRUE") : LC_TEXT("FALSE"));
}
}
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("총 퀴즈 수: %d"), c);
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Total number of the Quiz: %d"), c);
return true;
}
@ -197,22 +197,22 @@ EVENTFUNC(oxevent_timer)
switch (flag)
{
case 0:
SendNoticeMap(LC_TEXT("10초뒤 판정하겠습니다."), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("The result will follow in 10 seconds."), OXEVENT_MAP_INDEX, true);
flag++;
return PASSES_PER_SEC(10);
case 1:
SendNoticeMap(LC_TEXT("정답은"), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("The correct answer is:"), OXEVENT_MAP_INDEX, true);
if (info->answer == true)
{
COXEventManager::instance().CheckAnswer(true);
SendNoticeMap(LC_TEXT("O 입니다"), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("Yes (O)"), OXEVENT_MAP_INDEX, true);
}
else
{
COXEventManager::instance().CheckAnswer(false);
SendNoticeMap(LC_TEXT("X 입니다"), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("No (X)"), OXEVENT_MAP_INDEX, true);
}
if (LC_IsJapan())
@ -221,7 +221,7 @@ EVENTFUNC(oxevent_timer)
}
else
{
SendNoticeMap(LC_TEXT("5초 뒤 틀리신 분들을 바깥으로 이동 시키겠습니다."), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("In 5 sec. everyone who gave an incorrect answer will be removed."), OXEVENT_MAP_INDEX, true);
}
flag++;
@ -230,7 +230,7 @@ EVENTFUNC(oxevent_timer)
case 2:
COXEventManager::instance().WarpToAudience();
COXEventManager::instance().SetStatus(OXEVENT_CLOSE);
SendNoticeMap(LC_TEXT("다음 문제 준비해주세요."), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("Ready for the next question?"), OXEVENT_MAP_INDEX, true);
flag = 0;
break;
}
@ -247,9 +247,9 @@ bool COXEventManager::Quiz(unsigned char level, int timelimit)
int idx = Random::get<int>(0, m_vec_quiz[level].size() - 1);
SendNoticeMap(LC_TEXT("문제 입니다."), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("OX-Question: "), OXEVENT_MAP_INDEX, true);
SendNoticeMap(m_vec_quiz[level][idx].Quiz, OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("맞으면 O, 틀리면 X로 이동해주세요"), OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("If it's correct, then go to O. If it's wrong, go to X."), OXEVENT_MAP_INDEX, true);
if (m_timedEvent != NULL) {
event_cancel(&m_timedEvent);
@ -312,17 +312,17 @@ bool COXEventManager::CheckAnswer(bool answer)
}
else
{
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("정답입니다!"));
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Correct!"));
// pkChar->CreateFly(Random::get(FLY_FIREWORK1, FLY_FIREWORK6), pkChar);
char chatbuf[256];
int len = snprintf(chatbuf, sizeof(chatbuf),
"%s %u %u", Random::get(0, 1) == 1 ? "cheer1" : "cheer2", (DWORD)pkChar->GetVID(), 0);
// 리턴값이 sizeof(chatbuf) 이상일 경우 truncate되었다는 뜻..
// 리턴값이 sizeof(chatbuf) 이상일 경우 truncate되었다는 뜻..
if (len < 0 || len >= (int) sizeof(chatbuf))
len = sizeof(chatbuf) - 1;
// \0 문자 포함
// \0 문자 포함
++len;
TPacketGCChat pack_chat;

View File

@ -10,10 +10,10 @@ struct tag_Quiz
enum OXEventStatus
{
OXEVENT_FINISH = 0, // OX이벤트가 완전히 끝난 상태
OXEVENT_OPEN = 1, // OX이벤트가 시작됨. 을두지(20012)를 통해서 입장가능
OXEVENT_CLOSE = 2, // OX이벤트의 참가가 끝남. 을두지(20012)를 통한 입장이 차단됨
OXEVENT_QUIZ = 3, // 퀴즈를 출제함.
OXEVENT_FINISH = 0, // OX이벤트가 완전히 끝난 상태
OXEVENT_OPEN = 1, // OX이벤트가 시작됨. 을두지(20012)를 통해서 입장가능
OXEVENT_CLOSE = 2, // OX이벤트의 참가가 끝남. 을두지(20012)를 통한 입장이 차단됨
OXEVENT_QUIZ = 3, // 퀴즈를 출제함.
OXEVENT_ERR = 0xff
};

View File

@ -18,13 +18,13 @@ 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를 발생시킴.
// 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<petsystem_event_info*>( event->info );
@ -41,12 +41,12 @@ EVENTFUNC(petsystem_update_event)
pPetSystem->Update(0);
// 0.25초마다 갱신.
// 0.25초마다 갱신.
return PASSES_PER_SEC(1) / 4;
}
/// NOTE: 1캐릭터가 몇개의 펫을 가질 수 있는지 제한... 캐릭터마다 개수를 다르게 할거라면 변수로 넣등가... 음..
/// 가질 수 있는 개수와 동시에 소환할 수 있는 개수가 틀릴 수 있는데 이런건 기획 없으니 일단 무시
/// NOTE: 1캐릭터가 몇개의 펫을 가질 수 있는지 제한... 캐릭터마다 개수를 다르게 할거라면 변수로 넣등가... 음..
/// 가질 수 있는 개수와 동시에 소환할 수 있는 개수가 틀릴 수 있는데 이런건 기획 없으니 일단 무시
const float PET_COUNT_LIMIT = 3;
///////////////////////////////////////////////////////////////////////////////////////
@ -119,7 +119,7 @@ void CPetActor::Unsummon()
{
if (true == this->IsSummoned())
{
// 버프 삭제
// 버프 삭제
this->ClearBuff();
this->SetSummonItem(NULL);
if (NULL != m_pkOwner)
@ -175,14 +175,14 @@ DWORD CPetActor::Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar)
// m_pkOwner->DetailLog();
// m_pkChar->DetailLog();
//펫의 국가를 주인의 국가로 설정함.
//펫의 국가를 주인의 국가로 설정함.
m_pkChar->SetEmpire(m_pkOwner->GetEmpire());
m_dwVID = m_pkChar->GetVID();
this->SetName(petName);
// SetSummonItem(pSummonItem)를 부른 후에 ComputePoints를 부르면 버프 적용됨.
// SetSummonItem(pSummonItem)를 부른 후에 ComputePoints를 부르면 버프 적용됨.
this->SetSummonItem(pSummonItem);
m_pkOwner->ComputePoints();
m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z);
@ -197,11 +197,11 @@ bool CPetActor::_UpdatAloneActionAI(float fMinDist, float fMaxDist)
float dest_x = GetOwner()->GetX() + fDist * cos(r);
float dest_y = GetOwner()->GetY() + fDist * sin(r);
//m_pkChar->SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
//m_pkChar->SetRotation(Random::get(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;
@ -218,7 +218,7 @@ bool CPetActor::_UpdatAloneActionAI(float fMinDist, float fMaxDist)
return true;
}
// char_state.cpp StateHorse함수 그냥 C&P -_-;
// char_state.cpp StateHorse함수 그냥 C&P -_-;
bool CPetActor::_UpdateFollowAI()
{
if (0 == m_pkChar->m_pkMobData)
@ -227,9 +227,9 @@ bool CPetActor::_UpdateFollowAI()
return false;
}
// NOTE: 캐릭터(펫)의 원래 이동 속도를 알아야 하는데, 해당 값(m_pkChar->m_pkMobData->m_table.sMovingSpeed)을 직접적으로 접근해서 알아낼 수도 있지만
// m_pkChar->m_pkMobData 값이 invalid한 경우가 자주 발생함. 현재 시간관계상 원인은 다음에 파악하고 일단은 m_pkChar->m_pkMobData 값을 아예 사용하지 않도록 함.
// 여기서 매번 검사하는 이유는 최초 초기화 할 때 정상 값을 제대로 못얻어오는 경우도 있음.. -_-;; ㅠㅠㅠㅠㅠㅠㅠㅠㅠ
// NOTE: 캐릭터(펫)의 원래 이동 속도를 알아야 하는데, 해당 값(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);
@ -237,14 +237,14 @@ bool CPetActor::_UpdateFollowAI()
if (0 != mobData)
m_originalMoveSpeed = mobData->m_table.sMovingSpeed;
}
float START_FOLLOW_DISTANCE = 300.0f; // 이 거리 이상 떨어지면 쫓아가기 시작함
float START_RUN_DISTANCE = 900.0f; // 이 거리 이상 떨어지면 뛰어서 쫓아감.
float START_FOLLOW_DISTANCE = 300.0f; // 이 거리 이상 떨어지면 쫓아가기 시작함
float START_RUN_DISTANCE = 900.0f; // 이 거리 이상 떨어지면 뛰어서 쫓아감.
float RESPAWN_DISTANCE = 4500.f; // 이 거리 이상 멀어지면 주인 옆으로 소환함.
int APPROACH = 200; // 접근 거리
float RESPAWN_DISTANCE = 4500.f; // 이 거리 이상 멀어지면 주인 옆으로 소환함.
int APPROACH = 200; // 접근 거리
bool bDoMoveAlone = true; // 캐릭터와 가까이 있을 때 혼자 여기저기 움직일건지 여부 -_-;
bool bRun = false; // 뛰어야 하나?
bool bDoMoveAlone = true; // 캐릭터와 가까이 있을 때 혼자 여기저기 움직일건지 여부 -_-;
bool bRun = false; // 뛰어야 하나?
DWORD currentTime = get_dword_time();
@ -272,7 +272,7 @@ bool CPetActor::_UpdateFollowAI()
bRun = true;
}
m_pkChar->SetNowWalking(!bRun); // NOTE: 함수 이름보고 멈추는건줄 알았는데 SetNowWalking(false) 하면 뛰는거임.. -_-;
m_pkChar->SetNowWalking(!bRun); // NOTE: 함수 이름보고 멈추는건줄 알았는데 SetNowWalking(false) 하면 뛰는거임.. -_-;
Follow(APPROACH);
@ -288,7 +288,7 @@ bool CPetActor::_UpdateFollowAI()
// m_dwLastActionTime = currentTime;
// }
//}
// Follow 중이지만 주인과 일정 거리 이내로 가까워졌다면 멈춤
// Follow 중이지만 주인과 일정 거리 이내로 가까워졌다면 멈춤
else
m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
//else if (currentTime - m_dwLastActionTime > Random::get(5000, 12000))
@ -303,8 +303,8 @@ bool CPetActor::Update(DWORD deltaTime)
{
bool bResult = true;
// 펫 주인이 죽었거나, 소환된 펫의 상태가 이상하다면 펫을 없앰. (NOTE: 가끔가다 이런 저런 이유로 소환된 펫이 DEAD 상태에 빠지는 경우가 있음-_-;)
// 펫을 소환한 아이템이 없거나, 내가 가진 상태가 아니라면 펫을 없앰.
// 펫 주인이 죽었거나, 소환된 펫의 상태가 이상하다면 펫을 없앰. (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()
@ -320,10 +320,10 @@ bool CPetActor::Update(DWORD deltaTime)
return bResult;
}
//NOTE : 주의!!! MinDistance를 크게 잡으면 그 변위만큼의 변화동안은 follow하지 않는다,
//NOTE : 주의!!! MinDistance를 크게 잡으면 그 변위만큼의 변화동안은 follow하지 않는다,
bool CPetActor::Follow(float fMinDistance)
{
// 가려는 위치를 바라봐야 한다.
// 가려는 위치를 바라봐야 한다.
if( !m_pkOwner || !m_pkChar)
return false;
@ -367,7 +367,7 @@ void CPetActor::SetSummonItem (LPITEM pItem)
void CPetActor::GiveBuff()
{
// 파황 펫 버프는 던전에서만 발생함.
// 파황 펫 버프는 던전에서만 발생함.
if (34004 == m_dwVnum || 34009 == m_dwVnum)
{
if (NULL == m_pkOwner->GetDungeon())
@ -432,15 +432,15 @@ void CPetSystem::Destroy()
m_petActorMap.clear();
}
/// 펫 시스템 업데이트. 등록된 펫들의 AI 처리 등을 함.
/// 펫 시스템 업데이트. 등록된 펫들의 AI 처리 등을 함.
bool CPetSystem::Update(DWORD deltaTime)
{
bool bResult = true;
DWORD currentTime = get_dword_time();
// CHARACTER_MANAGER에서 캐릭터류 Update할 때 매개변수로 주는 (Pulse라고 되어있는)값이 이전 프레임과의 시간차이인줄 알았는데
// 전혀 다른 값이라서-_-; 여기에 입력으로 들어오는 deltaTime은 의미가 없음ㅠㅠ
// CHARACTER_MANAGER에서 캐릭터류 Update할 때 매개변수로 주는 (Pulse라고 되어있는)값이 이전 프레임과의 시간차이인줄 알았는데
// 전혀 다른 값이라서-_-; 여기에 입력으로 들어오는 deltaTime은 의미가 없음ㅠㅠ
if (m_dwUpdatePeriod > currentTime - m_dwLastUpdateTime)
return true;
@ -473,7 +473,7 @@ bool CPetSystem::Update(DWORD deltaTime)
return bResult;
}
/// 관리 목록에서 펫을 지움
/// 관리 목록에서 펫을 지움
void CPetSystem::DeletePet(DWORD mobVnum)
{
TPetActorMap::iterator iter = m_petActorMap.find(mobVnum);
@ -494,7 +494,7 @@ void CPetSystem::DeletePet(DWORD mobVnum)
m_petActorMap.erase(iter);
}
/// 관리 목록에서 펫을 지움
/// 관리 목록에서 펫을 지움
void CPetSystem::DeletePet(CPetActor* petActor)
{
for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
@ -542,7 +542,7 @@ CPetActor* CPetSystem::Summon(DWORD mobVnum, LPITEM pSummonItem, const char* pet
{
CPetActor* petActor = this->GetByVnum(mobVnum);
// 등록된 펫이 아니라면 새로 생성 후 관리 목록에 등록함.
// 등록된 펫이 아니라면 새로 생성 후 관리 목록에 등록함.
if (0 == petActor)
{
petActor = M2_NEW CPetActor(m_pkOwner, mobVnum, options);
@ -557,7 +557,7 @@ CPetActor* CPetSystem::Summon(DWORD mobVnum, LPITEM pSummonItem, const char* pet
info->pPetSystem = this;
m_pkPetSystemUpdateEvent = event_create(petsystem_update_event, info, PASSES_PER_SEC(1) / 4); // 0.25초
m_pkPetSystemUpdateEvent = event_create(petsystem_update_event, info, PASSES_PER_SEC(1) / 4); // 0.25초
}
return petActor;
@ -589,7 +589,7 @@ CPetActor* CPetSystem::GetByVID(DWORD vid) const
return bFound ? petActor : 0;
}
/// 등록 된 펫 중에서 주어진 몹 VNUM을 가진 액터를 반환하는 함수.
/// 등록 된 펫 중에서 주어진 몹 VNUM을 가진 액터를 반환하는 함수.
CPetActor* CPetSystem::GetByVnum(DWORD vnum) const
{
CPetActor* petActor = 0;

View File

@ -4,7 +4,7 @@
class CHARACTER;
// TODO: 펫으로서의 능력치? 라던가 친밀도, 배고픔 기타등등... 수치
// TODO: 펫으로서의 능력치? 라던가 친밀도, 배고픔 기타등등... 수치
struct SPetAbility
{
};
@ -34,8 +34,8 @@ protected:
virtual bool Update(DWORD deltaTime);
protected:
virtual bool _UpdateFollowAI(); ///< 주인을 따라다니는 AI 처리
virtual bool _UpdatAloneActionAI(float fMinDist, float fMaxDist); ///< 주인 근처에서 혼자 노는 AI 처리
virtual bool _UpdateFollowAI(); ///< 주인을 따라다니는 AI 처리
virtual bool _UpdatAloneActionAI(float fMinDist, float fMaxDist); ///< 주인 근처에서 혼자 노는 AI 처리
/// @TODO
//virtual bool _UpdateCombatAI();
@ -62,13 +62,13 @@ public:
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를 하는 것으로 한다.
// 버프 주는 함수와 거두는 함수.
// 이게 좀 괴랄한게, 서버가 ㅄ라서,
// POINT_MOV_SPEED, POINT_ATT_SPEED, POINT_CAST_SPEED는 PointChange()란 함수만 써서 변경해 봐야 소용이 없는게,
// PointChange() 이후에 어디선가 ComputePoints()를 하면 싹다 초기화되고,
// 더 웃긴건, ComputePoints()를 부르지 않으면 클라의 POINT는 전혀 변하지 않는다는 거다.
// 그래서 버프를 주는 것은 ComputePoints() 내부에서 petsystem->RefreshBuff()를 부르도록 하였고,
// 버프를 빼는 것은 ClearBuff()를 부르고, ComputePoints를 하는 것으로 한다.
void GiveBuff();
void ClearBuff();
@ -87,7 +87,7 @@ private:
LPCHARACTER m_pkChar; // Instance of pet(CHARACTER)
LPCHARACTER m_pkOwner;
// SPetAbility m_petAbility; // 능력치
// SPetAbility m_petAbility; // 능력치
};
/**
@ -95,7 +95,7 @@ private:
class CPetSystem
{
public:
typedef std::unordered_map<DWORD, CPetActor*> TPetActorMap; /// <VNUM, PetActor> map. (한 캐릭터가 같은 vnum의 펫을 여러개 가질 일이 있을까..??)
typedef std::unordered_map<DWORD, CPetActor*> TPetActorMap; /// <VNUM, PetActor> map. (한 캐릭터가 같은 vnum의 펫을 여러개 가질 일이 있을까..??)
public:
CPetSystem(LPCHARACTER owner);
@ -107,7 +107,7 @@ public:
bool Update(DWORD deltaTime);
void Destroy();
size_t CountSummoned() const; ///< 현재 소환된(실체화 된 캐릭터가 있는) 펫의 개수
size_t CountSummoned() const; ///< 현재 소환된(실체화 된 캐릭터가 있는) 펫의 개수
public:
void SetUpdatePeriod(DWORD ms);
@ -117,7 +117,7 @@ public:
void Unsummon(DWORD mobVnum, bool bDeleteFromList = false);
void Unsummon(CPetActor* petActor, bool bDeleteFromList = false);
// TODO: 진짜 펫 시스템이 들어갈 때 구현. (캐릭터가 보유한 펫의 정보를 추가할 때 라던가...)
// TODO: 진짜 펫 시스템이 들어갈 때 구현. (캐릭터가 보유한 펫의 정보를 추가할 때 라던가...)
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);
@ -126,8 +126,8 @@ public:
private:
TPetActorMap m_petActorMap;
LPCHARACTER m_pkOwner; ///< 펫 시스템의 Owner
DWORD m_dwUpdatePeriod; ///< 업데이트 주기 (ms단위)
LPCHARACTER m_pkOwner; ///< 펫 시스템의 Owner
DWORD m_dwUpdatePeriod; ///< 업데이트 주기 (ms단위)
DWORD m_dwLastUpdateTime;
LPEVENT m_pkPetSystemUpdateEvent;
};

View File

@ -3,7 +3,7 @@
#include "SpeedServer.h"
#include "locale_service.h"
// 쾌도 서버 보너스 경험치 시스템
// 쾌도 서버 보너스 경험치 시스템
// by rtsummit
CSpeedServerManager::CSpeedServerManager()
@ -361,13 +361,13 @@ HME CSpeedServerEmpireExp::GetCurrentExpPriv(int &duration, bool &is_change)
HME hme;
// 현재 날짜가 holiday이면 holiday bonus를 도입한다.
// 현재 날짜가 holiday이면 holiday bonus를 도입한다.
if (holi_it != holiday_map.end())
{
for (std::list <HME>::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;
@ -380,7 +380,7 @@ HME CSpeedServerEmpireExp::GetCurrentExpPriv(int &duration, bool &is_change)
for (std::list <HME>::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;

View File

@ -4,11 +4,11 @@
#include <common/length.h>
#include <list>
// castle.cpp 에 있는 것을 복붙 하였다
#define EMPIRE_NONE 0 // 아무국가 아님
#define EMPIRE_RED 1 // 신수
#define EMPIRE_YELLOW 2 // 천조
#define EMPIRE_BLUE 3 // 진노
// castle.cpp 에 있는 것을 복붙 하였다
#define EMPIRE_NONE 0 // 아무국가 아님
#define EMPIRE_RED 1 // 신수
#define EMPIRE_YELLOW 2 // 천조
#define EMPIRE_BLUE 3 // 진노
class HME
{

View File

@ -12,17 +12,17 @@
/**
* @class TrafficProfiler
* @brief Network I/O traffic profiler.
* @brief Network I/O traffic profiler.
* @author Bang2ni
* @version 05/07/07 Bang2ni - First release.
*
* Network I/O traffic , Text file .
* Network I/O traffic , Text file .
*/
class TrafficProfiler : public singleton< TrafficProfiler >
{
public:
/// I/O 방향
/// I/O 방향
enum IODirection {
IODIR_INPUT = 0, ///< Input
IODIR_OUTPUT, ///< Output
@ -37,25 +37,25 @@ class TrafficProfiler : public singleton< TrafficProfiler >
/// Destructor
~TrafficProfiler( void );
/// Profiling 에 필요한 초기화를 한다.
/// Profiling 에 필요한 초기화를 한다.
/**
* @param [in] dwFlushCycle Flush . .
* @param [in] pszLogFileName Profiling log file
* @return false profiling log file open .
* @param [in] dwFlushCycle Flush . .
* @param [in] pszLogFileName Profiling log file
* @return false profiling log file open .
*
* profiling log file open() .
* profiling log file open() .
*/
bool Initialize( DWORD dwFlushCycle, const char* pszLogFileName );
/// Profiling 을 위해 전송됐거나 전송 할 Packet 을 Report 한다.
/// Profiling 을 위해 전송됐거나 전송 할 Packet 을 Report 한다.
/**
* @param [in] dir Profiling Packet
* @param [in] byHeader Packet
* @param [in] dwSize Packet size
* @return Initialize false .
* @param [in] dir Profiling Packet
* @param [in] byHeader Packet
* @param [in] dwSize Packet size
* @return Initialize false .
*
* Packet size .
* Initialize Flush Flush Report Flush .
* Packet size .
* Initialize Flush Flush Report Flush .
*/
bool Report( IODirection dir, BYTE byHeader, DWORD dwSize )
{
@ -65,22 +65,22 @@ class TrafficProfiler : public singleton< TrafficProfiler >
return true;
}
/// 현재까지 Report 된 내용을 파일에 쓴다.
/// 현재까지 Report 된 내용을 파일에 쓴다.
/**
* @return Initialize .
* @return Initialize .
*/
bool Flush( void );
private:
/// Profling 에 관련된 variables 를 초기화 한다.
/// Profling 에 관련된 variables 를 초기화 한다.
void InitializeProfiling( void );
/// Report 된 Packet 의 traffic 를 계산한다.
/// Report 된 Packet 의 traffic 를 계산한다.
/**
* @param [in] dir Profiling Packet
* @param [in] byHeader Packet
* @param [in] dwSize Packet size
* @param [in] dir Profiling Packet
* @param [in] byHeader Packet
* @param [in] dwSize Packet size
*/
void ComputeTraffic( IODirection dir, BYTE byHeader, DWORD dwSize )
{
@ -96,8 +96,8 @@ class TrafficProfiler : public singleton< TrafficProfiler >
/// Traffic info type.
/**
* first: size
* second: packet
* first: size
* second: packet
*/
typedef std::pair< DWORD, DWORD > TrafficInfo;
@ -105,11 +105,11 @@ class TrafficProfiler : public singleton< TrafficProfiler >
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 를 가진다.
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_

View File

@ -54,23 +54,23 @@ enum EAffectTypes
AFFECT_DEF_GRADE, // 226
AFFECT_PREMIUM_START = 500,
AFFECT_EXP_BONUS = 500, // 경험의 반지
AFFECT_ITEM_BONUS = 501, // 도둑의 장갑
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_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_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_HAIR = 514, // 헤어 효과
AFFECT_COLLECT = 515, //수집퀘스트
AFFECT_EXP_BONUS_EURO_FREE = 516, // 경험의 반지 (유럽 버전 14 레벨 이하 기본 효과)
AFFECT_EXP_BONUS_EURO_FREE_UNDER_15 = 517,
AFFECT_UNIQUE_ABILITY = 518,
@ -121,8 +121,8 @@ enum EAffectBits
AFF_SLOW,
AFF_STUN,
AFF_DUNGEON_READY, // 던전에서 준비 상태
AFF_DUNGEON_UNIQUE, // 던전 유니크 (클라이언트에서 컬링되지않음)
AFF_DUNGEON_READY, // 던전에서 준비 상태
AFF_DUNGEON_UNIQUE, // 던전 유니크 (클라이언트에서 컬링되지않음)
AFF_BUILDING_CONSTRUCTION_SMALL,
AFF_BUILDING_CONSTRUCTION_LARGE,
@ -133,34 +133,34 @@ enum EAffectBits
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_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_HAIR, // 헤어
AFF_GERMANY, // 독일
AFF_BITS_MAX
};
@ -170,11 +170,11 @@ extern void SendAffectAddPacket(LPDESC d, CAffect * pkAff);
// AFFECT_DURATION_BUG_FIX
enum AffectVariable
{
// Affect가 무한대로 들어가 있어야 할 경우 사용.
// 시간을 계속 줄이기 때문에 매우 큰값으로 무한대를 에뮬레이션함.
//// 24비트는 적으므로 25비트를 사용.
// ... 25비트 사용한다고 해놓고선 29bit 사용하고 있는 엄청난 주석이란...
// collect quest에서 무한 시간을 60년으로 사용하고 있으므로, 여기도 60년으로 하자.
// Affect가 무한대로 들어가 있어야 할 경우 사용.
// 시간을 계속 줄이기 때문에 매우 큰값으로 무한대를 에뮬레이션함.
//// 24비트는 적으므로 25비트를 사용.
// ... 25비트 사용한다고 해놓고선 29bit 사용하고 있는 엄청난 주석이란...
// collect quest에서 무한 시간을 60년으로 사용하고 있으므로, 여기도 60년으로 하자.
INFINITE_AFFECT_DURATION = 60 * 365 * 24 * 60 * 60
};

View File

@ -55,7 +55,7 @@ const char* FN_weapon_type(int weapon)
class ANI
{
protected:
// [종족][일반0탈것1][무기][콤보]
// [종족][일반0탈것1][무기][콤보]
DWORD m_speed[MAIN_RACE_MAX_NUM][2][WEAPON_NUM_TYPES][9];
public:
@ -126,14 +126,14 @@ ANI::ANI()
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" // 무당(남)
"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 <MAIN_RACE_MAX_NUM; ++race)
@ -203,13 +203,13 @@ bool ANI::load_one_race(int race, const char *dir_name)
for (BYTE combo = 1; combo <= 8; ++combo)
{
// 말 안탔을 때
// 말 안탔을 때
m_speed[race][0][weapon][combo] = load_one_weapon(dir_name, weapon, combo, false);
m_speed[race][0][weapon][0] = std::min(m_speed[race][0][weapon][0], m_speed[race][0][weapon][combo]); // 최소값
m_speed[race][0][weapon][0] = std::min(m_speed[race][0][weapon][0], m_speed[race][0][weapon][combo]); // 최소값
// 말 탔을 때
// 말 탔을 때
m_speed[race][1][weapon][combo] = load_one_weapon(dir_name, weapon, combo, true);
m_speed[race][1][weapon][0] = std::min(m_speed[race][1][weapon][0], m_speed[race][1][weapon][combo]); // 최소값
m_speed[race][1][weapon][0] = std::min(m_speed[race][1][weapon][0], m_speed[race][1][weapon][combo]); // 최소값
SPDLOG_TRACE("combo{:02} speed={} horse={}",
combo, m_speed[race][0][weapon][combo], m_speed[race][1][weapon][combo]);
@ -337,8 +337,8 @@ DWORD ani_attack_speed(LPCHARACTER ch)
ch->GetPoint(POINT_ATT_SPEED));
*/
/* 투핸디드 소드의 경우 삼연참공격과 승마시 */
/* 오류가 많아 한손검 속도로 생각하자 */
/* 투핸디드 소드의 경우 삼연참공격과 승마시 */
/* 오류가 많아 한손검 속도로 생각하자 */
if (weapon == WEAPON_TWO_HANDED)
weapon = WEAPON_SWORD;

View File

@ -233,17 +233,17 @@ EVENTFUNC(ready_to_start_event)
if (chA != NULL)
{
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has finished, because your combatant vanished."));
SPDLOG_DEBUG("ARENA: Oppernent is disappered. MyPID({}) OppPID({})", pArena->GetPlayerAPID(), pArena->GetPlayerBPID());
}
if (chB != NULL)
{
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has finished, because your combatant vanished."));
SPDLOG_DEBUG("ARENA: Oppernent is disappered. MyPID({}) OppPID({})", pArena->GetPlayerBPID(), pArena->GetPlayerAPID());
}
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("The duel has finished, because your combatant vanished."));
pArena->EndDuel();
return 0;
@ -260,20 +260,20 @@ EVENTFUNC(ready_to_start_event)
if (count > 10000)
{
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물약 제한이 없습니다."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("물약 제한이 없습니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("There is no limit for Potions."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("There is no limit for Potions."));
}
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("You can use up to %d potions."), chA->GetPotionLimit());
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can use up to %d potions."), 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초뒤 대련이 시작됩니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The fight will start in 10 seconds."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The fight will start in 10 seconds."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The fight will start in 10 seconds."));
info->state++;
return PASSES_PER_SEC(10);
@ -282,15 +282,15 @@ EVENTFUNC(ready_to_start_event)
case 1:
{
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련이 시작되었습니다."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련이 시작되었습니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("대련이 시작되었습니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
TPacketGCDuelStart duelStart;
duelStart.header = HEADER_GC_DUEL_START;
duelStart.wSize = sizeof(TPacketGCDuelStart) + 4;
DWORD dwOppList[8]; // 최대 파티원 8명 이므로..
DWORD dwOppList[8]; // 최대 파티원 8명 이므로..
dwOppList[0] = (DWORD)chB->GetVID();
TEMP_BUFFER buf;
@ -339,7 +339,7 @@ EVENTFUNC(ready_to_start_event)
TEMP_BUFFER buf;
TEMP_BUFFER buf2;
DWORD dwOppList[8]; // 최대 파티원 8명 이므로..
DWORD dwOppList[8]; // 최대 파티원 8명 이므로..
TPacketGCDuelStart duelStart;
duelStart.header = HEADER_GC_DUEL_START;
duelStart.wSize = sizeof(TPacketGCDuelStart) + 4;
@ -354,9 +354,9 @@ EVENTFUNC(ready_to_start_event)
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("대련이 시작되었습니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The duel has begun."));
pArena->ClearEvent();
@ -366,9 +366,9 @@ EVENTFUNC(ready_to_start_event)
default:
{
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장 문제로 인하여 대련을 종료합니다."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장 문제로 인하여 대련을 종료합니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("대련장 문제로 인하여 대련을 종료합니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel is being finished because there is a problem in the duel arena."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel is being finished because there is a problem in the duel arena."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The duel is being finished because there is a problem in the duel arena."));
SPDLOG_DEBUG("ARENA: Something wrong in event func. info->state({})", info->state);
@ -407,17 +407,17 @@ EVENTFUNC(duel_time_out)
{
if (chA != NULL)
{
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
chA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has finished, because your combatant vanished."));
SPDLOG_DEBUG("ARENA: Oppernent is disappered. MyPID({}) OppPID({})", pArena->GetPlayerAPID(), pArena->GetPlayerBPID());
}
if (chB != NULL)
{
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
chB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has finished, because your combatant vanished."));
SPDLOG_DEBUG("ARENA: Oppernent is disappered. MyPID({}) OppPID({})", pArena->GetPlayerBPID(), pArena->GetPlayerAPID());
}
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("대련 상대가 사라져 대련을 종료합니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The duel has finished, because your combatant vanished."));
pArena->EndDuel();
return 0;
@ -427,14 +427,14 @@ EVENTFUNC(duel_time_out)
switch (info->state)
{
case 0:
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("대련 시간 초과로 대련을 중단합니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("10초뒤 마을로 이동합니다."));
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("The duel has finished because of a timeout."));
pArena->SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("In 10 seconds you will be teleported into the city."));
chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("대련 시간 초과로 대련을 중단합니다."));
chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10초뒤 마을로 이동합니다."));
chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("The duel has finished because of a timeout."));
chA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("In 10 seconds you will be teleported into the city."));
chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("대련 시간 초과로 대련을 중단합니다."));
chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("10초뒤 마을로 이동합니다."));
chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("The duel has finished because of a timeout."));
chB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("In 10 seconds you will be teleported into the city."));
TPacketGCDuelStart duelStart;
duelStart.header = HEADER_GC_DUEL_START;
@ -678,7 +678,7 @@ bool CArenaMap::CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim)
bool CArena::CanAttack(DWORD dwPIDA, DWORD dwPIDB)
{
// 1:1 전용 다대다 할 경우 수정 필요
// 1:1 전용 다대다 할 경우 수정 필요
if (m_dwPIDA == dwPIDA && m_dwPIDB == dwPIDB) return true;
if (m_dwPIDA == dwPIDB && m_dwPIDB == dwPIDA) return true;
@ -728,20 +728,20 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
if (pCharA == NULL && pCharB == NULL)
{
// 둘다 접속이 끊어졌다 ?!
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("대련자 문제로 인하여 대련을 중단합니다."));
// 둘다 접속이 끊어졌다 ?!
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("The duel has been stopped because there is a problem in the arena."));
restart = false;
}
else if (pCharA == NULL && pCharB != NULL)
{
pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("상대방 캐릭터의 문제로 인하여 대련을 종료합니다."));
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("대련자 문제로 인하여 대련을 종료합니다."));
pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("Your combatant has got some problems. The duel is being cancelled."));
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("The duel is being cancelled as there is a problem with the combatant."));
restart = false;
}
else if (pCharA != NULL && pCharB == NULL)
{
pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("상대방 캐릭터의 문제로 인하여 대련을 종료합니다."));
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("대련자 문제로 인하여 대련을 종료합니다."));
pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("Your combatant has got some problems. The duel is being cancelled."));
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("The duel is being cancelled as there is a problem with the combatant."));
restart = false;
}
else if (pCharA != NULL && pCharB != NULL)
@ -752,9 +752,9 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
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());
pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharA->GetName());
pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharA->GetName());
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharA->GetName());
SPDLOG_DEBUG("ARENA: Duel is end. Winner {}({}) Loser {}({})",
pCharA->GetName(), GetPlayerAPID(), pCharB->GetName(), GetPlayerBPID());
@ -762,10 +762,10 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
else
{
restart = true;
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님이 승리하였습니다."), pCharA->GetName());
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s has won."), 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_INFO, LC_TEXT("%s has won."), 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());
@ -779,19 +779,19 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
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());
pCharA->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharB->GetName());
pCharB->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharB->GetName());
SendChatPacketToObserver(CHAT_TYPE_NOTICE, LC_TEXT("%s has won the duel."), pCharB->GetName());
SPDLOG_DEBUG("ARENA: Duel is end. Winner({}) Loser({})", GetPlayerBPID(), GetPlayerAPID());
}
else
{
restart = true;
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님이 승리하였습니다."), pCharB->GetName());
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s has won."), 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_INFO, LC_TEXT("%s has won."), 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());
@ -811,18 +811,18 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
}
else
{
// 오면 안된다 ?!
// 오면 안된다 ?!
}
if (restart == false)
{
if (pCharA != NULL)
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10초뒤 마을로 되돌아갑니다."));
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You will be teleported into the city in 10 seconds."));
if ( pCharB != NULL)
pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10초뒤 마을로 되돌아갑니다."));
pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You will be teleported into the city in 10 seconds."));
SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10초뒤 마을로 되돌아갑니다."));
SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("You will be teleported into the city in 10 seconds."));
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);
@ -838,12 +838,12 @@ bool CArena::OnDead(DWORD dwPIDA, DWORD dwPIDB)
else
{
if (pCharA != NULL)
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10초뒤 다음 판을 시작합니다."));
pCharA->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The next round will begin in 10 seconds."));
if (pCharB != NULL)
pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("10초뒤 다음 판을 시작합니다."));
pCharB->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The next round will begin in 10 seconds."));
SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("10초뒤 다음 판을 시작합니다."));
SendChatPacketToObserver(CHAT_TYPE_INFO, LC_TEXT("The next round will begin in 10 seconds."));
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);
@ -949,7 +949,7 @@ void CArena::OnDisconnect(DWORD pid)
if (m_dwPIDA == pid)
{
if (GetPlayerB() != NULL)
GetPlayerB()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방 캐릭터가 접속을 종료하여 대련을 중지합니다."));
GetPlayerB()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The combatants have been separated. The duel has been stopped."));
SPDLOG_DEBUG("ARENA : Duel is end because of Opp({}) is disconnect. MyPID({})", GetPlayerAPID(), GetPlayerBPID());
EndDuel();
@ -957,7 +957,7 @@ void CArena::OnDisconnect(DWORD pid)
else if (m_dwPIDB == pid)
{
if (GetPlayerA() != NULL)
GetPlayerA()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방 캐릭터가 접속을 종료하여 대련을 중지합니다."));
GetPlayerA()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The combatants have been separated. The duel has been stopped."));
SPDLOG_DEBUG("ARENA : Duel is end because of Opp({}) is disconnect. MyPID({})", GetPlayerBPID(), GetPlayerAPID());
EndDuel();

View File

@ -215,7 +215,7 @@ void AuctionBoard::YourItemInfoList (TItemInfoVec& vec, DWORD player_id, int sta
}
// 0~1, 2~3, 4~5, 6~7, 8~9
// 짝수면 descending, 홀수면 accending.
// 짝수면 descending, 홀수면 accending.
struct FCheckGradeSatisfied
{
BYTE grade;
@ -796,7 +796,7 @@ void AuctionManager::YourBidItemInfoList (AuctionBoard::TItemInfoVec& vec, DWORD
}
else
{
// expired 만들고 여기서 넣어야한다.
// expired 만들고 여기서 넣어야한다.
}
}
}
@ -881,21 +881,21 @@ void AuctionManager::enroll_auction (LPCHARACTER ch, LPITEM item, BYTE empire, i
}
if (item->IsEquipped())
{
ch->ChatPacket(CHAT_TYPE_INFO, "장착한 건 등록할 수 없어.");
ch->ChatPacket(CHAT_TYPE_INFO, "I can't register anything that's equipped.");
return;
}
if (GetAuctionItemInfo (item->GetID()))
{
SPDLOG_ERROR("Item {} is already in auction.", item->GetID());
ch->ChatPacket(CHAT_TYPE_INFO, "이미 등록한 거야. 도대체 뭐지?");
ch->ChatPacket(CHAT_TYPE_INFO, "I'm already registered. What the heck?");
return;
}
if (item->GetWindow() == AUCTION)
{
SPDLOG_ERROR("Item {} is already in auction.", item->GetID());
ch->ChatPacket(CHAT_TYPE_INFO, "얜 또 뭐냐..");
ch->ChatPacket(CHAT_TYPE_INFO, "What the hell is this...");
return;
}
@ -918,21 +918,21 @@ void AuctionManager::enroll_sale (LPCHARACTER ch, LPITEM item, DWORD wisher_id,
}
if (item->IsEquipped())
{
ch->ChatPacket(CHAT_TYPE_INFO, "장착한 건 등록할 수 없어.");
ch->ChatPacket(CHAT_TYPE_INFO, "I can't register anything that's equipped.");
return;
}
if (GetSaleItemInfo (item->GetID()))
{
SPDLOG_ERROR("Item {} is already in auction.", item->GetID());
ch->ChatPacket(CHAT_TYPE_INFO, "이미 등록한 거야. 도대체 뭐지?");
ch->ChatPacket(CHAT_TYPE_INFO, "I'm already registered. What the heck?");
return;
}
if (item->GetWindow() == AUCTION)
{
SPDLOG_ERROR("Item {} is already in auction.", item->GetID());
ch->ChatPacket(CHAT_TYPE_INFO, "얜 또 뭐냐..");
ch->ChatPacket(CHAT_TYPE_INFO, "What the hell is this...");
return;
}
@ -959,11 +959,11 @@ void AuctionManager::bid (LPCHARACTER ch, DWORD item_id, int bid_price)
std::pair <int, bool> mb = MyBid.GetMoney(ch->GetPlayerID(), item_id);
if (mb.first != -1)
{
ch->ChatPacket (CHAT_TYPE_INFO, "재입찰을 하란 말이다.");
ch->ChatPacket (CHAT_TYPE_INFO, "Re-bid.");
}
if (ch->GetGold() < bid_price)
{
ch->ChatPacket(CHAT_TYPE_INFO, "돈이 부족해");
ch->ChatPacket(CHAT_TYPE_INFO, "I don't have enough money");
return;
}
@ -975,7 +975,7 @@ void AuctionManager::bid (LPCHARACTER ch, DWORD item_id, int bid_price)
}
// fixme
// 반드시 돈!!!
// 반드시 돈!!!
void AuctionManager::immediate_purchase (LPCHARACTER ch, DWORD item_id)
{
TAuctionItemInfo* item_info = GetAuctionItemInfo (item_id);
@ -988,13 +988,13 @@ void AuctionManager::immediate_purchase (LPCHARACTER ch, DWORD item_id)
if (item_info->get_impur_price() == 0)
{
ch->ChatPacket(CHAT_TYPE_INFO, "즉구 할 수 엄서");
ch->ChatPacket(CHAT_TYPE_INFO, "Improvisation");
return;
}
if (ch->GetGold() < item_info->get_impur_price())
{
ch->ChatPacket(CHAT_TYPE_INFO, "돈이 부족해");
ch->ChatPacket(CHAT_TYPE_INFO, "I don't have enough money");
return;
}
@ -1005,7 +1005,7 @@ void AuctionManager::immediate_purchase (LPCHARACTER ch, DWORD 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);
@ -1013,7 +1013,7 @@ void AuctionManager::get_auctioned_item (LPCHARACTER ch, DWORD item_id, DWORD it
if (pos == -1)
{
ch->ChatPacket(CHAT_TYPE_INFO, "자리가 엄서");
ch->ChatPacket(CHAT_TYPE_INFO, "Tight Spot");
return;
}
@ -1069,13 +1069,13 @@ void AuctionManager::rebid (LPCHARACTER ch, DWORD item_id, int bid_price)
if (lock)
{
ch->ChatPacket(CHAT_TYPE_INFO, "입찰 중이야.");
ch->ChatPacket(CHAT_TYPE_INFO, "You're bidding.");
return;
}
if (ch->GetGold() + money < bid_price)
{
ch->ChatPacket(CHAT_TYPE_INFO, "돈이 부족해");
ch->ChatPacket(CHAT_TYPE_INFO, "I don't have enough money");
return;
}
@ -1101,14 +1101,14 @@ void AuctionManager::bid_cancel (LPCHARACTER ch, DWORD item_id)
if (lock)
{
ch->ChatPacket(CHAT_TYPE_INFO, "입찰 중이야.");
ch->ChatPacket(CHAT_TYPE_INFO, "You're bidding.");
return;
}
TAuctionItemInfo* item_info = GetAuctionItemInfo(item_id);
if (item_info->get_bidder_id() == ch->GetPlayerID())
{
ch->ChatPacket(CHAT_TYPE_INFO, "니가 최고 입찰자야. 취소 못해.");
ch->ChatPacket(CHAT_TYPE_INFO, "You're the highest bidder. I can't cancel.");
return;
}
@ -1120,7 +1120,7 @@ void AuctionManager::bid_cancel (LPCHARACTER ch, DWORD 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);
@ -1142,7 +1142,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
Auction.InsertItemInfo (item_info);
if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "경매장에 등록했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "You've registered for the auction.");
}
break;
}
@ -1157,7 +1157,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID (player_item->owner);
ch->AutoGiveItem (item, true);
ch->ChatPacket(CHAT_TYPE_INFO, "경매장에 등록하지 못했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Failed to register for the auction house.");
}
break;
}
@ -1179,7 +1179,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
Sale.InsertItemInfo (item_info);
if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "판매장에 등록했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "You've signed up for a storefront.");
}
break;
}
@ -1193,7 +1193,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
ch->AutoGiveItem (item, true);
ch->ChatPacket(CHAT_TYPE_INFO, "판매장에 등록하지 못했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Failed to enroll in a storefront.");
}
break;
}
@ -1210,7 +1210,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
Wish.InsertItemInfo (item_info);
if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "삽니다에 등록했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Signed Up.");
}
break;
}
@ -1218,7 +1218,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
{
if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "삽니다에 등록하지 못했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Failed to sign up.");
}
break;
}
@ -1238,7 +1238,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
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, "입찰했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "You bid.");
}
}
break;
@ -1254,7 +1254,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
memcpy (old_item_info, new_item_info, sizeof(TAuctionItemInfo));
if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "즉구 해버렸어.");
ch->ChatPacket(CHAT_TYPE_INFO, "I improvised.");
}
}
break;
@ -1277,7 +1277,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
{
LPITEM item = ITEM_MANAGER::instance().CreateItem(player_item->vnum, player_item->count, item_id);
ch->AutoGiveItem (item, true);
ch->ChatPacket(CHAT_TYPE_INFO, "가져왔어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Imported.");
if (cmd == AUCTION_GET_AUC || cmd == AUCTION_CANCEL_AUC)
{
TPacketGDCommnadAuction pack_dai;
@ -1337,7 +1337,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
}
else if (ch != NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, "취소했어.");
ch->ChatPacket(CHAT_TYPE_INFO, "Canceled.");
}
}
break;
@ -1354,7 +1354,7 @@ void AuctionManager::recv_result_auction (DWORD commander_id, TPacketDGResultAuc
}
else
{
// insert하면 lock이 풀린다.
// insert하면 lock이 풀린다.
DWORD item_id = cmd_result->target;
cmd_result++;
TAuctionItemInfo* auction_info = (TAuctionItemInfo*)cmd_result;

View File

@ -63,7 +63,7 @@ private:
TPCMap offer_map;
// sorting을 위한 members
// sorting을 위한 members
public:
typedef std::vector <TAuctionItemInfo*> TItemInfoVec;
private:
@ -76,7 +76,7 @@ private:
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);
};
@ -131,7 +131,7 @@ private:
typedef std::pair <int, bool> BidInfo;
typedef std::map <DWORD, BidInfo > TItemMap;
typedef std::unordered_map <DWORD, TItemMap*> TMyBidBoard;
// bidder_id가 key
// bidder_id가 key
TMyBidBoard pc_map;
public:
@ -144,7 +144,7 @@ public:
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);
@ -156,7 +156,7 @@ private :
typedef std::unordered_map<DWORD, LPITEM> TItemMap;
TItemMap auction_item_map;
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
AuctionBoard Auction;
SaleBoard Sale;
WishBoard Wish;

View File

@ -3,7 +3,7 @@
#include "banword.h"
#include "config.h"
extern void SendLog(const char * c_pszBuf); // 운영자에게만 공지
extern void SendLog(const char * c_pszBuf); // 운영자에게만 공지
CBanwordManager::CBanwordManager()
{

View File

@ -46,7 +46,7 @@ bool timed_event_cancel(LPCHARACTER ch)
}
/* RECALL_DELAY
if (ch->m_pk_RecallEvent)
{
event_cancel(&ch->m_pkRecallEvent);
@ -59,11 +59,11 @@ bool timed_event_cancel(LPCHARACTER ch)
bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim)
{
// 상대방이 죽었으면 중단한다.
// 상대방이 죽었으면 중단한다.
if (victim->IsDead())
return false;
// 안전지대면 중단
// 안전지대면 중단
{
SECTREE *sectree = NULL;
@ -77,7 +77,7 @@ bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim)
}
// 내가 죽었으면 중단한다.
// 내가 죽었으면 중단한다.
if (ch->IsStun() || ch->IsDead())
return false;
@ -119,7 +119,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
if (test_server&&ch->IsPC())
SPDLOG_TRACE("battle_melee_attack : [{}] attack to [{}]", ch->GetName(), victim->GetName());
// 거리 체크
// 거리 체크
int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY());
if (!victim->IsBuilding())
@ -128,12 +128,12 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
if (false == ch->IsPC())
{
// 몬스터의 경우 몬스터 공격 거리를 사용
// 몬스터의 경우 몬스터 공격 거리를 사용
max = (int) (ch->GetMobAttackRange() * 1.15f);
}
else
{
// PC일 경우 상대가 melee 몹일 경우 몹의 공격 거리가 최대 공격 거리
// PC일 경우 상대가 melee 몹일 경우 몹의 공격 거리가 최대 공격 거리
if (false == victim->IsPC() && BATTLE_TYPE_MELEE == victim->GetMobBattleType())
max = std::max(300, (int) (victim->GetMobAttackRange() * 1.15f));
}
@ -147,10 +147,10 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
}
if (timed_event_cancel(ch))
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("전투가 시작 되어 취소 되었습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Action cancelled. You have entered a battle."));
if (timed_event_cancel(victim))
victim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("전투가 시작 되어 취소 되었습니다."));
victim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Action cancelled. You have entered a battle."));
ch->SetPosition(POS_FIGHTING);
ch->SetVictim(victim);
@ -161,7 +161,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
return battle_hit(ch, victim);
}
// 실제 GET_BATTLE_VICTIM을 NULL로 만들고 이벤트를 캔슬 시킨다.
// 실제 GET_BATTLE_VICTIM을 NULL로 만들고 이벤트를 캔슬 시킨다.
void battle_end_ex(LPCHARACTER ch)
{
if (ch->IsPosition(POS_FIGHTING))
@ -208,7 +208,7 @@ float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnor
int iARSrc;
int iERSrc;
if (LC_IsYMIR()) // 천마
if (LC_IsYMIR()) // 천마
{
iARSrc = std::min(90, pkAttacker->GetPolymorphPoint(POINT_DX));
iERSrc = std::min(90, pkVictim->GetPolymorphPoint(POINT_DX));
@ -238,11 +238,11 @@ float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnor
int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk)
{
// PvP에는 적용하지않음
// PvP에는 적용하지않음
if (!pkVictim->IsPC())
iAtk += pkAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_ATTACK_BONUS);
// PvP에는 적용하지않음
// PvP에는 적용하지않음
if (!pkAttacker->IsPC())
{
int iReduceDamagePct = pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_TRANSFER_DAMAGE);
@ -327,9 +327,9 @@ int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk)
}
}
//[ mob -> PC ] 원소 속성 방어 적용
//[ mob -> PC ] 원소 속성 방어 적용
//2013/01/17
//몬스터 속성공격 데미지의 30%에 해당하는 수치에만 저항이 적용됨.
//몬스터 속성공격 데미지의 30%에 해당하는 수치에만 저항이 적용됨.
if (pkAttacker->IsNPC() && pkVictim->IsPC())
{
if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ELEC))
@ -553,7 +553,7 @@ int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow,
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);
@ -613,7 +613,7 @@ int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow,
void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
{
// 독 공격은 특이하므로 특수 처리
// 독 공격은 특이하므로 특수 처리
if (pkAttacker->GetPoint(POINT_POISON_PCT) && !pkVictim->IsAffectFlag(AFF_POISON))
{
if (Random::get(1, 100) <= pkAttacker->GetPoint(POINT_POISON_PCT))
@ -637,7 +637,7 @@ int battle_hit(LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
NormalAttackAffect(pkAttacker, pkVictim);
// 데미지 계산
// 데미지 계산
//iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST)) / 100;
LPITEM pkWeapon = pkAttacker->GetWear(WEAR_WEAPON);
@ -670,7 +670,7 @@ int battle_hit(LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
}
//최종적인 데미지 보정. (2011년 2월 현재 대왕거미에게만 적용.)
//최종적인 데미지 보정. (2011년 2월 현재 대왕거미에게만 적용.)
float attMul = pkAttacker->GetAttMul();
float tempIDam = iDam;
iDam = attMul * tempIDam + 0.5f;
@ -691,19 +691,19 @@ DWORD GET_ATTACK_SPEED(LPCHARACTER 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 default_bonus = SPEEDHACK_LIMIT_BONUS * 3; // 유두리 공속(기본 80) (일반 유저가 speed hack 에 걸리는 것을 막기 위해 *3 추가. 2013.09.11 CYH)
DWORD riding_bonus = 0;
if (ch->IsRiding())
{
// 뭔가를 탔으면 추가공속 50
// 뭔가를 탔으면 추가공속 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배
// 단검의 경우 공속 2배
if (item && item->GetSubType() == WEAPON_DAGGER)
real_speed /= 2;

View File

@ -3,7 +3,7 @@
#include "char.h"
enum EBattleTypes // 상대방 기준
enum EBattleTypes // 상대방 기준
{
BATTLE_NONE,
BATTLE_DAMAGE,
@ -30,7 +30,7 @@ extern void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim);
extern int battle_hit(LPCHARACTER ch, LPCHARACTER victim);
// 특성 공격
// 특성 공격
inline void AttackAffect(LPCHARACTER pkAttacker,
LPCHARACTER pkVictim,
BYTE att_point,

View File

@ -13,7 +13,7 @@ public:
{
static TGradeUnit beltGradeByLevelTable[] =
{
0, // 벨트+0
0, // 벨트+0
1, // +1
1, // +2
2, // +3
@ -34,20 +34,20 @@ public:
return beltGradeByLevelTable[level];
}
// 현재 벨트 레벨을 기준으로, 어떤 셀들을 이용할 수 있는지 리턴
// 현재 벨트 레벨을 기준으로, 어떤 셀들을 이용할 수 있는지 리턴
static const TGradeUnit* GetAvailableRuleTableByGrade()
{
/**
+0 ~ +9 , 7 .
. >= .
(, 0 , )
+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)
4x4 (16)
*/
static TGradeUnit availableRuleByGrade[BELT_INVENTORY_SLOT_COUNT] = {
@ -62,14 +62,14 @@ public:
static bool IsAvailableCell(WORD cell, int beltGrade /*int beltLevel*/)
{
// 기획 또 바뀜.. 아놔...
// 기획 또 바뀜.. 아놔...
//const TGradeUnit beltGrade = GetBeltGradeByRefineLevel(beltLevel);
const TGradeUnit* ruleTable = GetAvailableRuleTableByGrade();
return ruleTable[cell] <= beltGrade;
}
/// pc의 벨트 인벤토리에 아이템이 하나라도 존재하는 지 검사하는 함수.
/// pc의 벨트 인벤토리에 아이템이 하나라도 존재하는 지 검사하는 함수.
static bool IsExistItemInBeltInventory(LPCHARACTER pc)
{
for (WORD i = BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END; ++i)
@ -83,7 +83,7 @@ public:
return false;
}
/// item이 벨트 인벤토리에 들어갈 수 있는 타입인지 검사하는 함수. (이 규칙은 기획자가 결정함)
/// item이 벨트 인벤토리에 들어갈 수 있는 타입인지 검사하는 함수. (이 규칙은 기획자가 결정함)
static bool CanMoveIntoBeltInventory(LPITEM item)
{
bool canMove = false;

View File

@ -170,10 +170,10 @@ static int FN_random_index()
return 0;
}
// 충기환의 확률 테이블
// blend.txt에서 확률도 받도록 고치면 깔끔하겠지만
// 각 나라별로 item proto 등을 따로 관리하므로,
// 혼란이 올 수 있어 이렇게 추가한다.
// 충기환의 확률 테이블
// blend.txt에서 확률도 받도록 고치면 깔끔하겠지만
// 각 나라별로 item proto 등을 따로 관리하므로,
// 혼란이 올 수 있어 이렇게 추가한다.
// by rtsummit
static int FN_ECS_random_index()

View File

@ -39,8 +39,8 @@ void CBuffOnAttributes::RemoveBuffFromItem(LPITEM pItem)
{
TPlayerItemAttribute attr = pItem->GetAttribute(j);
TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType);
// m_map_additional_attrs에서 해당 attribute type에 대한 값을 제거하고,
// 변경된 값의 (m_bBuffValue)%만큼의 버프 효과 감소
// m_map_additional_attrs에서 해당 attribute type에 대한 값을 제거하고,
// 변경된 값의 (m_bBuffValue)%만큼의 버프 효과 감소
if (it != m_map_additional_attrs.end())
{
int& sum_of_attr_value = it->second;
@ -76,15 +76,15 @@ void CBuffOnAttributes::AddBuffFromItem(LPITEM pItem)
TPlayerItemAttribute attr = pItem->GetAttribute(j);
TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType);
// m_map_additional_attrs에서 해당 attribute type에 대한 값이 없다면 추가.
// 추가된 값의 (m_bBuffValue)%만큼의 버프 효과 추가
// 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)%만큼의 버프 효과 추가
// m_map_additional_attrs에서 해당 attribute type에 대한 값이 있다면, 그 값을 증가시키고,
// 변경된 값의 (m_bBuffValue)%만큼의 버프 효과 추가
else
{
int& sum_of_attr_value = it->second;
@ -105,8 +105,8 @@ void CBuffOnAttributes::ChangeBuffValue(BYTE bNewValue)
Off();
else
{
// 기존에, m_map_additional_attrs의 값의 (m_bBuffValue)%만큼이 버프로 들어가 있었으므로,
// (bNewValue)%만큼으로 값을 변경함.
// 기존에, 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;

View File

@ -9,20 +9,20 @@ public:
CBuffOnAttributes(LPCHARACTER pOwner, BYTE m_point_type, std::vector <BYTE>* vec_buff_targets);
~CBuffOnAttributes();
// 장착 중 이면서, m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템으로 인해 붙은 효과를 제거.
// 장착 중 이면서, m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템으로 인해 붙은 효과를 제거.
void RemoveBuffFromItem(LPITEM pItem);
// m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템의 attribute에 대한 효과 추가.
// m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템의 attribute에 대한 효과 추가.
void AddBuffFromItem(LPITEM pItem);
// m_bBuffValue를 바꾸고, 버프의 값도 바꿈.
// m_bBuffValue를 바꾸고, 버프의 값도 바꿈.
void ChangeBuffValue(BYTE bNewValue);
// CHRACTRE::ComputePoints에서 속성치를 초기화하고 다시 계산하므로,
// 버프 속성치들을 강제적으로 owner에게 줌.
// CHRACTRE::ComputePoints에서 속성치를 초기화하고 다시 계산하므로,
// 버프 속성치들을 강제적으로 owner에게 줌.
void GiveAllAttributes();
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하고,
// 그 attribute들의 (m_bBuffValue)% 만큼을 버프로 줌.
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하고,
// 그 attribute들의 (m_bBuffValue)% 만큼을 버프로 줌.
bool On(BYTE bValue);
// 버프 제거 후, 초기화
// 버프 제거 후, 초기화
void Off();
void Initialize();
@ -32,9 +32,9 @@ private:
BYTE m_bBuffValue;
std::vector <BYTE>* m_p_vec_buff_wear_targets;
// apply_type, apply_value 페어의 맵
// apply_type, apply_value 페어의 맵
typedef std::map <BYTE, int> TMapAttr;
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하여 가지고 있음.
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하여 가지고 있음.
TMapAttr m_map_additional_attrs;
};

View File

@ -112,7 +112,7 @@ void CObject::EncodeInsertPacket(LPENTITY entity)
pack.z = GetZ();
pack.wRaceNum = m_data.dwVnum;
// 빌딩 회전 정보(벽일때는 문 위치)를 변환
// 빌딩 회전 정보(벽일때는 문 위치)를 변환
pack.dwAffectFlag[0] = unsigned(m_data.xRot);
pack.dwAffectFlag[1] = unsigned(m_data.yRot);
@ -298,7 +298,7 @@ void CObject::RegenNPC()
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());
@ -690,10 +690,10 @@ TObjectProto * CManager::GetObjectProto(DWORD dwVnum)
bool CManager::LoadLand(TLand * pTable) // from DB
{
// MapAllow에 없는 맵의 땅일지라도 load를 해야한다.
// 건물(object)이 어느 길드에 속해 있는지 알기 위해서는 건물이 세위진 땅이 어느 길드 소속인지 알아한다.
// 만약 땅을 load해 놓지 않으면 길드 건물이 어느 길드에 소속된 건지 알지 못해서
// 길드 건물에 의한 길드 버프를 받지 못한다.
// MapAllow에 없는 맵의 땅일지라도 load를 해야한다.
// 건물(object)이 어느 길드에 속해 있는지 알기 위해서는 건물이 세위진 땅이 어느 길드 소속인지 알아한다.
// 만약 땅을 load해 놓지 않으면 길드 건물이 어느 길드에 소속된 건지 알지 못해서
// 길드 건물에 의한 길드 버프를 받지 못한다.
//if (!map_allow_find(pTable->lMapIndex))
// return false;
@ -1098,7 +1098,7 @@ bool CLand::RequestCreateWall(int nMapIndex, float rot)
int wall_half_w = 1000;
int wall_half_h = 1362;
if (rot == 0.0f) // 남쪽 문
if (rot == 0.0f) // 남쪽 문
{
int door_x = wall_x;
int door_y = wall_y + wall_half_h;
@ -1107,7 +1107,7 @@ bool CLand::RequestCreateWall(int nMapIndex, float rot)
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) // 북쪽 문
else if (rot == 180.0f) // 북쪽 문
{
int door_x = wall_x;
int door_y = wall_y - wall_half_h;
@ -1116,7 +1116,7 @@ bool CLand::RequestCreateWall(int nMapIndex, float rot)
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) // 동쪽 문
else if (rot == 90.0f) // 동쪽 문
{
int door_x = wall_x + wall_half_h;
int door_y = wall_y;
@ -1125,7 +1125,7 @@ bool CLand::RequestCreateWall(int nMapIndex, float rot)
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) // 서쪽 문
else if (rot == 270.0f) // 서쪽 문
{
int door_x = wall_x - wall_half_h;
int door_y = wall_y;

View File

@ -3,8 +3,8 @@
* file : castle.cpp
* author : mhh
* description :
* : 11506 - 11510
* : 8012 - 8014, 8024-8027
* : 11506 - 11510
* : 8012 - 8014, 8024-8027
*/
#define _castle_cpp_
@ -21,13 +21,13 @@
#include "char.h"
#include "sectree_manager.h"
#define EMPIRE_NONE 0 // 아무국가 아님
#define EMPIRE_RED 1 // 신수
#define EMPIRE_YELLOW 2 // 천조
#define EMPIRE_BLUE 3 // 진노
#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 SIEGE_EVENT_PULSE PASSES_PER_SEC(60*5) // 5분
#define GET_CAHR_MANAGER() CHARACTER_MANAGER::instance()
@ -171,7 +171,7 @@ static POSITION s_frog_pos[4][MAX_CASTLE_FROG] = {
};
/* 경비병 경비구역 */
/* 경비병 경비구역 */
struct GUARD_REGION
{
int sx, sy, ex, ey;
@ -247,10 +247,10 @@ EVENTFUNC(castle_siege_event)
info->pulse += SIEGE_EVENT_PULSE;
// 공성 시작후 30분 이내라면 안내만 하자
// 공성 시작후 30분 이내라면 안내만 하자
if (info->pulse < PASSES_PER_SEC(30*60))
{
snprintf(buf, sizeof(buf), LC_TEXT("%s에서 봉화를 둘러싸고 전투가 진행중입니다."),
snprintf(buf, sizeof(buf), LC_TEXT("There are %s wars to inflame the bonfires."),
EMPIRE_NAME(GET_SIEGE_EMPIRE()));
BroadcastNotice(buf);
@ -264,19 +264,19 @@ EVENTFUNC(castle_siege_event)
case CASTLE_SIEGE_STRUGGLE:
{
snprintf(buf, sizeof(buf), LC_TEXT("%s이 수성에 성공했습니다."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
snprintf(buf, sizeof(buf), LC_TEXT("%s has successfully defended."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
BroadcastNotice(buf);
snprintf(buf, sizeof(buf), LC_TEXT("지금부터 %s은 30분간 봉화를 파괴하여 보상을 획득 할 수 있습니다."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
snprintf(buf, sizeof(buf), LC_TEXT("30 minutes from now on the player %s can get a reward because he destroyed the bonfire."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
BroadcastNotice(buf);
GET_SIEGE_STATE() = CASTLE_SIEGE_END;
return PASSES_PER_SEC(60*30); // 30분
return PASSES_PER_SEC(60*30); // 30분
}
break;
case CASTLE_SIEGE_END:
BroadcastNotice(LC_TEXT("30분이 경과했습니다.. 봉화가 사라집니다."));
BroadcastNotice(LC_TEXT("30 minutes are over. The bonfires have disappeared."));
castle_end_siege();
break;
}
@ -331,7 +331,7 @@ EVENTFUNC(castle_stone_event)
if (NULL == sectree_map)
return 0;
/* 15마리씩 2번 소환 */
/* 15마리씩 2번 소환 */
const int SPAWN_COUNT = 15;
if (info->spawn_count < (SPAWN_COUNT * 2))
@ -351,7 +351,7 @@ EVENTFUNC(castle_stone_event)
info->spawn_count += SPAWN_COUNT;
if (info->spawn_count < (SPAWN_COUNT * 2))
return PASSES_PER_SEC(30 * 60); // 30분
return PASSES_PER_SEC(30 * 60); // 30분
else
return 0;
}
@ -598,24 +598,24 @@ void castle_start_siege(int empire, int tower_count)
castle_spawn_tower(empire, tower_count);
/* 공성 타이머 시작 */
/* 공성 타이머 시작 */
{
castle_event_info* info = AllocEventInfo<castle_event_info>();
info->empire = empire;
info->pulse = 0;
GET_SIEGE_EVENT(empire) = event_create(castle_siege_event, info, /*5분*/SIEGE_EVENT_PULSE);
GET_SIEGE_EVENT(empire) = event_create(castle_siege_event, info, /*5분*/SIEGE_EVENT_PULSE);
}
/* 메틴석 소환 타이머 시작 */
/* 메틴석 소환 타이머 시작 */
{
castle_stone_event_info* info = AllocEventInfo<castle_stone_event_info>();
info->spawn_count = 0;
info->empire = empire;
GET_STONE_EVENT(empire) = event_create(castle_stone_event, info, /* 1초 */PASSES_PER_SEC(1));
GET_STONE_EVENT(empire) = event_create(castle_stone_event, info, /* 1초 */PASSES_PER_SEC(1));
}
}
@ -649,7 +649,7 @@ LPCHARACTER castle_spawn_frog(int empire)
int dir = 1;
int map_index = FN_castle_map_index(empire);
/* 황금두꺼비 소환할 곳이 있나? */
/* 황금두꺼비 소환할 곳이 있나? */
POSITION *empty_pos = FN_empty_frog_pos(empire);
if (NULL == empty_pos)
return NULL;
@ -667,7 +667,7 @@ LPCHARACTER castle_spawn_frog(int empire)
{
frog->SetEmpire(empire);
int empty_index = FN_empty_frog_index(empire);
// 스폰성공
// 스폰성공
GET_FROG(empire, empty_index) = frog;
return frog;
}
@ -778,7 +778,7 @@ bool castle_spawn_tower(int empire, int tower_count)
if (NULL == sectree_map)
return false;
// 초기화
// 초기화
DO_ALL_TOWER(i)
{
if (GET_TOWER(empire, i))
@ -786,7 +786,7 @@ bool castle_spawn_tower(int empire, int tower_count)
GET_TOWER(empire, i) = NULL;
}
int spawn_count = std::clamp(tower_count, MIN_CASTLE_TOWER, MAX_CASTLE_TOWER); // 5 ~ 10마리
int spawn_count = std::clamp(tower_count, MIN_CASTLE_TOWER, MAX_CASTLE_TOWER); // 5 ~ 10마리
for (int j = 0; j < spawn_count; ++j)
{
@ -796,13 +796,13 @@ bool castle_spawn_tower(int empire, int tower_count)
// broad cast
{
char buf[1024];
snprintf(buf, sizeof(buf), LC_TEXT("%s에 전쟁의 시작을 알리는 봉화가 나타났습니다."), EMPIRE_NAME(empire));
snprintf(buf, sizeof(buf), LC_TEXT("A bonfire was inflamed at %s to warn because of a battle."), EMPIRE_NAME(empire));
BroadcastNotice(buf);
}
return true;
}
/* 경비병리더가 죽으면 단순하게 슬롯만 비운다. */
/* 경비병리더가 죽으면 단순하게 슬롯만 비운다. */
void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer)
{
int empire = ch->GetEmpire();
@ -823,7 +823,7 @@ void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer)
}
/* 황금 두꺼비가 죽으면 killer에게 1천만냥 */
/* 황금 두꺼비가 죽으면 killer에게 1천만냥 */
void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer)
{
if (NULL == ch || NULL == killer)
@ -837,15 +837,15 @@ void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer)
{
GET_FROG(empire, i) = NULL;
killer->PointChange(POINT_GOLD, 10000000 /*1천만*/, true);
//CMonarch::instance().SendtoDBAddMoney(30000000/*3천만*/, killer->GetEmpire(), killer);
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};
@ -864,7 +864,7 @@ void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer)
case CASTLE_SIEGE_END:
{
int siege_end = true;
snprintf(buf, sizeof(buf), LC_TEXT("%s이 봉화를 파괴했습니다."), EMPIRE_NAME(killer_empire));
snprintf(buf, sizeof(buf), LC_TEXT("%s has destroyed the bonfire."), EMPIRE_NAME(killer_empire));
BroadcastNotice(buf);
LogManager::instance().CharLog(killer, 0, "CASTLE_TORCH_KILL", "");
@ -885,12 +885,12 @@ void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer)
{
if (GET_SIEGE_STATE() == CASTLE_SIEGE_STRUGGLE)
{
snprintf(buf, sizeof(buf), LC_TEXT("%s이 수성에 실패하여 전쟁에 패배하였습니다.."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
snprintf(buf, sizeof(buf), LC_TEXT("%s lost the war as they have not been able to defend the castle."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
BroadcastNotice(buf);
}
else
{
snprintf(buf, sizeof(buf), LC_TEXT("%s이 모든 봉화를 파괴하였습니다."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
snprintf(buf, sizeof(buf), LC_TEXT("%s has destroyed all the bonfires."), EMPIRE_NAME(GET_SIEGE_EMPIRE()));
BroadcastNotice(buf);
}
castle_end_siege();
@ -929,27 +929,27 @@ 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:
@ -963,34 +963,34 @@ int castle_cost_of_hiring_guard(DWORD group_vnum)
{
switch (group_vnum)
{
/* 하급 */
case 9501: /* 신수 창경비 */
case 9511: /* 진노 창경비 */
case 9521: /* 천조 창경비 */
/* 하급 */
case 9501: /* 신수 창경비 */
case 9511: /* 진노 창경비 */
case 9521: /* 천조 창경비 */
case 9502: /* 신수 활경비 */
case 9512: /* 진노 활경비 */
case 9522: /* 천조 활경비 */
case 9502: /* 신수 활경비 */
case 9512: /* 진노 활경비 */
case 9522: /* 천조 활경비 */
return (100*10000);
/* 중급 */
case 9503: /* 신수 창경비 */
case 9513: /* 진노 창경비 */
case 9523: /* 천조 창경비 */
/* 중급 */
case 9503: /* 신수 창경비 */
case 9513: /* 진노 창경비 */
case 9523: /* 천조 창경비 */
case 9504: /* 신수 활경비 */
case 9514: /* 진노 활경비 */
case 9524: /* 천조 활경비 */
case 9504: /* 신수 활경비 */
case 9514: /* 진노 활경비 */
case 9524: /* 천조 활경비 */
return (300*10000);
/* 상급 */
case 9505: /* 신수 창경비 */
case 9515: /* 진노 창경비 */
case 9525: /* 천조 창경비 */
/* 상급 */
case 9505: /* 신수 창경비 */
case 9515: /* 진노 창경비 */
case 9525: /* 천조 창경비 */
case 9506: /* 신수 활경비 */
case 9516: /* 진노 활경비 */
case 9526: /* 천조 활경비 */
case 9506: /* 신수 활경비 */
case 9516: /* 진노 활경비 */
case 9526: /* 천조 활경비 */
return (1000*10000);
}
@ -1010,7 +1010,7 @@ bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim)
if (CASTLE_SIEGE_END == GET_SIEGE_STATE())
{
// 수성에 성공했을때 같은 제국만 봉화를 칠 수 있음
// 수성에 성공했을때 같은 제국만 봉화를 칠 수 있음
if (castle_is_tower_vnum(victim->GetRaceNum()))
{
if (ch->GetEmpire() == victim->GetEmpire())
@ -1020,7 +1020,7 @@ bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim)
}
}
// 같은 제국은 파괴 불가
// 같은 제국은 파괴 불가
if (ch->GetEmpire() == victim->GetEmpire())
return false;
@ -1044,7 +1044,7 @@ bool castle_frog_to_empire_money(LPCHARACTER ch)
if (false == CMonarch::instance().SendtoDBAddMoney(CASTLE_FROG_PRICE, empire, ch))
return false;
GET_FROG(empire, i) = NULL; // 등록해제
GET_FROG(empire, i) = NULL; // 등록해제
npc->Dead(/*killer*/NULL, /*immediate_dead*/true);
return true;
}

View File

@ -9,36 +9,36 @@
#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 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 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시간동안 봉화를 깰 수 있다.
CASTLE_SIEGE_NONE, // 평화모드
CASTLE_SIEGE_STRUGGLE, // 공성중
CASTLE_SIEGE_END // 수성에 성공했다면 1시간동안 봉화를 깰 수 있다.
};
struct CASTLE_DATA
{
LPCHARACTER frog[MAX_CASTLE_FROG]; // 황금두꺼비
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 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]; // 봉화
LPCHARACTER tower[MAX_CASTLE_TOWER]; // 봉화
LPEVENT siege_event;
LPEVENT stone_event;

File diff suppressed because it is too large Load Diff

View File

@ -83,7 +83,7 @@ enum EDamageType
DAMAGE_TYPE_NONE,
DAMAGE_TYPE_NORMAL,
DAMAGE_TYPE_NORMAL_RANGE,
//스킬
//스킬
DAMAGE_TYPE_MELEE,
DAMAGE_TYPE_RANGE,
DAMAGE_TYPE_FIRE,
@ -105,103 +105,103 @@ enum EPointTypes
POINT_MAX_HP, // 6
POINT_SP, // 7
POINT_MAX_SP, // 8
POINT_STAMINA, // 9 스테미너
POINT_MAX_STAMINA, // 10 최대 스테미너
POINT_STAMINA, // 9 스테미너
POINT_MAX_STAMINA, // 10 최대 스테미너
POINT_GOLD, // 11
POINT_ST, // 12 근력
POINT_HT, // 13 체력
POINT_DX, // 14 민첩성
POINT_IQ, // 15 정신력
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_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_BOW_DISTANCE, // 34 활 사정거리 증가치 (meter)
POINT_HP_RECOVERY, // 35 체력 회복 증가량
POINT_SP_RECOVERY, // 36 정신력 회복 증가량
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_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_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_RESIST_WARRIOR, // 59 무사에게 저항
POINT_RESIST_ASSASSIN, // 60 자객에게 저항
POINT_RESIST_SURA, // 61 수라에게 저항
POINT_RESIST_SHAMAN, // 62 무당에게 저항
POINT_STEAL_HP, // 63 생명력 흡수
POINT_STEAL_SP, // 64 정신력 흡수
POINT_STEAL_HP, // 63 생명력 흡수
POINT_STEAL_SP, // 64 정신력 흡수
POINT_MANA_BURN_PCT, // 65 마나 번
POINT_MANA_BURN_PCT, // 65 마나 번
/// 피해시 보너스 ///
/// 피해시 보너스 ///
POINT_DAMAGE_SP_RECOVER, // 66 공격당할 시 정신력 회복 확률
POINT_DAMAGE_SP_RECOVER, // 66 공격당할 시 정신력 회복 확률
POINT_BLOCK, // 67 블럭율
POINT_DODGE, // 68 회피율
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_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_MELEE, // 79 공격 반사
/// 특수 피해시 ///
POINT_REFLECT_CURSE, // 80 저주 반사
POINT_POISON_REDUCE, // 81 독데미지 감소
/// 특수 피해시 ///
POINT_REFLECT_CURSE, // 80 저주 반사
POINT_POISON_REDUCE, // 81 독데미지 감소
/// 적 소멸시 ///
POINT_KILL_SP_RECOVER, // 82 적 소멸시 MP 회복
/// 적 소멸시 ///
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
@ -225,7 +225,7 @@ enum EPointTypes
POINT_HIT_HP_RECOVERY, // 100
POINT_HIT_SP_RECOVERY, // 101
POINT_MANASHIELD, // 102 흑신수호 스킬에 의한 마나쉴드 효과 정도
POINT_MANASHIELD, // 102 흑신수호 스킬에 의한 마나쉴드 효과 정도
POINT_PARTY_BUFFER_BONUS, // 103
POINT_PARTY_SKILL_MASTER_BONUS, // 104
@ -234,58 +234,57 @@ enum EPointTypes
POINT_SP_RECOVER_CONTINUE, // 106
POINT_STEAL_GOLD, // 107
POINT_POLYMORPH, // 108 변신한 몬스터 번호
POINT_MOUNT, // 109 타고있는 몬스터 번호
POINT_POLYMORPH, // 108 변신한 몬스터 번호
POINT_MOUNT, // 109 타고있는 몬스터 번호
POINT_PARTY_HASTE_BONUS, // 110
POINT_PARTY_DEFENDER_BONUS, // 111
POINT_STAT_RESET_COUNT, // 112 피의 단약 사용을 통한 스텟 리셋 포인트 (1당 1포인트 리셋가능)
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_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_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)%
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 평타 방어 데미지
POINT_SKILL_DEFEND_BONUS, // 123 스킬 방어 데미지
POINT_NORMAL_HIT_DEFEND_BONUS, // 124 평타 방어 데미지
// END_OF_DEFEND_BONUS_ATTRIBUTES
// TODO: check if the PC_BANG_* bonuses can be safely removed
// PC_BANG_ITEM_ADD
POINT_PC_BANG_EXP_BONUS, // 125 PC방 전용 경험치 보너스
POINT_PC_BANG_DROP_BONUS, // 126 PC방 전용 드롭률 보너스
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_RAMADAN_CANDY_BONUS_EXP, // 라마단 사탕 경험치 증가용
POINT_ENERGY = 128, // 128 기력
POINT_ENERGY = 128, // 128 기력
// 기력 ui 용.
// 서버에서 쓰지 않기만, 클라이언트에서 기력의 끝 시간을 POINT로 관리하기 때문에 이렇게 한다.
// 아 부끄럽다
POINT_ENERGY_END_TIME = 129, // 129 기력 종료 시간
// 기력 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_ICE = 133, // 냉기 저항 : 얼음공격에 대한 대미지 감소
POINT_RESIST_EARTH = 134, // 대지 저항 : 얼음공격에 대한 대미지 감소
POINT_RESIST_DARK = 135, // 어둠 저항 : 얼음공격에 대한 대미지 감소
POINT_RESIST_CRITICAL = 136, // 크리티컬 저항 : 상대의 크리티컬 확률을 감소
POINT_RESIST_PENETRATE = 137, // 관통타격 저항 : 상대의 관통타격 확률을 감소
POINT_RESIST_CRITICAL = 136, // 크리티컬 저항 : 상대의 크리티컬 확률을 감소
POINT_RESIST_PENETRATE = 137, // 관통타격 저항 : 상대의 관통타격 확률을 감소
//POINT_MAX_NUM = 129 common/length.h
};
@ -354,7 +353,7 @@ struct DynamicCharacterPtr {
uint32_t id;
};
/* 저장하는 데이터 */
/* 저장하는 데이터 */
typedef struct character_point
{
int points[POINT_MAX_NUM];
@ -377,7 +376,7 @@ typedef struct character_point
BYTE skill_group;
} CHARACTER_POINT;
/* 저장되지 않는 캐릭터 데이터 */
/* 저장되지 않는 캐릭터 데이터 */
typedef struct character_point_instant
{
int points[POINT_MAX_NUM];
@ -399,7 +398,7 @@ typedef struct character_point_instant
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];
@ -411,7 +410,7 @@ typedef struct character_point_instant
BYTE gm_level;
BYTE bBasePart; // 평상복 번호
BYTE bBasePart; // 평상복 번호
int iMaxStamina;
@ -509,7 +508,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
{
protected:
//////////////////////////////////////////////////////////////////////////////////
// Entity 관련
// Entity 관련
virtual void EncodeInsertPacket(LPENTITY entity);
virtual void EncodeRemovePacket(LPENTITY entity);
//////////////////////////////////////////////////////////////////////////////////
@ -519,7 +518,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void UpdatePacket();
//////////////////////////////////////////////////////////////////////////////////
// FSM (Finite State Machine) 관련
// FSM (Finite State Machine) 관련
protected:
CStateTemplate<CHARACTER> m_stateMove;
CStateTemplate<CHARACTER> m_stateBattle;
@ -589,13 +588,13 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
DWORD GetPlayerID() const { return m_dwPlayerID; }
void SetPlayerProto(const TPlayerTable * table);
void CreatePlayerProto(TPlayerTable & tab); // 저장 시 사용
void CreatePlayerProto(TPlayerTable & tab); // 저장 시 사용
void SetProto(const CMob * c_pkMob);
WORD GetRaceNum() const;
void Save(); // DelayedSave
void SaveReal(); // 실제 저장
void SaveReal(); // 실제 저장
void FlushDelayedSaveItem();
const char * GetName() const;
@ -636,7 +635,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
DWORD GetExp() const { return m_points.exp; }
void SetExp(DWORD exp) { m_points.exp = exp; }
DWORD GetNextExp() const;
LPCHARACTER DistributeExp(); // 제일 많이 때린 사람을 리턴한다.
LPCHARACTER DistributeExp(); // 제일 많이 때린 사람을 리턴한다.
void DistributeHP(LPCHARACTER pkKiller);
void DistributeSP(LPCHARACTER pkKiller, int iMethod=0);
@ -719,14 +718,14 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
DWORD GetPolymorphItemVnum() const;
DWORD GetMonsterDrainSPPoint() const;
void MainCharacterPacket(); // 내가 메인캐릭터라고 보내준다.
void MainCharacterPacket(); // 내가 메인캐릭터라고 보내준다.
void ComputePoints();
void ComputeBattlePoints();
void PointChange(BYTE type, int amount, bool bAmount = false, bool bBroadcast = false);
void PointsPacket();
void ApplyPoint(BYTE bApplyType, int iVal);
void CheckMaximumPoints(); // HP, SP 등의 현재 값이 최대값 보다 높은지 검사하고 높다면 낮춘다.
void CheckMaximumPoints(); // HP, SP 등의 현재 값이 최대값 보다 높은지 검사하고 높다면 낮춘다.
bool Show(int lMapIndex, int x, int y, int z = INT_MAX, bool bShowSpawnMotion = false);
@ -750,7 +749,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
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; } // 이전 스텟을 유지하는 폴리모프.
bool IsPolyMaintainStat() const { return m_bPolyMaintainStat; } // 이전 스텟을 유지하는 폴리모프.
void SetPolymorph(DWORD dwRaceNum, bool bMaintainStat = false);
DWORD GetPolymorphVnum() const { return m_dwPolymorphRace; }
int GetPolymorphPower() const;
@ -805,15 +804,15 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void SetNowWalking(bool bWalkFlag);
void ResetWalking() { SetNowWalking(m_bWalking); }
bool Goto(int x, int y); // 바로 이동 시키지 않고 목표 위치로 BLENDING 시킨다.
bool Goto(int x, int y); // 바로 이동 시키지 않고 목표 위치로 BLENDING 시킨다.
void Stop();
bool CanMove() const; // 이동할 수 있는가?
bool CanMove() const; // 이동할 수 있는가?
void SyncPacket();
bool Sync(int x, int y); // 실제 이 메소드로 이동 한다 (각 종 조건에 의한 이동 불가가 없음)
bool Move(int x, int y); // 조건을 검사하고 Sync 메소드를 통해 이동 한다.
void OnMove(bool bIsAttack = false); // 움직일때 불린다. Move() 메소드 이외에서도 불릴 수 있다.
bool Sync(int x, int y); // 실제 이 메소드로 이동 한다 (각 종 조건에 의한 이동 불가가 없음)
bool Move(int x, int y); // 조건을 검사하고 Sync 메소드를 통해 이동 한다.
void OnMove(bool bIsAttack = false); // 움직일때 불린다. Move() 메소드 이외에서도 불릴 수 있다.
DWORD GetMotionMode() const;
float GetMoveMotionSpeed() const;
float GetMoveSpeed() const;
@ -824,7 +823,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
DWORD GetLastMoveTime() const { return m_dwLastMoveTime; }
DWORD GetLastAttackTime() const { return m_dwLastAttackTime; }
void SetLastAttacked(DWORD time); // 마지막으로 공격받은 시간 및 위치를 저장함
void SetLastAttacked(DWORD time); // 마지막으로 공격받은 시간 및 위치를 저장함
bool SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList = true);
bool IsSyncOwner(LPCHARACTER ch) const;
@ -851,7 +850,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
float m_fSyncTime;
LPCHARACTER m_pkChrSyncOwner;
CHARACTER_LIST m_kLst_pkChrSyncOwned; // 내가 SyncOwner인 자들
CHARACTER_LIST m_kLst_pkChrSyncOwned; // 내가 SyncOwner인 자들
PIXEL_POSITION m_posDest;
PIXEL_POSITION m_posStart;
@ -874,7 +873,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool m_bStaminaConsume;
// End
// Quickslot 관련
// Quickslot 관련
public:
void SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos);
bool GetQuickslot(BYTE pos, TQuickslot ** ppSlot);
@ -903,7 +902,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void LoadAffect(DWORD dwCount, TPacketAffectElement * pElements);
void SaveAffect();
// Affect loading이 끝난 상태인가?
// Affect loading이 끝난 상태인가?
bool IsLoadedAffect() const { return m_bIsLoadedAffect; }
bool IsGoodAffect(BYTE bAffectType) const;
@ -929,25 +928,25 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void DenyToParty(LPCHARACTER member);
void AcceptToParty(LPCHARACTER member);
/// 자신의 파티에 다른 character 를 초대한다.
/// 자신의 파티에 다른 character 를 초대한다.
/**
* @param pchInvitee character. .
* @param pchInvitee character. .
*
* character .
* character .
*/
void PartyInvite(LPCHARACTER pchInvitee);
/// 초대했던 character 의 수락을 처리한다.
/// 초대했던 character 의 수락을 처리한다.
/**
* @param pchInvitee character. .
* @param pchInvitee character. .
*
* pchInvitee .
* pchInvitee .
*/
void PartyInviteAccept(LPCHARACTER pchInvitee);
/// 초대했던 character 의 초대 거부를 처리한다.
/// 초대했던 character 의 초대 거부를 처리한다.
/**
* @param [in] dwPID character PID
* @param [in] dwPID character PID
*/
void PartyInviteDeny(DWORD dwPID);
@ -960,45 +959,45 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
protected:
/// 파티에 가입한다.
/// 파티에 가입한다.
/**
* @param pkLeader
* @param pkLeader
*/
void PartyJoin(LPCHARACTER pkLeader);
/**
* .
* Error code (mutable) type (static) type .
* Error code PERR_SEPARATOR type type .
* .
* Error code (mutable) type (static) type .
* Error code PERR_SEPARATOR type type .
*/
enum PartyJoinErrCode {
PERR_NONE = 0, ///< 처리성공
PERR_SERVER, ///< 서버문제로 파티관련 처리 불가
PERR_DUNGEON, ///< 캐릭터가 던전에 있음
PERR_OBSERVER, ///< 관전모드임
PERR_LVBOUNDARY, ///< 상대 캐릭터와 레벨차이가 남
PERR_LOWLEVEL, ///< 상대파티의 최고레벨보다 30레벨 낮음
PERR_HILEVEL, ///< 상대파티의 최저레벨보다 30레벨 높음
PERR_ALREADYJOIN, ///< 파티가입 대상 캐릭터가 이미 파티중
PERR_PARTYISFULL, ///< 파티인원 제한 초과
PERR_NONE = 0, ///< 처리성공
PERR_SERVER, ///< 서버문제로 파티관련 처리 불가
PERR_DUNGEON, ///< 캐릭터가 던전에 있음
PERR_OBSERVER, ///< 관전모드임
PERR_LVBOUNDARY, ///< 상대 캐릭터와 레벨차이가 남
PERR_LOWLEVEL, ///< 상대파티의 최고레벨보다 30레벨 낮음
PERR_HILEVEL, ///< 상대파티의 최저레벨보다 30레벨 높음
PERR_ALREADYJOIN, ///< 파티가입 대상 캐릭터가 이미 파티중
PERR_PARTYISFULL, ///< 파티인원 제한 초과
PERR_SEPARATOR, ///< Error type separator.
PERR_DIFFEMPIRE, ///< 상대 캐릭터와 다른 제국임
PERR_MAX ///< Error code 최고치. 이 앞에 Error code 를 추가한다.
PERR_DIFFEMPIRE, ///< 상대 캐릭터와 다른 제국임
PERR_MAX ///< Error code 최고치. 이 앞에 Error code 를 추가한다.
};
/// 파티 가입이나 결성 가능한 조건을 검사한다.
/// 파티 가입이나 결성 가능한 조건을 검사한다.
/**
* @param pchLeader leader character
* @param pchGuest character
* @return PartyJoinErrCode .
* @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 .
* @param pchLeader leader character
* @param pchGuest character
* @return mutable type code .
*/
static PartyJoinErrCode IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest);
@ -1007,11 +1006,11 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
LPEVENT m_pkPartyRequestEvent;
/**
* Event map.
* key: PID
* value: event의 pointer
* Event map.
* key: PID
* value: event의 pointer
*
* event map.
* event map.
*/
typedef std::map< DWORD, LPEVENT > EventMap;
EventMap m_PartyInviteEventMap;
@ -1045,7 +1044,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
////////////////////////////////////////////////////////////////////////////////////////
// Item related
public:
bool CanHandleItem(bool bSkipRefineCheck = false, bool bSkipObserver = false); // 아이템 관련 행위를 할 수 있는가?
bool CanHandleItem(bool bSkipRefineCheck = false, bool bSkipObserver = false); // 아이템 관련 행위를 할 수 있는가?
bool IsItemLoaded() const { return m_bItemLoaded; }
void SetItemLoaded() { m_bItemLoaded = true; }
@ -1060,14 +1059,14 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
LPITEM GetWear(BYTE bCell) const;
// MYSHOP_PRICE_LIST
void UseSilkBotary(void); /// 비단 보따리 아이템의 사용
void UseSilkBotary(void); /// 비단 보따리 아이템의 사용
/// DB 캐시로 부터 받아온 가격정보 리스트를 유저에게 전송하고 보따리 아이템 사용을 처리한다.
/// DB 캐시로 부터 받아온 가격정보 리스트를 유저에게 전송하고 보따리 아이템 사용을 처리한다.
/**
* @param [in] p
* @param [in] p
*
* UseSilkBotary DB
* .
* UseSilkBotary DB
* .
*/
void UseSilkBotaryReal(const TPacketMyshopPricelistHeader* p);
// END_OF_MYSHOP_PRICE_LIST
@ -1113,10 +1112,10 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool EquipItem(LPITEM item, int iCandidateCell = -1);
bool UnequipItem(LPITEM item);
// 현재 item을 착용할 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
// 현재 item을 착용할 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
bool CanEquipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS);
// 착용중인 item을 벗을 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
// 착용중인 item을 벗을 수 있는 지 확인하고, 불가능 하다면 캐릭터에게 이유를 알려주는 함수
bool CanUnequipNow(const LPITEM item, const TItemPos& srcCell = NPOS, const TItemPos& destCell = NPOS);
bool SwapItem(BYTE bCell, BYTE bDestCell);
@ -1148,14 +1147,14 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
protected:
/// 한 아이템에 대한 가격정보를 전송한다.
/// 한 아이템에 대한 가격정보를 전송한다.
/**
* @param [in] dwItemVnum vnum
* @param [in] dwItemPrice
* @param [in] dwItemVnum vnum
* @param [in] dwItemPrice
*/
void SendMyShopPriceListCmd(DWORD dwItemVnum, DWORD dwItemPrice);
bool m_bNoOpenedShop; ///< 이번 접속 후 개인상점을 연 적이 있는지의 여부(열었던 적이 없다면 true)
bool m_bNoOpenedShop; ///< 이번 접속 후 개인상점을 연 적이 있는지의 여부(열었던 적이 없다면 true)
bool m_bItemLoaded;
int m_iRefineAdditionalCell;
@ -1169,7 +1168,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void SetGold(INT gold) { m_points.gold = gold; }
bool DropGold(INT gold);
INT GetAllowedGold() const;
void GiveGold(INT iAmount); // 파티가 있으면 파티 분배, 로그 등의 처리
void GiveGold(INT iAmount); // 파티가 있으면 파티 분배, 로그 등의 처리
// End of Money
////////////////////////////////////////////////////////////////////////////////////////
@ -1235,9 +1234,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool CanFight() const;
bool CanBeginFight() const;
void BeginFight(LPCHARACTER pkVictim); // pkVictimr과 싸우기 시작한다. (강제적임, 시작할 수 있나 체크하려면 CanBeginFight을 사용)
void BeginFight(LPCHARACTER pkVictim); // pkVictimr과 싸우기 시작한다. (강제적임, 시작할 수 있나 체크하려면 CanBeginFight을 사용)
bool CounterAttack(LPCHARACTER pkChr); // 반격하기 (몬스터만 사용)
bool CounterAttack(LPCHARACTER pkChr); // 반격하기 (몬스터만 사용)
bool IsStun() const;
void Stun();
@ -1267,7 +1266,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void UpdateAlignment(int iAmount);
int GetAlignment() const;
//선악치 얻기
//선악치 얻기
int GetRealAlignment() const;
void ShowAlignment(bool bShow);
@ -1316,7 +1315,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
DWORD m_dwFlyTargetID;
std::vector<DWORD> m_vec_dwFlyTargets;
TDamageMap m_map_kDamage; // 어떤 캐릭터가 나에게 얼마만큼의 데미지를 주었는가?
TDamageMap m_map_kDamage; // 어떤 캐릭터가 나에게 얼마만큼의 데미지를 주었는가?
// AttackLog m_kAttackLog;
DWORD m_dwKillerPID;
@ -1339,8 +1338,8 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
BYTE GetDropMetinStonePct() const { return m_bDropMetinStonePct; }
protected:
LPCHARACTER m_pkChrStone; // 나를 스폰한 돌
CHARACTER_SET m_set_pkChrSpawnedBy; // 내가 스폰한 놈들
LPCHARACTER m_pkChrStone; // 나를 스폰한 돌
CHARACTER_SET m_set_pkChrSpawnedBy; // 내가 스폰한 놈들
DWORD m_dwDropMetinStone;
BYTE m_bDropMetinStonePct;
// End of Stone
@ -1398,7 +1397,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
private:
bool m_bDisableCooltime;
DWORD m_dwLastSkillTime; ///< 마지막으로 skill 을 쓴 시간(millisecond).
DWORD m_dwLastSkillTime; ///< 마지막으로 skill 을 쓴 시간(millisecond).
// End of Skill
// MOB_SKILL
@ -1455,10 +1454,10 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
// AI related
public:
void AssignTriggers(const TMobTable * table);
LPCHARACTER GetVictim() const; // 공격할 대상 리턴
LPCHARACTER GetVictim() const; // 공격할 대상 리턴
void SetVictim(LPCHARACTER pkVictim);
LPCHARACTER GetNearestVictim(LPCHARACTER pkChr);
LPCHARACTER GetProtege() const; // 보호해야 할 대상 리턴
LPCHARACTER GetProtege() const; // 보호해야 할 대상 리턴
bool Follow(LPCHARACTER pkChr, float fMinimumDistance = 150.0f);
bool Return();
@ -1482,8 +1481,8 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
////////////////////////////////////////////////////////////////////////////////////////
// Target
protected:
LPCHARACTER m_pkChrTarget; // 내 타겟
CHARACTER_SET m_set_pkChrTargetedBy; // 나를 타겟으로 가지고 있는 사람들
LPCHARACTER m_pkChrTarget; // 내 타겟
CHARACTER_SET m_set_pkChrTargetedBy; // 나를 타겟으로 가지고 있는 사람들
public:
void SetTarget(LPCHARACTER pkChrTarget);
@ -1504,19 +1503,19 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void ChangeSafeboxSize(BYTE bSize);
void CloseSafebox();
/// 창고 열기 요청
/// 창고 열기 요청
/**
* @param [in] pszPassword 1 6
* @param [in] pszPassword 1 6
*
* DB .
* , 10 .
* DB .
* , 10 .
*/
void ReqSafeboxLoad(const char* pszPassword);
/// 창고 열기 요청의 취소
/// 창고 열기 요청의 취소
/**
* ReqSafeboxLoad CloseSafebox .
* DB .
* ReqSafeboxLoad CloseSafebox .
* DB .
*/
void CancelSafeboxLoad( void ) { m_bOpeningSafebox = false; }
@ -1534,7 +1533,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
CSafebox * m_pkSafebox;
int m_iSafeboxSize;
int m_iSafeboxLoadTime;
bool m_bOpeningSafebox; ///< 창고가 열기 요청 중이거나 열려있는가 여부, true 일 경우 열기요청이거나 열려있음.
bool m_bOpeningSafebox; ///< 창고가 열기 요청 중이거나 열려있는가 여부, true 일 경우 열기요청이거나 열려있음.
CSafebox * m_pkMall;
int m_iMallLoadTime;
@ -1568,7 +1567,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void HorseSummon(bool bSummon, bool bFromFar = false, DWORD dwVnum = 0, const char* name = 0);
LPCHARACTER GetHorse() const { return m_chHorse; } // 현재 소환중인 말
LPCHARACTER GetHorse() const { return m_chHorse; } // 현재 소환중인 말
LPCHARACTER GetRider() const; // rider on horse
void SetRider(LPCHARACTER ch);
@ -1630,7 +1629,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
////////////////////////////////////////////////////////////////////////////////////////
// Resists & Proofs
public:
bool CannotMoveByAffect() const; // 특정 효과에 의해 움직일 수 없는 상태인가?
bool CannotMoveByAffect() const; // 특정 효과에 의해 움직일 수 없는 상태인가?
bool IsImmune(DWORD dwImmuneFlag);
void SetImmuneFlag(DWORD dw) { m_pointsInstant.dwImmuneFlag = dw; }
@ -1670,7 +1669,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void UpdateStateMachine(DWORD dwPulse);
void SetNextStatePulse(int iPulseNext);
// 캐릭터 인스턴스 업데이트 함수. 기존엔 이상한 상속구조로 CFSM::Update 함수를 호출하거나 UpdateStateMachine 함수를 사용했는데, 별개의 업데이트 함수 추가함.
// 캐릭터 인스턴스 업데이트 함수. 기존엔 이상한 상속구조로 CFSM::Update 함수를 호출하거나 UpdateStateMachine 함수를 사용했는데, 별개의 업데이트 함수 추가함.
void UpdateCharacter(DWORD dwPulse);
protected:
@ -1740,9 +1739,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
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 이름
static const DWORD msc_dwDefaultChangeItemAttrCycle; ///< 디폴트 아이템 속성변경 가능 주기
static const char msc_szLastChangeItemAttrFlag[]; ///< 최근 아이템 속성을 변경한 시간의 Quest Flag 이름
static const char msc_szChangeItemAttrCycleFlag[]; ///< 아이템 속성병경 가능 주기의 Quest Flag 이름
// END_OF_CHANGE_ITEM_ATTRIBUTES
// NEW_HAIR_STYLE_ADD
@ -1814,7 +1813,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
int GetMyShopTime() const { return m_iMyShopTime; }
void SetMyShopTime() { m_iMyShopTime = thecore_pulse(); }
// Hack 방지를 위한 체크.
// Hack 방지를 위한 체크.
bool IsHack(bool bSendMsg = true, bool bCheckShopOwner = true, int limittime = g_nPortalLimitTime);
// MONARCH
@ -1864,9 +1863,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool IsSiegeNPC() const;
private:
//중국 전용
//18세 미만 전용
//3시간 : 50 % 5 시간 0%
//중국 전용
//18세 미만 전용
//3시간 : 50 % 5 시간 0%
e_overtime m_eOverTime;
public:
@ -1942,7 +1941,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
typedef std::map <BYTE, CBuffOnAttributes*> TMapBuffOnAttrs;
TMapBuffOnAttrs m_map_buff_on_attrs;
// 무적 : 원활한 테스트를 위하여.
// 무적 : 원활한 테스트를 위하여.
public:
void SetArmada() { cannot_dead = true; }
void ResetArmada() { cannot_dead = false; }
@ -1957,7 +1956,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool IsPet() { return m_bIsPet; }
#endif
//최종 데미지 보정.
//최종 데미지 보정.
private:
float m_fAttMul;
float m_fDamMul;
@ -1970,7 +1969,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
private:
bool IsValidItemPosition(TItemPos Pos) const;
//독일 선물 기능 패킷 임시 저장
//독일 선물 기능 패킷 임시 저장
private:
unsigned int itemAward_vnum = 0;
char itemAward_cmd[20] = "";
@ -1984,10 +1983,10 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
//void SetItemAward_flag(bool flag) { itemAward_flag = flag; }
public:
//용혼석
//용혼석
// 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
// affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
// 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
// affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
void DragonSoul_Initialize();
bool DragonSoul_IsQualified() const;
@ -1998,17 +1997,17 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool DragonSoul_ActivateDeck(int deck_idx);
void DragonSoul_DeactivateAll();
// 반드시 ClearItem 전에 불러야 한다.
// 왜냐하면....
// 용혼석 하나 하나를 deactivate할 때마다 덱에 active인 용혼석이 있는지 확인하고,
// active인 용혼석이 하나도 없다면, 캐릭터의 용혼석 affect와, 활성 상태를 제거한다.
// 반드시 ClearItem 전에 불러야 한다.
// 왜냐하면....
// 용혼석 하나 하나를 deactivate할 때마다 덱에 active인 용혼석이 있는지 확인하고,
// active인 용혼석이 하나도 없다면, 캐릭터의 용혼석 affect와, 활성 상태를 제거한다.
//
// 하지만 ClearItem 시, 캐릭터가 착용하고 있는 모든 아이템을 unequip하는 바람에,
// 용혼석 Affect가 제거되고, 결국 로그인 시, 용혼석이 활성화되지 않는다.
// (Unequip할 때에는 로그아웃 상태인지, 아닌지 알 수 없다.)
// 용혼석만 deactivate시키고 캐릭터의 용혼석 덱 활성 상태는 건드리지 않는다.
// 하지만 ClearItem 시, 캐릭터가 착용하고 있는 모든 아이템을 unequip하는 바람에,
// 용혼석 Affect가 제거되고, 결국 로그인 시, 용혼석이 활성화되지 않는다.
// (Unequip할 때에는 로그아웃 상태인지, 아닌지 알 수 없다.)
// 용혼석만 deactivate시키고 캐릭터의 용혼석 덱 활성 상태는 건드리지 않는다.
void DragonSoul_CleanUp();
// 용혼석 강화창
// 용혼석 강화창
public:
bool DragonSoul_RefineWindow_Open(LPENTITY pEntity);
bool DragonSoul_RefineWindow_Close();
@ -2016,8 +2015,8 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
bool DragonSoul_RefineWindow_CanRefine();
private:
// SyncPosition을 악용하여 타유저를 이상한 곳으로 보내는 핵 방어하기 위하여,
// SyncPosition이 일어날 때를 기록.
// SyncPosition을 악용하여 타유저를 이상한 곳으로 보내는 핵 방어하기 위하여,
// SyncPosition이 일어날 때를 기록.
timeval m_tvLastSyncTime;
int m_iSyncHackCount;
public:

View File

@ -84,13 +84,13 @@ EVENTFUNC(affect_event)
if (!ch->UpdateAffect())
return 0;
else
return passes_per_sec; // 1초
return passes_per_sec; // 1초
}
bool CHARACTER::UpdateAffect()
{
// affect_event 에서 처리할 일은 아니지만, 1초짜리 이벤트에서 처리하는 것이
// 이것 뿐이라 여기서 물약 처리를 한다.
// affect_event 에서 처리할 일은 아니지만, 1초짜리 이벤트에서 처리하는 것이
// 이것 뿐이라 여기서 물약 처리를 한다.
if (GetPoint(POINT_HP_RECOVERY) > 0)
{
if (GetMaxHP() <= GetHP())
@ -146,7 +146,7 @@ bool CHARACTER::UpdateAffect()
AutoRecoveryItemProcess( AFFECT_AUTO_HP_RECOVERY );
AutoRecoveryItemProcess( AFFECT_AUTO_SP_RECOVERY );
// 스테미나 회복
// 스테미나 회복
if (GetMaxStamina() > GetStamina())
{
int iSec = (get_dword_time() - GetStopTime()) / 3000;
@ -155,7 +155,7 @@ bool CHARACTER::UpdateAffect()
}
// ProcessAffect는 affect가 없으면 true를 리턴한다.
// ProcessAffect는 affect가 없으면 true를 리턴한다.
if (ProcessAffect())
if (GetPoint(POINT_HP_RECOVERY) == 0 && GetPoint(POINT_SP_RECOVERY) == 0 && GetStamina() == GetMaxStamina())
{
@ -226,7 +226,7 @@ int CHARACTER::ProcessAffect()
CAffect *pkAff = NULL;
//
// 프리미엄 처리
// 프리미엄 처리
//
for (int i = 0; i <= PREMIUM_MAX_NUM; ++i)
{
@ -300,8 +300,8 @@ int CHARACTER::ProcessAffect()
}
// AFFECT_DURATION_BUG_FIX
// 무한 효과 아이템도 시간을 줄인다.
// 시간을 매우 크게 잡기 때문에 상관 없을 것이라 생각됨.
// 무한 효과 아이템도 시간을 줄인다.
// 시간을 매우 크게 잡기 때문에 상관 없을 것이라 생각됨.
if ( --pkAff->lDuration <= 0 )
{
bEnd = true;
@ -470,7 +470,7 @@ void CHARACTER::LoadAffect(DWORD dwCount, TPacketAffectElement * pElements)
for (DWORD i = 0; i < dwCount; ++i, ++pElements)
{
// 무영진은 로드하지않는다.
// 무영진은 로드하지않는다.
if (pElements->dwType == SKILL_MUYEONG)
continue;
@ -524,7 +524,7 @@ void CHARACTER::LoadAffect(DWORD dwCount, TPacketAffectElement * pElements)
m_bIsLoadedAffect = true;
// 용혼석 셋팅 로드 및 초기화
// 용혼석 셋팅 로드 및 초기화
DragonSoul_Initialize();
}
@ -533,7 +533,7 @@ bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, int lApplyValue, DWORD dw
// CHAT_BLOCK
if (dwType == AFFECT_BLOCK_CHAT && lDuration > 1)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("운영자 제제로 채팅이 금지 되었습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your chat has been blocked by a GM."));
}
// END_OF_CHAT_BLOCK
@ -562,10 +562,10 @@ bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, int lApplyValue, DWORD dw
}
}
// 이미 있는 효과를 덮어 쓰는 처리
// 이미 있는 효과를 덮어 쓰는 처리
if (pkAff && bOverride)
{
ComputeAffect(pkAff, false); // 일단 효과를 삭제하고
ComputeAffect(pkAff, false); // 일단 효과를 삭제하고
if (GetDesc())
SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
@ -573,9 +573,9 @@ bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, int lApplyValue, DWORD dw
else
{
//
// 새 에펙를 추가
// 새 에펙를 추가
//
// NOTE: 따라서 같은 type 으로도 여러 에펙트를 붙을 수 있다.
// NOTE: 따라서 같은 type 으로도 여러 에펙트를 붙을 수 있다.
//
pkAff = CAffect::Acquire();
m_list_pkAffect.push_back(pkAff);
@ -677,15 +677,15 @@ bool CHARACTER::RemoveAffect(CAffect * pkAff)
ComputeAffect(pkAff, false);
// 백기 버그 수정.
// 백기 버그는 버프 스킬 시전->둔갑->백기 사용(AFFECT_REVIVE_INVISIBLE) 후 바로 공격 할 경우에 발생한다.
// 원인은 둔갑을 시전하는 시점에, 버프 스킬 효과를 무시하고 둔갑 효과만 적용되게 되어있는데,
// 백기 사용 후 바로 공격하면 RemoveAffect가 불리게 되고, ComputePoints하면서 둔갑 효과 + 버프 스킬 효과가 된다.
// ComputePoints에서 둔갑 상태면 버프 스킬 효과 안 먹히도록 하면 되긴 하는데,
// ComputePoints는 광범위하게 사용되고 있어서 큰 변화를 주는 것이 꺼려진다.(어떤 side effect가 발생할지 알기 힘들다.)
// 따라서 AFFECT_REVIVE_INVISIBLE가 RemoveAffect로 삭제되는 경우만 수정한다.
// 시간이 다 되어 백기 효과가 풀리는 경우는 버그가 발생하지 않으므로 그와 똑같이 함.
// (ProcessAffect를 보면 시간이 다 되어서 Affect가 삭제되는 경우, ComputePoints를 부르지 않는다.)
// 백기 버그 수정.
// 백기 버그는 버프 스킬 시전->둔갑->백기 사용(AFFECT_REVIVE_INVISIBLE) 후 바로 공격 할 경우에 발생한다.
// 원인은 둔갑을 시전하는 시점에, 버프 스킬 효과를 무시하고 둔갑 효과만 적용되게 되어있는데,
// 백기 사용 후 바로 공격하면 RemoveAffect가 불리게 되고, ComputePoints하면서 둔갑 효과 + 버프 스킬 효과가 된다.
// ComputePoints에서 둔갑 상태면 버프 스킬 효과 안 먹히도록 하면 되긴 하는데,
// ComputePoints는 광범위하게 사용되고 있어서 큰 변화를 주는 것이 꺼려진다.(어떤 side effect가 발생할지 알기 힘들다.)
// 따라서 AFFECT_REVIVE_INVISIBLE가 RemoveAffect로 삭제되는 경우만 수정한다.
// 시간이 다 되어 백기 효과가 풀리는 경우는 버그가 발생하지 않으므로 그와 똑같이 함.
// (ProcessAffect를 보면 시간이 다 되어서 Affect가 삭제되는 경우, ComputePoints를 부르지 않는다.)
if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType)
{
ComputePoints();
@ -713,7 +713,7 @@ bool CHARACTER::RemoveAffect(DWORD dwType)
// CHAT_BLOCK
if (dwType == AFFECT_BLOCK_CHAT)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채팅 금지가 풀렸습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your chat block has been lifted."));
}
// END_OF_CHAT_BLOCK
@ -795,41 +795,41 @@ bool CHARACTER::IsGoodAffect(BYTE bAffectType) const
void CHARACTER::RemoveBadAffect()
{
SPDLOG_DEBUG("RemoveBadAffect {}", GetName());
// 독
// 독
RemovePoison();
RemoveFire();
// 스턴 : Value%로 상대방을 5초간 머리 위에 별이 돌아간다. (때리면 1/2 확률로 풀림) AFF_STUN
// 스턴 : Value%로 상대방을 5초간 머리 위에 별이 돌아간다. (때리면 1/2 확률로 풀림) AFF_STUN
RemoveAffect(AFFECT_STUN);
// 슬로우 : Value%로 상대방의 공속/이속 모두 느려진다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_SLOW
// 슬로우 : Value%로 상대방의 공속/이속 모두 느려진다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_SLOW
RemoveAffect(AFFECT_SLOW);
// 투속마령
// 투속마령
RemoveAffect(SKILL_TUSOK);
// 저주
// 저주
//RemoveAffect(SKILL_CURSE);
// 파법술
// 파법술
//RemoveAffect(SKILL_PABUP);
// 기절 : Value%로 상대방을 기절시킨다. 2초 AFF_FAINT
// 기절 : Value%로 상대방을 기절시킨다. 2초 AFF_FAINT
//RemoveAffect(AFFECT_FAINT);
// 다리묶임 : Value%로 상대방의 이동속도를 떨어트린다. 5초간 -40 AFF_WEB
// 다리묶임 : Value%로 상대방의 이동속도를 떨어트린다. 5초간 -40 AFF_WEB
//RemoveAffect(AFFECT_WEB);
// 잠들기 : Value%로 상대방을 10초간 잠재운다. (때리면 풀림) AFF_SLEEP
// 잠들기 : Value%로 상대방을 10초간 잠재운다. (때리면 풀림) AFF_SLEEP
//RemoveAffect(AFFECT_SLEEP);
// 저주 : Value%로 상대방의 공등/방등 모두 떨어트린다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_CURSE
// 저주 : Value%로 상대방의 공등/방등 모두 떨어트린다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_CURSE
//RemoveAffect(AFFECT_CURSE);
// 마비 : Value%로 상대방을 4초간 마비시킨다. AFF_PARA
// 마비 : Value%로 상대방을 4초간 마비시킨다. AFF_PARA
//RemoveAffect(AFFECT_PARALYZE);
// 부동박부 : 무당 기술
// 부동박부 : 무당 기술
//RemoveAffect(SKILL_BUDONG);
}

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,12 @@
/*
Return Value
0 : or
1 :
2 :
3 :
0 : or
1 :
2 :
3 :
999 :
999 :
*/
int CHARACTER::ChangeEmpire(BYTE empire)
{
@ -27,7 +27,7 @@ int CHARACTER::ChangeEmpire(BYTE empire)
memset(dwPID, 0, sizeof(dwPID));
{
// 1. 내 계정의 모든 pid를 얻어 온다
// 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());
@ -51,8 +51,8 @@ int CHARACTER::ChangeEmpire(BYTE empire)
const int loop = 4;
{
// 2. 각 캐릭터의 길드 정보를 얻어온다.
// 한 캐릭터라도 길드에 가입 되어 있다면, 제국 이동을 할 수 없다.
// 2. 각 캐릭터의 길드 정보를 얻어온다.
// 한 캐릭터라도 길드에 가입 되어 있다면, 제국 이동을 할 수 없다.
DWORD dwGuildID[4];
CGuild * pGuild[4];
SQLMsg * pMsg = NULL;
@ -91,8 +91,8 @@ int CHARACTER::ChangeEmpire(BYTE empire)
}
{
// 3. 각 캐릭터의 결혼 정보를 얻어온다.
// 한 캐릭터라도 결혼 상태라면 제국 이동을 할 수 없다.
// 3. 각 캐릭터의 결혼 정보를 얻어온다.
// 한 캐릭터라도 결혼 상태라면 제국 이동을 할 수 없다.
for (int i = 0; i < loop; ++i)
{
if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true)
@ -101,7 +101,7 @@ int CHARACTER::ChangeEmpire(BYTE empire)
}
{
// 4. db의 제국 정보를 업데이트 한다.
// 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());
@ -109,7 +109,7 @@ int CHARACTER::ChangeEmpire(BYTE empire)
if (msg->Get()->uiAffectedRows > 0)
{
// 5. 제국 변경 이력을 추가한다.
// 5. 제국 변경 이력을 추가한다.
SetChangeEmpireCount();
return 999;

View File

@ -5,14 +5,14 @@
#include "DragonSoul.h"
#include "log.h"
// 용혼석 초기화
// 용혼석 on/off는 Affect로 저장되기 때문에,
// 용혼석 Affect가 있다면 덱에 있는 용혼석을 activate해야한다.
// 또한 용혼석 사용 자격은 QuestFlag로 저장해 놓았기 때문에,
// 퀘스트 Flag에서 용혼석 사용 자격을 읽어온다.
// 용혼석 초기화
// 용혼석 on/off는 Affect로 저장되기 때문에,
// 용혼석 Affect가 있다면 덱에 있는 용혼석을 activate해야한다.
// 또한 용혼석 사용 자격은 QuestFlag로 저장해 놓았기 때문에,
// 퀘스트 Flag에서 용혼석 사용 자격을 읽어온다.
// 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
// affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
// 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
// affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
void CHARACTER::DragonSoul_Initialize()
{
for (int i = INVENTORY_MAX_NUM + WEAR_MAX_NUM; i < DRAGON_SOUL_EQUIP_SLOT_END; i++)
@ -55,7 +55,7 @@ void CHARACTER::DragonSoul_GiveQualification()
}
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
//// 자격있다면 POINT_DRAGON_SOUL_IS_QUALIFIED는 무조건 1
//PointChange(POINT_DRAGON_SOUL_IS_QUALIFIED, 1 - GetPoint(POINT_DRAGON_SOUL_IS_QUALIFIED));
}
@ -73,7 +73,7 @@ bool CHARACTER::DragonSoul_ActivateDeck(int deck_idx)
if (!DragonSoul_IsQualified())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 상자가 활성화되지 않았습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Dragon Soul Alchemy is not active"));
return false;
}

View File

@ -17,22 +17,22 @@ bool CHARACTER::StartRiding()
{
if (IsDead() == true)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("쓰러진 상태에서는 말에 탈 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride a Horse while downed."));
return false;
}
if (IsPolymorphed())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신 상태에서는 말에 탈 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride a Horse while you are transformed."));
return false;
}
// 턱시도 입은 상태의 말 타기 금지
// 턱시도 입은 상태의 말 타기 금지
LPITEM armor = GetWear(WEAR_BODY);
if (armor && (armor->GetVnum() >= 11901 && armor->GetVnum() <= 11904))
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("예복을 입은 상태에서 말을 탈 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride while you are wearing a Wedding Dress or a Tuxedo."));
return false;
}
@ -48,16 +48,16 @@ bool CHARACTER::StartRiding()
if (false == CHorseRider::StartRiding())
{
if (GetHorseLevel() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 소유하고 있지 않습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have a Horse."));
else if (GetHorseHealth() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말이 죽어있는 상태 입니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your Horse is dead."));
else if (GetHorseStamina() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말의 스테미너가 부족하여 말을 탈 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your Horse's endurance is too low."));
return false;
}
// 소환한 말 없애고
// 소환한 말 없애고
HorseSummon(false);
MountVnum(dwMountVnum);
@ -78,7 +78,7 @@ bool CHARACTER::StopRiding()
DWORD dwOldVnum = GetMountVnum();
MountVnum(0);
// [NOTE] 말에서 내릴 땐 자기가 탔던걸 소환하도록 수정
// [NOTE] 말에서 내릴 땐 자기가 탔던걸 소환하도록 수정
HorseSummon(true, false, dwOldVnum);
}
else
@ -139,14 +139,14 @@ void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const cha
{
if ( bSummon )
{
//NOTE : summon했는데 이미 horse가 있으면 아무것도 안한다.
//NOTE : summon했는데 이미 horse가 있으면 아무것도 안한다.
if( m_chHorse != NULL )
return;
if (GetHorseLevel() <= 0)
return;
// 무언가를 타고 있다면 실패
// 무언가를 타고 있다면 실패
if (IsRiding())
return;
@ -177,16 +177,16 @@ void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const cha
if (!m_chHorse)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말 소환에 실패하였습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Calling the Horse has failed."));
return;
}
if (GetHorseHealth() <= 0)
{
// 죽은거처럼 있게 하는 처리
// 죽은거처럼 있게 하는 처리
m_chHorse->SetPosition(POS_DEAD);
// 일정시간있다 사라지게 하자.
// 일정시간있다 사라지게 하자.
char_event_info* info = AllocEventInfo<char_event_info>();
info->ch = this;
m_chHorse->m_pkDeadEvent = event_create(horse_dead_event, info, PASSES_PER_SEC(60));
@ -203,7 +203,7 @@ void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const cha
else
{
m_chHorse->m_stName = GetName();
m_chHorse->m_stName += LC_TEXT("님의 말");
m_chHorse->m_stName += LC_TEXT("'s Horse");
}
if (!m_chHorse->Show(GetMapIndex(), x, y, GetZ()))
@ -239,7 +239,7 @@ void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const cha
}
else
{
// 멀어지면서 사라지는 처리 하기
// 멀어지면서 사라지는 처리 하기
chHorse->SetNowWalking(false);
float fx, fy;
chHorse->SetRotation(GetDegreeFromPositionXY(chHorse->GetX(), chHorse->GetY(), GetX(), GetY())+180);
@ -310,7 +310,7 @@ void CHARACTER::SendHorseInfo()
3: 70% < ~ <= 100%
2: 30% < ~ <= 70%
1: 0% < ~ <= 30%
0:
0:
STM
@ -343,8 +343,8 @@ STM
{
ChatPacket(CHAT_TYPE_COMMAND, "horse_state %d %d %d", GetHorseLevel(), iHealthGrade, iStaminaGrade);
// FIX : 클라이언트에 "말 상태 버프" 아이콘을 표시하지 않을 목적으로 함수 초입에 return함으로써 아래 코드를 무시한다면
// 말을 무한대로 소환하는 무시무시한 버그가 생김.. 정확한 원인은 파악 안해봐서 모름.
// FIX : 클라이언트에 "말 상태 버프" 아이콘을 표시하지 않을 목적으로 함수 초입에 return함으로써 아래 코드를 무시한다면
// 말을 무한대로 소환하는 무시무시한 버그가 생김.. 정확한 원인은 파악 안해봐서 모름.
m_bSendHorseLevel = GetHorseLevel();
m_bSendHorseHealthGrade = iHealthGrade;
m_bSendHorseStaminaGrade = iStaminaGrade;
@ -366,7 +366,7 @@ bool CHARACTER::CanUseHorseSkill()
if (GetMountVnum() >= 20209 && GetMountVnum() <= 20212)
return true;
//라마단 흑마
//라마단 흑마
if (CMobVnumHelper::IsRamadanBlackHorse(GetMountVnum()))
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -114,7 +114,7 @@ void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch, const char* file, size_
return; // prevent duplicated destrunction
}
// 던전에 소속된 몬스터는 던전에서도 삭제하도록.
// 던전에 소속된 몬스터는 던전에서도 삭제하도록.
if (ch->IsNPC() && !ch->IsPet() && ch->GetRider() == NULL)
{
if (ch->GetDungeon())
@ -230,7 +230,7 @@ LPCHARACTER CHARACTER_MANAGER::FindPC(const char * name)
LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, int lMapIndex)
{
// 왜구 스폰할지말지를 결정할 수 있게함
// 왜구 스폰할지말지를 결정할 수 있게함
{
if (dwVnum == 5001 && !quest::CQuestManager::instance().GetEventFlag("japan_regen"))
{
@ -239,7 +239,7 @@ LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, int lMapInde
}
}
// 해태를 스폰할지 말지를 결정할 수 있게 함
// 해태를 스폰할지 말지를 결정할 수 있게 함
{
if (dwVnum == 5002 && !quest::CQuestManager::instance().GetEventFlag("newyear_mob"))
{
@ -248,7 +248,7 @@ LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, int lMapInde
}
}
// 광복절 이벤트
// 광복절 이벤트
{
if (dwVnum == 5004 && !quest::CQuestManager::instance().GetEventFlag("independence_day"))
{
@ -448,7 +448,7 @@ LPCHARACTER CHARACTER_MANAGER::SpawnMobRange(DWORD dwVnum, int lMapIndex, int sx
if (!pkMob)
return NULL;
if (pkMob->m_table.bType == CHAR_TYPE_STONE) // 돌은 무조건 SPAWN 모션이 있다.
if (pkMob->m_table.bType == CHAR_TYPE_STONE) // 돌은 무조건 SPAWN 모션이 있다.
bSpawnMotion = true;
int i = 16;
@ -512,7 +512,7 @@ bool CHARACTER_MANAGER::SpawnMoveGroup(DWORD dwVnum, int lMapIndex, int sx, int
if (!tch)
{
if (i == 0) // 못만든 몬스터가 대장일 경우에는 그냥 실패
if (i == 0) // 못만든 몬스터가 대장일 경우에는 그냥 실패
return false;
continue;
@ -596,7 +596,7 @@ LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, int lMapIndex, int sx, i
if (!tch)
{
if (i == 0) // 못만든 몬스터가 대장일 경우에는 그냥 실패
if (i == 0) // 못만든 몬스터가 대장일 경우에는 그냥 실패
return NULL;
continue;
@ -652,11 +652,11 @@ void CHARACTER_MANAGER::Update(int iPulse)
BeginPendingDestroy();
// PC 캐릭터 업데이트
// PC 캐릭터 업데이트
{
if (!m_map_pkPCChr.empty())
{
// 컨테이너 복사
// 컨테이너 복사
CHARACTER_VECTOR v;
v.reserve(m_map_pkPCChr.size());
#ifdef __GNUC__
@ -680,7 +680,7 @@ void CHARACTER_MANAGER::Update(int iPulse)
// for_each_pc(bind2nd(mem_fun(&CHARACTER::UpdateCharacter), iPulse));
}
// 몬스터 업데이트
// 몬스터 업데이트
{
if (!m_set_pkChrState.empty())
{
@ -695,7 +695,7 @@ void CHARACTER_MANAGER::Update(int iPulse)
}
}
// 산타 따로 업데이트
// 산타 따로 업데이트
{
CharacterVectorInteractor i;
@ -706,7 +706,7 @@ void CHARACTER_MANAGER::Update(int iPulse)
}
}
// 1시간에 한번씩 몹 사냥 개수 기록
// 1시간에 한번씩 몹 사냥 개수 기록
if (0 == (iPulse % PASSES_PER_SEC(3600)))
{
for (itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.begin(); it != m_map_dwMobKillCount.end(); ++it)
@ -715,11 +715,11 @@ void CHARACTER_MANAGER::Update(int iPulse)
m_map_dwMobKillCount.clear();
}
// 테스트 서버에서는 60초마다 캐릭터 개수를 센다
// 테스트 서버에서는 60초마다 캐릭터 개수를 센다
if (test_server && 0 == (iPulse % PASSES_PER_SEC(60)))
SPDLOG_TRACE("CHARACTER COUNT vid {} pid {}", m_map_pkChrByVID.size(), m_map_pkChrByPID.size());
// 지연된 DestroyCharacter 하기
// 지연된 DestroyCharacter 하기
FlushPendingDestroy();
}
@ -835,7 +835,7 @@ void CHARACTER_MANAGER::RegisterRaceNumMap(LPCHARACTER ch)
{
DWORD dwVnum = ch->GetRaceNum();
if (m_set_dwRegisteredRaceNum.find(dwVnum) != m_set_dwRegisteredRaceNum.end()) // 등록된 번호 이면
if (m_set_dwRegisteredRaceNum.find(dwVnum) != m_set_dwRegisteredRaceNum.end()) // 등록된 번호 이면
{
SPDLOG_TRACE("RegisterRaceNumMap {} {}", ch->GetName(), dwVnum);
m_map_pkChrByRaceNum[dwVnum].insert(ch);
@ -859,7 +859,7 @@ bool CHARACTER_MANAGER::GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorI
if (it == m_map_pkChrByRaceNum.end())
return false;
// 컨테이너 복사
// 컨테이너 복사
i = it->second;
return true;
}
@ -1056,8 +1056,8 @@ void CHARACTER_MANAGER::SendScriptToMap(int lMapIndex, const std::string & s)
bool CHARACTER_MANAGER::BeginPendingDestroy()
{
// Begin 이 후에 Begin을 또 하는 경우에 Flush 하지 않는 기능 지원을 위해
// 이미 시작되어있으면 false 리턴 처리
// Begin 이 후에 Begin을 또 하는 경우에 Flush 하지 않는 기능 지원을 위해
// 이미 시작되어있으면 false 리턴 처리
if (m_bUsePendingDestroy)
return false;
@ -1069,7 +1069,7 @@ void CHARACTER_MANAGER::FlushPendingDestroy()
{
using namespace std;
m_bUsePendingDestroy = false; // 플래그를 먼저 설정해야 실제 Destroy 처리가 됨
m_bUsePendingDestroy = false; // 플래그를 먼저 설정해야 실제 Destroy 처리가 됨
if (!m_set_pkChrPendingDestroy.empty())
{

View File

@ -24,7 +24,7 @@ class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
void Destroy();
void GracefulShutdown(); // 정상적 셧다운할 때 사용. PC를 모두 저장시키고 Destroy 한다.
void GracefulShutdown(); // 정상적 셧다운할 때 사용. PC를 모두 저장시키고 Destroy 한다.
DWORD AllocVID();
@ -56,11 +56,11 @@ class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
bool AddToStateList(LPCHARACTER ch);
void RemoveFromStateList(LPCHARACTER ch);
// DelayedSave: 어떠한 루틴 내에서 저장을 해야 할 짓을 많이 하면 저장
// 쿼리가 너무 많아지므로 "저장을 한다" 라고 표시만 해두고 잠깐
// (예: 1 frame) 후에 저장시킨다.
// DelayedSave: 어떠한 루틴 내에서 저장을 해야 할 짓을 많이 하면 저장
// 쿼리가 너무 많아지므로 "저장을 한다" 라고 표시만 해두고 잠깐
// (예: 1 frame) 후에 저장시킨다.
void DelayedSave(LPCHARACTER ch);
bool FlushDelayedSave(LPCHARACTER ch); // Delayed 리스트에 있다면 지우고 저장한다. 끊김 처리시 사용 됨.
bool FlushDelayedSave(LPCHARACTER ch); // Delayed 리스트에 있다면 지우고 저장한다. 끊김 처리시 사용 됨.
void ProcessDelayedSave();
template<class Func> Func for_each_pc(Func f);
@ -124,7 +124,7 @@ class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
NAME_MAP m_map_pkPCChr;
char dummy1[1024]; // memory barrier
CHARACTER_SET m_set_pkChrState; // FSM이 돌아가고 있는 놈들
CHARACTER_SET m_set_pkChrState; // FSM이 돌아가고 있는 놈들
CHARACTER_SET m_set_pkChrForDelayedSave;
CHARACTER_SET m_set_pkChrMonsterLog;

View File

@ -9,7 +9,7 @@
/////////////////////////////////////////////////////////////////////////////
// QUICKSLOT HANDLING
/////////////////////////////////////////////////////////////////////////////
void CHARACTER::SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos) // bNewPos == 255 면 DELETE
void CHARACTER::SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos) // bNewPos == 255 면 DELETE
{
if (bOldPos == bNewPos)
return;
@ -121,7 +121,7 @@ bool CHARACTER::SwapQuickslot(BYTE a, BYTE b)
if (a >= QUICKSLOT_MAX_NUM || b >= QUICKSLOT_MAX_NUM)
return false;
// 퀵 슬롯 자리를 서로 바꾼다.
// 퀵 슬롯 자리를 서로 바꾼다.
quickslot = m_quickslot[a];
m_quickslot[a] = m_quickslot[b];

View File

@ -6,7 +6,7 @@
#include "affect.h"
#include "locale_service.h"
// 독
// 독
const int poison_damage_rate[MOB_RANK_MAX_NUM] =
{
80, 50, 40, 30, 25, 1
@ -135,7 +135,7 @@ EVENTFUNC(fire_event)
/*
LEVEL에 ..
LEVEL에 ..
+8 0%
+7 5%
@ -184,7 +184,7 @@ void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker)
if (m_pkPoisonEvent)
return;
if (m_bHasPoisoned && !IsPC()) // 몬스터는 독이 한번만 걸린다.
if (m_bHasPoisoned && !IsPC()) // 몬스터는 독이 한번만 걸린다.
return;
if (pkAttacker && pkAttacker->GetLevel() < GetLevel())
@ -201,7 +201,7 @@ void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker)
/*if (IsImmune(IMMUNE_POISON))
return;*/
// 독 내성 굴림 실패, 독에 걸렸다!
// 독 내성 굴림 실패, 독에 걸렸다!
m_bHasPoisoned = true;
AddAffect(AFFECT_POISON, POINT_NONE, 0, AFF_POISON, POISON_LENGTH + 1, 0, true);

View File

@ -62,7 +62,7 @@ void CHARACTER::SetSkillNextReadTime(DWORD dwVnum, time_t time)
bool TSkillUseInfo::HitOnce(DWORD dwVnum)
{
// 쓰지도않았으면 때리지도 못한다.
// 쓰지도않았으면 때리지도 못한다.
if (!bUsed)
return false;
@ -97,7 +97,7 @@ bool TSkillUseInfo::UseSkill(bool isGrandMaster, DWORD vid, DWORD dwCooltime, in
this->isGrandMaster = isGrandMaster;
DWORD dwCur = get_dword_time();
// 아직 쿨타임이 끝나지 않았다.
// 아직 쿨타임이 끝나지 않았다.
if (bUsed && dwNextSkillUsableTime > dwCur)
{
SPDLOG_DEBUG("cooltime is not over delta {}", dwNextSkillUsableTime - dwCur);
@ -267,7 +267,7 @@ bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
if (!IsLearnableSkill(dwSkillVnum))
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련할 수 없는 스킬입니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot train this skill."));
return false;
}
@ -280,9 +280,9 @@ bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
{
if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY))
{
// 주안술서 사용중에는 시간 제한 무시
// 주안술서 사용중에는 시간 제한 무시
RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY);
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("주안술서를 통해 주화입마에서 빠져나왔습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have escaped the evil ghost curse with the help of an Exorcism Scroll."));
}
else
{
@ -293,19 +293,19 @@ bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
}
*/
// bType이 0이면 처음부터 책으로 수련 가능
// bType이 0이면 처음부터 책으로 수련 가능
if (pkSk->dwType == 0)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그랜드 마스터 수련을 할 수 없는 스킬입니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot train this skill up to Grand Master level."));
return false;
}
if (GetSkillMasterType(dwSkillVnum) != SKILL_GRAND_MASTER)
{
if (GetSkillMasterType(dwSkillVnum) > SKILL_GRAND_MASTER)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퍼펙트 마스터된 스킬입니다. 더 이상 수련 할 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You already are a Master of this skill. You cannot train this skill any further."));
else
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 스킬은 아직 그랜드 마스터 수련을 할 경지에 이르지 않았습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your Skill is not high enough to become a Grand Master."));
return false;
}
@ -316,7 +316,7 @@ bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
strTrainSkill = os.str();
}
// 여기서 확률을 계산합니다.
// 여기서 확률을 계산합니다.
BYTE bLastLevel = GetSkillLevel(dwSkillVnum);
int idx = std::min(9, GetSkillLevel(dwSkillVnum) - 30);
@ -386,15 +386,15 @@ bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
if (bLastLevel == GetSkillLevel(dwSkillVnum))
{
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("크윽, 기가 역류하고 있어! 이거 설마 주화입마인가!? 젠장!"));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련이 실패로 끝났습니다. 다시 도전해주시기 바랍니다."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("That did not work. Damn!"));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Training has failed. Please try again later."));
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("더 높은 경지의 수련을 성공적으로 끝내셨습니다."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("My body is full of energy!"));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("The training seems to be working already..."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You successfully finished your training with the book."));
LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_SUCCESS", "");
return true;
}
@ -421,7 +421,7 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
if (!IsLearnableSkill(dwSkillVnum))
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련할 수 없는 스킬입니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot train this skill."));
return false;
}
@ -433,20 +433,20 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
if ( GetExp() < need_exp )
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("경험치가 부족하여 책을 읽을 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot read this due to your lack of experience."));
return false;
}
}
// bType이 0이면 처음부터 책으로 수련 가능
// bType이 0이면 처음부터 책으로 수련 가능
if (pkSk->dwType != 0)
{
if (GetSkillMasterType(dwSkillVnum) != SKILL_MASTER)
{
if (GetSkillMasterType(dwSkillVnum) > SKILL_MASTER)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 스킬은 책으로 더이상 수련할 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot train this skill with a Book."));
else
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 스킬은 아직 책으로 수련할 경지에 이르지 않았습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This skill's level is not high enough to be trained with a Book."));
return false;
}
}
@ -457,9 +457,9 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
{
if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY))
{
// 주안술서 사용중에는 시간 제한 무시
// 주안술서 사용중에는 시간 제한 무시
RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY);
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("주안술서를 통해 주화입마에서 빠져나왔습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have escaped the evil ghost curse with the help of an Exorcism Scroll."));
}
else
{
@ -469,7 +469,7 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
}
}
// 여기서 확률을 계산합니다.
// 여기서 확률을 계산합니다.
BYTE bLastLevel = GetSkillLevel(dwSkillVnum);
if (bProb != 0)
@ -527,13 +527,13 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
if (Random::get(1, 100) > percent)
{
// 책읽기에 성공
// 책읽기에 성공
if (read_count >= need_bookcount)
{
SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK);
pPC->SetFlag(flag, 0);
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("책으로 더 높은 경지의 수련을 성공적으로 끝내셨습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have successfully finished your training with the Book."));
LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
return true;
}
@ -544,27 +544,27 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
switch (Random::get(1, 3))
{
case 1:
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("어느정도 이 기술에 대해 이해가 되었지만 조금 부족한듯 한데.."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("I'm making progress, but I still haven't understood everything."));
break;
case 2:
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("드디어 끝이 보이는 건가... 이 기술은 이해하기가 너무 힘들어.."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("These instructions are difficult to understand. I have to carry on studying."));
break;
case 3:
default:
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열심히 하는 배움을 가지는 것만이 기술을 배울수 있는 유일한 길이다.."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("I understand this chapter. But I've got to carry on working hard."));
break;
}
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d 권을 더 읽어야 수련을 완료 할 수 있습니다."), need_bookcount - read_count);
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have to read %d more skill books to improve this skill."), need_bookcount - read_count);
return true;
}
}
}
else
{
// 사용자의 퀘스트 정보 로드 실패
// 사용자의 퀘스트 정보 로드 실패
}
}
// INTERNATIONAL_VERSION
@ -602,15 +602,15 @@ bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
if (bLastLevel != GetSkillLevel(dwSkillVnum))
{
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("몸에서 뭔가 힘이 터져 나오는 기분이야!"));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("뜨거운 무엇이 계속 용솟음치고 있어! 이건, 이것은!"));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("책으로 더 높은 경지의 수련을 성공적으로 끝내셨습니다."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("My body is full of energy!"));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("The training seems to be working already..."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have successfully finished your training with the Book."));
LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
}
else
{
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("크윽, 기가 역류하고 있어! 이거 설마 주화입마인가!? 젠장!"));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련이 실패로 끝났습니다. 다시 도전해주시기 바랍니다."));
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("That did not work. Damn!"));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Training has failed. Please try again later."));
LogManager::instance().CharLog(this, dwSkillVnum, "READ_FAIL", "");
}
@ -694,7 +694,7 @@ void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
if (IsPolymorphed())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot change your status while you are transformed."));
return;
}
@ -721,7 +721,7 @@ void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
if (!IsLearnableSkill(dwVnum))
return;
// 그랜드 마스터는 퀘스트로만 수행가능
// 그랜드 마스터는 퀘스트로만 수행가능
if (pkSk->dwType != 0)
{
switch (GetSkillMasterType(pkSk->dwVnum))
@ -738,7 +738,7 @@ void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
if (bMethod == SKILL_UP_BY_POINT)
{
// 마스터가 아닌 상태에서만 수련가능
// 마스터가 아닌 상태에서만 수련가능
if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL)
return;
@ -747,7 +747,7 @@ void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
}
else if (bMethod == SKILL_UP_BY_BOOK)
{
if (pkSk->dwType != 0) // 직업에 속하지 않았거나 포인트로 올릴수 없는 스킬은 처음부터 책으로 배울 수 있다.
if (pkSk->dwType != 0) // 직업에 속하지 않았거나 포인트로 올릴수 없는 스킬은 처음부터 책으로 배울 수 있다.
if (GetSkillMasterType(pkSk->dwVnum) != SKILL_MASTER)
return;
}
@ -801,11 +801,11 @@ void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
if (pkSk->dwType != 0)
{
// 갑자기 그레이드 업하는 코딩
// 갑자기 그레이드 업하는 코딩
switch (GetSkillMasterType(pkSk->dwVnum))
{
case SKILL_NORMAL:
// 번섭은 스킬 업그레이드 17~20 사이 랜덤 마스터 수련
// 번섭은 스킬 업그레이드 17~20 사이 랜덤 마스터 수련
if (GetSkillLevel(pkSk->dwVnum) >= 17)
{
if (GetQuestFlag("reset_scroll.force_to_master_skill") > 0)
@ -863,7 +863,7 @@ void CHARACTER::ResetSkill()
if (NULL == m_pSkillLevels)
return;
// 보조 스킬은 리셋시키지 않는다
// 보조 스킬은 리셋시키지 않는다
std::vector<std::pair<DWORD, TPlayerSkill> > vec;
size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]);
@ -994,7 +994,7 @@ EVENTFUNC(ChainLightningEvent)
SPDLOG_DEBUG("chainlighting event {}", pkChr->GetName());
if (pkChrVictim->GetParty()) // 파티 먼저
if (pkChrVictim->GetParty()) // 파티 먼저
{
pkTarget = pkChrVictim->GetParty()->GetNextOwnership(NULL, pkChrVictim->GetX(), pkChrVictim->GetY());
if (pkTarget == pkChrVictim || !Random::get(0, 2) || pkChr->GetChainLightingExcept().find(pkTarget) != pkChr->GetChainLightingExcept().end())
@ -1090,7 +1090,7 @@ struct FuncSplashDamage
}
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)
{
@ -1159,7 +1159,7 @@ struct FuncSplashDamage
m_pkSk->SetPointVar("chain", m_pkChr->GetChainLightningIndex());
m_pkChr->IncChainLightningIndex();
bool bUnderEunhyung = m_pkChr->GetAffectedEunhyung() > 0; // 이건 왜 여기서 하지??
bool bUnderEunhyung = m_pkChr->GetAffectedEunhyung() > 0; // 이건 왜 여기서 하지??
m_pkSk->SetPointVar("ek", m_pkChr->GetAffectedEunhyung()*1./100);
//m_pkChr->ClearAffectedEunhyung();
@ -1178,7 +1178,7 @@ struct FuncSplashDamage
if (test_server && iAmount == 0 && m_pkSk->bPointOn != POINT_NONE)
{
m_pkChr->ChatPacket(CHAT_TYPE_INFO, "효과가 없습니다. 스킬 공식을 확인하세요");
m_pkChr->ChatPacket(CHAT_TYPE_INFO, "Not working, check the skill formula");
}
////////////////////////////////////////////////////////////////////////////////
iAmount = -iAmount;
@ -1241,11 +1241,11 @@ struct FuncSplashDamage
if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() != (DWORD) pkChrVictim->GetVID())
{
// 데미지 감소
// 데미지 감소
iDam = (int) (iDam * m_pkSk->kSplashAroundDamageAdjustPoly.Eval());
}
// TODO 스킬에 따른 데미지 타입 기록해야한다.
// TODO 스킬에 따른 데미지 타입 기록해야한다.
EDamageType dt = DAMAGE_TYPE_NONE;
switch (m_pkSk->bSkillAttrType)
@ -1268,7 +1268,7 @@ struct FuncSplashDamage
case WEAPON_TWO_HANDED:
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_TWOHAND)) / 100;
// 양손검 페널티 10%
// 양손검 페널티 10%
//iDam = iDam * 95 / 100;
break;
@ -1293,8 +1293,8 @@ struct FuncSplashDamage
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;
@ -1302,8 +1302,8 @@ struct FuncSplashDamage
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;
@ -1314,13 +1314,13 @@ struct FuncSplashDamage
}
//
// 20091109 독일 스킬 속성 요청 작업
// 기존 스킬 테이블에 SKILL_FLAG_WIND, SKILL_FLAG_ELEC, SKILL_FLAG_FIRE를 가진 스킬이
// 전혀 없었으므로 몬스터의 RESIST_WIND, RESIST_ELEC, RESIST_FIRE도 사용되지 않고 있었다.
// 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로
// 복사하였다.
// PvP와 PvE밸런스 분리를 위해 의도적으로 NPC만 적용하도록 했으며 기존 밸런스와 차이점을
// 느끼지 못하기 위해 mob_proto의 RESIST_MAGIC을 RESIST_WIND, RESIST_ELEC, RESIST_FIRE로
// 복사하였다.
//
if (pkChrVictim->IsNPC())
{
@ -1627,7 +1627,7 @@ EVENTFUNC(skill_gwihwan_event)
{
PIXEL_POSITION pos;
// 성공
// 성공
if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos))
{
SPDLOG_DEBUG("Recall: {} {} {} -> {} {}", ch->GetName(), ch->GetX(), ch->GetY(), pos.x, pos.y);
@ -1641,8 +1641,8 @@ EVENTFUNC(skill_gwihwan_event)
}
else
{
//실패
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("귀환에 실패하였습니다."));
//실패
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Teleportation has failed."));
}
return 0;
}
@ -1666,11 +1666,11 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
SPDLOG_TRACE("ComputeSkillAtPosition {} vnum {} x {} y {} level {}",
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;
@ -1754,7 +1754,7 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE)
{
ChatPacket(CHAT_TYPE_INFO, "효과가 없습니다. 스킬 공식을 확인하세요");
ChatPacket(CHAT_TYPE_INFO, "Not working, check the skill formula");
}
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT))
@ -1769,7 +1769,7 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE))
{
//
// 공격 스킬일 경우
// 공격 스킬일 경우
//
bool bAdded = false;
@ -1794,7 +1794,7 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
int iDur = (int) pkSk->kDurationPoly.Eval();
if (IsPC())
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // 길드 스킬은 쿨타임 처리를 하지 않는다.
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // 길드 스킬은 쿨타임 처리를 하지 않는다.
if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG)
{
return BATTLE_NONE;
@ -1884,7 +1884,7 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
if (iDur > 0)
{
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
// AffectFlag가 없거나, toggle 하는 것이 아니라면..
// AffectFlag가 없거나, toggle 하는 것이 아니라면..
pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/);
AddAffect(pkSk->dwVnum,
@ -1939,13 +1939,13 @@ int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTar
}
}
// bSkillLevel 인자가 0이 아닐 경우에는 m_abSkillLevels를 사용하지 않고 강제로
// bSkillLevel로 계산한다.
// bSkillLevel 인자가 0이 아닐 경우에는 m_abSkillLevels를 사용하지 않고 강제로
// bSkillLevel로 계산한다.
int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel)
{
const bool bCanUseHorseSkill = CanUseHorseSkill();
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return
if (false == bCanUseHorseSkill && true == IsRiding())
return BATTLE_NONE;
@ -1967,7 +1967,7 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
return BATTLE_NONE;
// 상대방에게 쓰는 것이 아니면 나에게 써야 한다.
// 상대방에게 쓰는 것이 아니면 나에게 써야 한다.
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY))
pkVictim = this;
@ -2093,7 +2093,7 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE)
{
ChatPacket(CHAT_TYPE_INFO, "효과가 없습니다. 스킬 공식을 확인하세요");
ChatPacket(CHAT_TYPE_INFO, "Not working, check the skill formula");
}
// END_OF_ADD_GRANDMASTER_SKILL
@ -2135,7 +2135,7 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
if (IsPC())
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // 길드 스킬은 쿨타임 처리를 하지 않는다.
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)) // 길드 스킬은 쿨타임 처리를 하지 않는다.
if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG)
{
return BATTLE_NONE;
@ -2248,7 +2248,7 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
if (iDur > 0)
{
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
// AffectFlag가 없거나, toggle 하는 것이 아니라면..
// AffectFlag가 없거나, toggle 하는 것이 아니라면..
pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/);
if (pkSk->bPointOn2 != POINT_NONE)
@ -2421,14 +2421,14 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
return false;
if (GetHorseLevel() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말이 없습니다. 마굿간 경비병을 찾아가세요."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("No Horse here. Ask the Stable Boy."));
else
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말 소환 아이템을 사용하세요."));
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Please use an item to call a Horse."));
return true;
}
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return false
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return false
if (false == bCanUseHorseSkill && true == IsRiding())
return false;
@ -2474,7 +2474,7 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
}
m_SkillUseInfo[dwVnum].SetMainTargetVID(pkVictim->GetVID());
// DASH 상태의 탄환격은 공격기술
// DASH 상태의 탄환격은 공격기술
ComputeSkill(dwVnum, pkVictim);
RemoveAffect(dwVnum);
return true;
@ -2492,7 +2492,7 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
return true;
}
// Toggle 할 때는 SP를 쓰지 않음 (SelfOnly로 구분)
// Toggle 할 때는 SP를 쓰지 않음 (SelfOnly로 구분)
if ((0 != pkSk->dwAffectFlag || pkSk->dwVnum == SKILL_MUYEONG) && (pkSk->dwFlag & SKILL_FLAG_TOGGLE) && RemoveAffect(pkSk->dwVnum))
{
return true;
@ -2506,7 +2506,7 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
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;
@ -2560,7 +2560,7 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
return false;
if (test_server)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s SP소모: %d"), pkSk->szName, iNeededSP);
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s FP-Consumption: %d"), pkSk->szName, iNeededSP);
PointChange(POINT_SP, -iNeededSP);
}
@ -2570,7 +2570,7 @@ bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaste
if (pkSk->dwVnum == SKILL_MUYEONG || pkSk->IsChargeSkill() && !IsAffectFlag(AFF_TANHWAN_DASH) && !pkVictim)
{
// 처음 사용하는 무영진은 자신에게 Affect를 붙인다.
// 처음 사용하는 무영진은 자신에게 Affect를 붙인다.
pkVictim = this;
}
@ -2643,7 +2643,7 @@ int CHARACTER::GetSkillMasterType(DWORD dwVnum) const
int CHARACTER::GetSkillPower(DWORD dwVnum, BYTE bLevel) const
{
// 인어반지 아이템
// 인어반지 아이템
if (dwVnum >= SKILL_LANGUAGE1 && dwVnum <= SKILL_LANGUAGE3 && IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE))
{
return 100;
@ -2745,41 +2745,41 @@ void CHARACTER::SkillLearnWaitMoreTimeMessage(DWORD ms)
//const char* str = "";
//
if (ms < 3 * 60)
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("몸 속이 뜨겁군. 하지만 아주 편안해. 이대로 기를 안정시키자."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I am burning inside, but it is calming down my body. My Chi has to stabilise."));
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("A little slow, but steady... Without stopping!"));
else if (ms < 10 * 60) // 10분
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Yes, that feels great. My body is full of Chi."));
else if (ms < 30 * 60) // 30분
{
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("다 읽었다! 이제 비급에 적혀있는 대로 전신에 기를 돌리기만 하면,"));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("그것으로 수련은 끝난 거야!"));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I have read it! Now the Chi will spread through my body."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("The training is completed."));
}
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 < 1 * 3600) // 1시간
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I am on the last page of the book. The training is nearly finished!"));
else if (ms < 2 * 3600) // 2시간
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Nearly finished! Just a little bit more to go!"));
else if (ms < 3 * 3600)
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("좋았어! 조금만 더 읽으면 끝이다!"));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Eureka! I have nearly finished reading it!"));
else if (ms < 6 * 3600)
{
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("책장도 이제 얼마 남지 않았군."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("뭔가 몸 안에 힘이 생기는 기분인 걸."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Only a few more pages and then I'll have read everything."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I feel refreshed."));
}
else if (ms < 12 * 3600)
{
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("이제 좀 슬슬 가닥이 잡히는 것 같은데."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("좋아, 이 기세로 계속 나간다!"));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Now I understand it!"));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("Okay I have to stay concentrated!"));
}
else if (ms < 18 * 3600)
{
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("아니 어떻게 된 게 종일 읽어도 머리에 안 들어오냐."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("공부하기 싫어지네."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I keep reading the same line over and over again."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("I do not want to learn any more."));
}
else //if (ms < 2 * 86400)
{
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("생각만큼 읽기가 쉽지가 않군. 이해도 어렵고 내용도 난해해."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("이래서야 공부가 안된다구."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("It is a lot more complicated and more difficult to understand than I thought."));
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("It's hard for me to concentrate. I should take a break."));
}
/*
str = "30%";
@ -3138,10 +3138,10 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
static DWORD s_anMotion2SkillVnumList[MOTION_MAX_NUM][SKILL_LIST_MAX_COUNT] =
{
// 스킬수 무사스킬ID 자객스킬ID 수라스킬ID 무당스킬ID
// 스킬수 무사스킬ID 자객스킬ID 수라스킬ID 무당스킬ID
{ 0, 0, 0, 0, 0 }, // 0
// 1번 직군 기본 스킬
// 1번 직군 기본 스킬
{ 4, 1, 31, 61, 91 }, // 1
{ 4, 2, 32, 62, 92 }, // 2
{ 4, 3, 33, 63, 93 }, // 3
@ -3150,9 +3150,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 6, 36, 66, 96 }, // 6
{ 0, 0, 0, 0, 0 }, // 7
{ 0, 0, 0, 0, 0 }, // 8
// 1번 직군 기본 스킬 끝
// 1번 직군 기본 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 9
{ 0, 0, 0, 0, 0 }, // 10
{ 0, 0, 0, 0, 0 }, // 11
@ -3160,9 +3160,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 0, 0, 0, 0, 0 }, // 13
{ 0, 0, 0, 0, 0 }, // 14
{ 0, 0, 0, 0, 0 }, // 15
// 여유분 끝
// 여유분 끝
// 2번 직군 기본 스킬
// 2번 직군 기본 스킬
{ 4, 16, 46, 76, 106 }, // 16
{ 4, 17, 47, 77, 107 }, // 17
{ 4, 18, 48, 78, 108 }, // 18
@ -3171,14 +3171,14 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 21, 51, 81, 111 }, // 21
{ 0, 0, 0, 0, 0 }, // 22
{ 0, 0, 0, 0, 0 }, // 23
// 2번 직군 기본 스킬 끝
// 2번 직군 기본 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 24
{ 0, 0, 0, 0, 0 }, // 25
// 여유분 끝
// 여유분 끝
// 1번 직군 마스터 스킬
// 1번 직군 마스터 스킬
{ 4, 1, 31, 61, 91 }, // 26
{ 4, 2, 32, 62, 92 }, // 27
{ 4, 3, 33, 63, 93 }, // 28
@ -3187,9 +3187,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 6, 36, 66, 96 }, // 31
{ 0, 0, 0, 0, 0 }, // 32
{ 0, 0, 0, 0, 0 }, // 33
// 1번 직군 마스터 스킬 끝
// 1번 직군 마스터 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 34
{ 0, 0, 0, 0, 0 }, // 35
{ 0, 0, 0, 0, 0 }, // 36
@ -3197,9 +3197,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 0, 0, 0, 0, 0 }, // 38
{ 0, 0, 0, 0, 0 }, // 39
{ 0, 0, 0, 0, 0 }, // 40
// 여유분 끝
// 여유분 끝
// 2번 직군 마스터 스킬
// 2번 직군 마스터 스킬
{ 4, 16, 46, 76, 106 }, // 41
{ 4, 17, 47, 77, 107 }, // 42
{ 4, 18, 48, 78, 108 }, // 43
@ -3208,14 +3208,14 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 21, 51, 81, 111 }, // 46
{ 0, 0, 0, 0, 0 }, // 47
{ 0, 0, 0, 0, 0 }, // 48
// 2번 직군 마스터 스킬 끝
// 2번 직군 마스터 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 49
{ 0, 0, 0, 0, 0 }, // 50
// 여유분 끝
// 여유분 끝
// 1번 직군 그랜드 마스터 스킬
// 1번 직군 그랜드 마스터 스킬
{ 4, 1, 31, 61, 91 }, // 51
{ 4, 2, 32, 62, 92 }, // 52
{ 4, 3, 33, 63, 93 }, // 53
@ -3224,9 +3224,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 6, 36, 66, 96 }, // 56
{ 0, 0, 0, 0, 0 }, // 57
{ 0, 0, 0, 0, 0 }, // 58
// 1번 직군 그랜드 마스터 스킬 끝
// 1번 직군 그랜드 마스터 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 59
{ 0, 0, 0, 0, 0 }, // 60
{ 0, 0, 0, 0, 0 }, // 61
@ -3234,9 +3234,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 0, 0, 0, 0, 0 }, // 63
{ 0, 0, 0, 0, 0 }, // 64
{ 0, 0, 0, 0, 0 }, // 65
// 여유분 끝
// 여유분 끝
// 2번 직군 그랜드 마스터 스킬
// 2번 직군 그랜드 마스터 스킬
{ 4, 16, 46, 76, 106 }, // 66
{ 4, 17, 47, 77, 107 }, // 67
{ 4, 18, 48, 78, 108 }, // 68
@ -3245,14 +3245,14 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 21, 51, 81, 111 }, // 71
{ 0, 0, 0, 0, 0 }, // 72
{ 0, 0, 0, 0, 0 }, // 73
// 2번 직군 그랜드 마스터 스킬 끝
// 2번 직군 그랜드 마스터 스킬 끝
//여유분
//여유분
{ 0, 0, 0, 0, 0 }, // 74
{ 0, 0, 0, 0, 0 }, // 75
// 여유분 끝
// 여유분 끝
// 1번 직군 퍼펙트 마스터 스킬
// 1번 직군 퍼펙트 마스터 스킬
{ 4, 1, 31, 61, 91 }, // 76
{ 4, 2, 32, 62, 92 }, // 77
{ 4, 3, 33, 63, 93 }, // 78
@ -3261,9 +3261,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 6, 36, 66, 96 }, // 81
{ 0, 0, 0, 0, 0 }, // 82
{ 0, 0, 0, 0, 0 }, // 83
// 1번 직군 퍼펙트 마스터 스킬 끝
// 1번 직군 퍼펙트 마스터 스킬 끝
// 여유분
// 여유분
{ 0, 0, 0, 0, 0 }, // 84
{ 0, 0, 0, 0, 0 }, // 85
{ 0, 0, 0, 0, 0 }, // 86
@ -3271,9 +3271,9 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 0, 0, 0, 0, 0 }, // 88
{ 0, 0, 0, 0, 0 }, // 89
{ 0, 0, 0, 0, 0 }, // 90
// 여유분 끝
// 여유분 끝
// 2번 직군 퍼펙트 마스터 스킬
// 2번 직군 퍼펙트 마스터 스킬
{ 4, 16, 46, 76, 106 }, // 91
{ 4, 17, 47, 77, 107 }, // 92
{ 4, 18, 48, 78, 108 }, // 93
@ -3282,23 +3282,23 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 4, 21, 51, 81, 111 }, // 96
{ 0, 0, 0, 0, 0 }, // 97
{ 0, 0, 0, 0, 0 }, // 98
// 2번 직군 퍼펙트 마스터 스킬 끝
// 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
@ -3313,13 +3313,13 @@ bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
{ 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)
@ -3473,7 +3473,7 @@ bool CHARACTER::CanUseSkill(DWORD dwSkillVnum) const
if (true == IsRiding())
{
//마운트 탈것중 고급말만 스킬 사용가능
//마운트 탈것중 고급말만 스킬 사용가능
if(GetMountVnum())
{
if( GetMountVnum() < 20209 && GetMountVnum() > 20212)

View File

@ -63,7 +63,7 @@ namespace
!pkChr->IsAffectFlag(AFF_WAR_FLAG2) &&
!pkChr->IsAffectFlag(AFF_WAR_FLAG3))
{
// 우리편 깃발일 경우
// 우리편 깃발일 경우
if ((DWORD) m_pkChr->GetPoint(POINT_STAT) == pkChr->GetGuild()->GetID())
{
CWarMap * pMap = pkChr->GetWarMap();
@ -72,8 +72,8 @@ namespace
if (!pMap || !pMap->GetTeamIndex(pkChr->GetGuild()->GetID(), idx))
return;
// 우리편 기지에 깃발이 없을 때만 깃발을 뽑는다. 안그러면 기지에 있는 깃발을
// 가만히 두고 싶은데도 뽑힐수가 있으므로..
// 우리편 기지에 깃발이 없을 때만 깃발을 뽑는다. 안그러면 기지에 있는 깃발을
// 가만히 두고 싶은데도 뽑힐수가 있으므로..
if (!pMap->IsFlagOnBase(idx))
{
m_pkChrFind = pkChr;
@ -82,7 +82,7 @@ namespace
}
else
{
// 상대편 깃발인 경우 무조건 뽑는다.
// 상대편 깃발인 경우 무조건 뽑는다.
m_pkChrFind = pkChr;
m_iMinDistance = iDist;
}
@ -155,7 +155,7 @@ namespace
//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());
snprintf(buf, sizeof(buf), LC_TEXT("The guild %s's flag has been stolen by player %s."), pMap->GetGuild(idx)->GetName(), pMap->GetGuild(idx_opp)->GetName());
pMap->Notice(buf);
}
}
@ -186,7 +186,7 @@ namespace
LPCHARACTER pkChr = (LPCHARACTER) ent;
// 일단 PC 공격안함
// 일단 PC 공격안함
if (pkChr->IsPC())
return;
@ -202,7 +202,7 @@ namespace
pkChr->IsAffectFlag(AFF_REVIVE_INVISIBLE))
return;
// 왜구는 패스
// 왜구는 패스
if (pkChr->GetRaceNum() == 5001)
return;
@ -285,7 +285,7 @@ void CHARACTER::CowardEscape()
for (int iDistIdx = 2; iDistIdx >= 0; --iDistIdx)
for (int iTryCount = 0; iTryCount < 8; ++iTryCount)
{
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
float fx, fy;
float fDist = Random::get(iDist[iDistIdx], iDist[iDistIdx+1]);
@ -367,7 +367,7 @@ void CHARACTER::StateIdle()
}
else if (IsWarp() || IsGoto())
{
// 워프는 이벤트로 처리
// 워프는 이벤트로 처리
m_dwStateDuration = 60 * passes_per_sec;
return;
}
@ -375,7 +375,7 @@ void CHARACTER::StateIdle()
if (IsPC())
return;
// NPC 처리
// NPC 처리
if (!IsMonster())
{
__StateIdle_NPC();
@ -506,7 +506,7 @@ void CHARACTER::__StateIdle_NPC()
MonsterChat(MONSTER_CHAT_WAIT);
m_dwStateDuration = PASSES_PER_SEC(5);
// 펫 시스템의 Idle 처리는 기존 거의 모든 종류의 캐릭터들이 공유해서 사용하는 상태머신이 아닌 CPetActor::Update에서 처리함.
// 펫 시스템의 Idle 처리는 기존 거의 모든 종류의 캐릭터들이 공유해서 사용하는 상태머신이 아닌 CPetActor::Update에서 처리함.
if (IsPet())
return;
else if (IsGuardNPC())
@ -531,21 +531,21 @@ void CHARACTER::__StateIdle_NPC()
}
else
{
if (GetRaceNum() == xmas::MOB_SANTA_VNUM) // 산타
if (GetRaceNum() == xmas::MOB_SANTA_VNUM) // 산타
{
if (get_dword_time() > m_dwPlayStartTime)
{
int next_warp_time = 2 * 1000; // 2초
int next_warp_time = 2 * 1000; // 2초
m_dwPlayStartTime = get_dword_time() + next_warp_time;
// 시간이 넘었으니 워프합시다.
// 시간이 넘었으니 워프합시다.
/*
*
*
const int WARP_MAP_INDEX_NUM = 4;
static const int c_lWarpMapIndexs[WARP_MAP_INDEX_NUM] = {61, 62, 63, 64};
*/
// 신선자 노해용
// 신선자 노해용
const int WARP_MAP_INDEX_NUM = 7;
static const int c_lWarpMapIndexs[WARP_MAP_INDEX_NUM] = { 61, 62, 63, 64, 3, 23, 43 };
int lNextMapIndex;
@ -553,7 +553,7 @@ void CHARACTER::__StateIdle_NPC()
if (map_allow_find(lNextMapIndex))
{
// 이곳입니다.
// 이곳입니다.
M2_DESTROY_CHARACTER(this);
int iNextSpawnDelay = 0;
if (LC_IsYMIR())
@ -565,7 +565,7 @@ void CHARACTER::__StateIdle_NPC()
}
else
{
// 다른 서버 입니다.
// 다른 서버 입니다.
TPacketGGXmasWarpSanta p;
p.bHeader = HEADER_GG_XMAS_WARP_SANTA;
p.bChannel = g_bChannel;
@ -579,7 +579,7 @@ void CHARACTER::__StateIdle_NPC()
if (!IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
{
//
// 이 곳 저 곳 이동한다.
// 이 곳 저 곳 이동한다.
//
LPCHARACTER pkChrProtege = GetProtege();
@ -594,14 +594,14 @@ void CHARACTER::__StateIdle_NPC()
if (!Random::get(0, 6))
{
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
float fx, fy;
float fDist = Random::get(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;
@ -627,7 +627,7 @@ void CHARACTER::__StateIdle_Monster()
if (IsCoward())
{
// 겁쟁이 몬스터는 도망만 다닙니다.
// 겁쟁이 몬스터는 도망만 다닙니다.
if (!IsDead())
CowardEscape();
@ -653,16 +653,16 @@ void CHARACTER::__StateIdle_Monster()
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);
}
@ -683,40 +683,40 @@ void CHARACTER::__StateIdle_Monster()
LPCHARACTER pkChrProtege = GetProtege();
// 보호할 것(돌, 파티장)에게로 부터 멀다면 따라간다.
// 보호할 것(돌, 파티장)에게로 부터 멀다면 따라간다.
if (pkChrProtege)
{
if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000)
{
if (Follow(pkChrProtege, Random::get(150, 400)))
{
MonsterLog("[IDLE] 리더로부터 너무 멀리 떨어졌다! 복귀한다.");
MonsterLog("[IDLE] You're too far from the leader! Return to the leader.");
return;
}
}
}
//
// 그냥 왔다리 갔다리 한다.
// 그냥 왔다리 갔다리 한다.
//
if (!no_wander && !IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
{
if (!Random::get(0, 6))
{
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
float fx, fy;
float fDist = Random::get(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 상태에서 주변을 서성거릴 때, 현재 무조건 뛰어가게 되어 있음. (절대로 걷지 않음)
// 그래픽 팀에서 몬스터가 걷는 모습도 보고싶다고 해서 임시로 특정확률로 걷거나 뛰게 함. (게임의 전반적인 느낌이 틀려지기 때문에 일단 테스트 모드에서만 작동)
// NOTE: 몬스터가 IDLE 상태에서 주변을 서성거릴 때, 현재 무조건 뛰어가게 되어 있음. (절대로 걷지 않음)
// 그래픽 팀에서 몬스터가 걷는 모습도 보고싶다고 해서 임시로 특정확률로 걷거나 뛰게 함. (게임의 전반적인 느낌이 틀려지기 때문에 일단 테스트 모드에서만 작동)
if (g_test_server)
{
if (Random::get(0, 100) < 60)
@ -745,13 +745,13 @@ bool __CHARACTER_GotoNearTarget(LPCHARACTER self, LPCHARACTER victim)
{
case BATTLE_TYPE_RANGE:
case BATTLE_TYPE_MAGIC:
// 마법사나 궁수는 공격 거리의 80%까지 가서 공격을 시작한다.
// 마법사나 궁수는 공격 거리의 80%까지 가서 공격을 시작한다.
if (self->Follow(victim, self->GetMobAttackRange() * 8 / 10))
return true;
break;
default:
// 나머지는 90%?
// 나머지는 90%?
if (self->Follow(victim, self->GetMobAttackRange() * 9 / 10))
return true;
}
@ -781,7 +781,7 @@ void CHARACTER::StateMove()
LPCHARACTER victim = GetExchange()->GetCompany()->GetOwner();
int iDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY());
// 거리 체크
// 거리 체크
if (iDist >= EXCHANGE_MAX_DISTANCE)
{
GetExchange()->Cancel();
@ -789,17 +789,17 @@ void CHARACTER::StateMove()
}
}
// 스테미나가 0 이상이어야 한다.
// 스테미나가 0 이상이어야 한다.
if (IsPC())
{
if (IsWalking() && GetStamina() < GetMaxStamina())
{
// 5초 후 부터 스테미너 증가
// 5초 후 부터 스테미너 증가
if (get_dword_time() - GetWalkStartTime() > 5000)
PointChange(POINT_STAMINA, GetMaxStamina() / 1);
}
// 전투 중이면서 뛰는 중이면
// 전투 중이면서 뛰는 중이면
if (!IsWalking() && !IsRiding())
if ((get_dword_time() - GetLastAttackTime()) < 20000)
{
@ -817,7 +817,7 @@ void CHARACTER::StateMove()
if (GetStamina() <= 0)
{
// 스테미나가 모자라 걸어야함
// 스테미나가 모자라 걸어야함
SetStamina(0);
SetNowWalking(true);
StopStaminaConsume();
@ -838,7 +838,7 @@ void CHARACTER::StateMove()
if (g_test_server)
{
// 몬스터가 적을 쫓아가는 것이면 무조건 뛰어간다.
// 몬스터가 적을 쫓아가는 것이면 무조건 뛰어간다.
SetNowWalking(false);
}
}
@ -847,10 +847,10 @@ void CHARACTER::StateMove()
{
LPCHARACTER victim = GetVictim();
// 거대 거북
// 거대 거북
if (GetRaceNum() == 2191 && Random::get(1, 20) == 1 && get_dword_time() - m_pkMobInst->m_dwLastWarpTime > 1000)
{
// 워프 테스트
// 워프 테스트
float fx, fy;
GetDeltaByDegree(victim->GetRotation(), 400, &fx, &fy);
int new_x = victim->GetX() + (int)fx;
@ -864,7 +864,7 @@ void CHARACTER::StateMove()
return;
}
// TODO 방향전환을 해서 덜 바보가 되자!
// TODO 방향전환을 해서 덜 바보가 되자!
if (Random::get(0, 3) == 0)
{
if (__CHARACTER_GotoNearTarget(this, victim))
@ -877,7 +877,7 @@ void CHARACTER::StateMove()
{
if (IsPC())
{
SPDLOG_DEBUG("도착 {} {} {}", GetName(), x, y);
SPDLOG_DEBUG("Arrival {} {} {}", GetName(), x, y);
GotoState(m_stateIdle);
StopStaminaConsume();
}
@ -886,7 +886,7 @@ void CHARACTER::StateMove()
if (GetVictim() && !IsCoward())
{
if (!IsState(m_stateBattle))
MonsterLog("[BATTLE] 근처에 왔으니 공격시작 %s", GetVictim()->GetName());
MonsterLog("[BATTLE] Now that you're nearby, start attacking %s", GetVictim()->GetName());
GotoState(m_stateBattle);
m_dwStateDuration = 1;
@ -894,7 +894,7 @@ void CHARACTER::StateMove()
else
{
if (!IsState(m_stateIdle))
MonsterLog("[IDLE] 대상이 없으니 쉬자");
MonsterLog("[IDLE] No target, let's take a break");
GotoState(m_stateIdle);
@ -1005,7 +1005,7 @@ void CHARACTER::StateBattle()
{
if (!GetParty())
{
// 서몬해서 채워둘 파티를 만들어 둡니다.
// 서몬해서 채워둘 파티를 만들어 둡니다.
CPartyManager::instance().CreateParty(this);
}
@ -1014,8 +1014,8 @@ void CHARACTER::StateBattle()
if (bPct && pParty->CountMemberByVnum(GetSummonVnum()) < SUMMON_MONSTER_COUNT)
{
MonsterLog("부하 몬스터 소환!");
// 모자라는 녀석을 불러내 채웁시다.
MonsterLog("Summon Subordinate Monsters!");
// 모자라는 녀석을 불러내 채웁시다.
int sx = GetX() - 300;
int sy = GetY() - 300;
int ex = GetX() + 300;
@ -1035,12 +1035,12 @@ void CHARACTER::StateBattle()
float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY());
if (fDist >= 4000.0f) // 40미터 이상 멀어지면 포기
if (fDist >= 4000.0f) // 40미터 이상 멀어지면 포기
{
MonsterLog("타겟이 멀어서 포기");
MonsterLog("Give up because the target is far away");
SetVictim(NULL);
// 보호할 것(돌, 파티장) 주변으로 간다.
// 보호할 것(돌, 파티장) 주변으로 간다.
if (pkChrProtege)
if (DISTANCE_APPROX(GetX() - pkChrProtege->GetX(), GetY() - pkChrProtege->GetY()) > 1000)
Follow(pkChrProtege, Random::get(150, 400));
@ -1059,7 +1059,7 @@ void CHARACTER::StateBattle()
if (2493 == m_pkMobData->m_table.dwVnum)
{
// 수룡(2493) 특수 처리
// 수룡(2493) 특수 처리
m_dwStateDuration = BlueDragon_StateBattle(this);
return;
}
@ -1067,7 +1067,7 @@ void CHARACTER::StateBattle()
DWORD dwCurTime = get_dword_time();
DWORD dwDuration = CalculateDuration(GetLimitPoint(POINT_ATT_SPEED), 2000);
if ((dwCurTime - m_dwLastAttackTime) < dwDuration) // 2초 마다 공격해야 한다.
if ((dwCurTime - m_dwLastAttackTime) < dwDuration) // 2초 마다 공격해야 한다.
{
m_dwStateDuration = std::max<int>(1, (passes_per_sec * (dwDuration - (dwCurTime - m_dwLastAttackTime)) / 1000));
return;
@ -1084,7 +1084,7 @@ void CHARACTER::StateBattle()
SetGodSpeed(true);
//
// 몹 스킬 처리
// 몹 스킬 처리
//
if (HasMobSkill())
{
@ -1109,11 +1109,11 @@ void CHARACTER::StateBattle()
}
}
if (!Attack(victim)) // 공격 실패라면? 왜 실패했지? TODO
if (!Attack(victim)) // 공격 실패라면? 왜 실패했지? TODO
m_dwStateDuration = passes_per_sec / 2;
else
{
// 적을 바라보게 만든다.
// 적을 바라보게 만든다.
SetRotationToXY(victim->GetX(), victim->GetY());
SendMovePacket(FUNC_ATTACK, 0, GetX(), GetY(), 0, dwCurTime);
@ -1152,7 +1152,7 @@ void CHARACTER::StateFlag()
pMap->RemoveFlag(idx);
snprintf(buf, sizeof(buf), LC_TEXT("%s 길드의 깃발을 %s 님이 획득하였습니다."), pMap->GetGuild(idx)->GetName(), f.m_pkChrFind->GetName());
snprintf(buf, sizeof(buf), LC_TEXT("%s has captured the flag of %s!"), pMap->GetGuild(idx)->GetName(), f.m_pkChrFind->GetName());
pMap->Notice(buf);
}
@ -1166,15 +1166,15 @@ void CHARACTER::StateFlagBase()
void CHARACTER::StateHorse()
{
float START_FOLLOW_DISTANCE = 400.0f; // 이 거리 이상 떨어지면 쫓아가기 시작함
float START_RUN_DISTANCE = 700.0f; // 이 거리 이상 떨어지면 뛰어서 쫓아감.
int MIN_APPROACH = 150; // 최소 접근 거리
int MAX_APPROACH = 300; // 최대 접근 거리
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); // 상태 지속 시간
DWORD STATE_DURATION = (DWORD)PASSES_PER_SEC(0.5); // 상태 지속 시간
bool bDoMoveAlone = true; // 캐릭터와 가까이 있을 때 혼자 여기저기 움직일건지 여부 -_-;
bool bRun = true; // 뛰어야 하나?
bool bDoMoveAlone = true; // 캐릭터와 가까이 있을 때 혼자 여기저기 움직일건지 여부 -_-;
bool bRun = true; // 뛰어야 하나?
if (IsDead())
return;
@ -1183,7 +1183,7 @@ void CHARACTER::StateHorse()
LPCHARACTER victim = GetRider();
// ! 아님 // 대상이 없는 경우 소환자가 직접 나를 클리어할 것임
// ! 아님 // 대상이 없는 경우 소환자가 직접 나를 클리어할 것임
if (!victim)
{
M2_DESTROY_CHARACTER(this);
@ -1197,7 +1197,7 @@ void CHARACTER::StateHorse()
if (fDist >= START_FOLLOW_DISTANCE)
{
if (fDist > START_RUN_DISTANCE)
SetNowWalking(!bRun); // NOTE: 함수 이름보고 멈추는건줄 알았는데 SetNowWalking(false) 하면 뛰는거임.. -_-;
SetNowWalking(!bRun); // NOTE: 함수 이름보고 멈추는건줄 알았는데 SetNowWalking(false) 하면 뛰는거임.. -_-;
Follow(victim, Random::get(MIN_APPROACH, MAX_APPROACH));
@ -1208,14 +1208,14 @@ void CHARACTER::StateHorse()
// wondering-.-
m_dwLastAttackTime = get_dword_time() + Random::get(5000, 12000);
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
SetRotation(Random::get(0, 359)); // 방향은 랜덤으로 설정
float fx, fy;
float fDist = Random::get(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;

View File

@ -162,7 +162,7 @@ ACMD(do_add_socket);
ACMD(do_inputall)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("명령어를 모두 입력하세요."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Please enter the Order in full length."));
}
ACMD(do_show_arena_list);
@ -182,7 +182,7 @@ 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);
@ -191,7 +191,7 @@ ACMD(do_monarch_tax);
ACMD(do_monarch_mob);
ACMD(do_monarch_notice);
//군주 관리 기능
//군주 관리 기능
ACMD(do_rmcandidacy);
ACMD(do_setmonarch);
ACMD(do_rmmonarch);
@ -199,10 +199,10 @@ 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);
@ -230,7 +230,7 @@ ACMD(do_ride);
ACMD(do_get_item_id_list);
ACMD(do_set_socket);
#ifdef __AUCTION__
// temp_auction 임시
// temp_auction 임시
ACMD(do_get_auction_list);
ACMD (do_get_my_auction_list);
ACMD (do_get_my_purchase_list);
@ -250,21 +250,21 @@ 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이 없어 클라에서 확인 할 수 없는 아이템 착용을 위해 만듦.
// 아이템 착용. icon이 없어 클라에서 확인 할 수 없는 아이템 착용을 위해 만듦.
ACMD (do_use_item);
ACMD (do_dragon_soul);
ACMD (do_ds_list);
@ -272,7 +272,7 @@ ACMD (do_clear_affect);
struct command_info cmd_info[] =
{
{ "!RESERVED!", NULL, 0, POS_DEAD, GM_IMPLEMENTOR }, /* 반드시 이 것이 처음이어야 한다. */
{ "!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 },
@ -293,7 +293,7 @@ struct command_info cmd_info[] =
{ "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 */
{ "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 },
@ -391,8 +391,9 @@ struct command_info cmd_info[] =
{ "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 },
// TODO: these can probably be removed
{ "\xB7\xCE\xB1\xD7\xB8\xA6\xBA\xB8\xBF\xA9\xC1\xE0", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD },
{ "\xB8\xF3\xBD\xBA\xC5\xCD\xBA\xB8\xBF\xA9\xC1\xE0", 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 },
@ -528,7 +529,7 @@ struct command_info cmd_info[] =
{ "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 },
{ "\xC1\xD6\xBB\xE7\xC0\xA7", 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 },
@ -538,7 +539,7 @@ struct command_info cmd_info[] =
{ "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 임시
{ "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 },
@ -578,7 +579,7 @@ struct command_info cmd_info[] =
{ "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 } /* 반드시 이 것이 마지막이어야 한다. */
{ "\n", NULL, 0, POS_DEAD, GM_IMPLEMENTOR } /* 반드시 이 것이 마지막이어야 한다. */
};
void interpreter_set_privilege(const char *cmd, int lvl)
@ -601,7 +602,7 @@ 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 넣을 자리 확보
// \0 넣을 자리 확보
dest_len -= 1;
while (src_len-- && *tmp)
@ -636,7 +637,7 @@ void interpret_command(LPCHARACTER ch, const char * argument, size_t len)
return ;
}
char cmd[128 + 1]; // buffer overflow 문제가 생기지 않도록 일부러 길이를 짧게 잡음
char cmd[128 + 1]; // buffer overflow 문제가 생기지 않도록 일부러 길이를 짧게 잡음
char new_line[256 + 1];
const char * line;
int icmd;
@ -653,7 +654,7 @@ void interpret_command(LPCHARACTER ch, const char * argument, size_t len)
{
if (cmd_info[icmd].command_pointer == do_cmd)
{
if (!strcmp(cmd_info[icmd].command, cmd)) // do_cmd는 모든 명령어를 쳐야 할 수 있다.
if (!strcmp(cmd_info[icmd].command, cmd)) // do_cmd는 모든 명령어를 쳐야 할 수 있다.
break;
}
else if (!strncmp(cmd_info[icmd].command, cmd, cmdlen))
@ -665,24 +666,24 @@ void interpret_command(LPCHARACTER ch, const char * argument, size_t len)
switch (ch->GetPosition())
{
case POS_MOUNTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("탄 상태에서는 할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot do this whilst sitting on a Horse."));
break;
case POS_DEAD:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("쓰러진 상태에서는 할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot do that while you are lying on the ground."));
break;
case POS_SLEEPING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("꿈속에서 어떻게요?"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("In my Dreams? What?"));
break;
case POS_RESTING:
case POS_SITTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("먼저 일어 나세요."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Get up first."));
break;
/*
case POS_FIGHTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("목숨을 걸고 전투 중 입니다. 집중 하세요."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You're in a fight for your life. Stay focused."));
break;
*/
default:
@ -695,17 +696,17 @@ void interpret_command(LPCHARACTER ch, const char * argument, size_t len)
if (*cmd_info[icmd].command == '\n')
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This command does not exist."));
return;
}
if (cmd_info[icmd].gm_level && cmd_info[icmd].gm_level > ch->GetGMLevel())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This command does not exist."));
return;
}
if (strncmp("phase", cmd_info[icmd].command, 5) != 0) // 히든 명령어 처리
if (strncmp("phase", cmd_info[icmd].command, 5) != 0) // 히든 명령어 처리
SPDLOG_DEBUG("COMMAND: {}: {}", ch->GetName(), cmd_info[icmd].command);
((*cmd_info[icmd].command_pointer) (ch, line, icmd, cmd_info[icmd].subcmd));

View File

@ -51,11 +51,11 @@ enum SCMD_XMAS
};
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); // 같은 제국에게 공지
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();

View File

@ -23,65 +23,65 @@ struct emotion_type_s
int 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 },
{ "\xC5\xB0\xBD\xBA", "french_kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 2.0f },
{ "\xBB\xC7\xBB\xC7", "kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 1.5f },
{ "\xB5\xFB\xB1\xCD", "slap", NEED_PC | SELF_DISARM, 1.5f },
{ "\xB9\xDA\xBC\xF6", "clap", 0, 1.0f },
{ "\xBF\xCD", "cheer1", 0, 1.0f },
{ "\xB8\xB8\xBC\xBC", "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 },
{ "\xB4\xED\xBD\xBA\1", "dance1", 0, 1.0f },
{ "\xB4\xED\xBD\xBA\2", "dance2", 0, 1.0f },
{ "\xB4\xED\xBD\xBA\3", "dance3", 0, 1.0f },
{ "\xB4\xED\xBD\xBA\4", "dance4", 0, 1.0f },
{ "\xB4\xED\xBD\xBA\5", "dance5", 0, 1.0f },
{ "\xB4\xED\xBD\xBA\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 },
{ "\xC3\xE0\xC7\xCF", "congratulation", 0, 1.0f },
{ "\xBF\xEB\xBC\xAD", "forgive", 0, 1.0f },
{ "\xC8\xAD\xB3\xB2", "angry", 0, 1.0f },
{ "\xC0\xAF\xC8\xA4", "attractive", 0, 1.0f },
{ "\xBD\xBD\xC7\xC4", "sad", 0, 1.0f },
{ "\xBA\xEA\xB2\xF4", "shy", 0, 1.0f },
{ "\xC0\xC0\xBF\xF8", "cheerup", 0, 1.0f },
{ "\xC1\xFA\xC5\xF5", "banter", 0, 1.0f },
{ "\xB1\xE2\xBB\xDD", "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 },
//{ "\xC5\xB0\xBD\xBA", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_FRENCH_KISS, 1.0f },
{ "\xBB\xC7\xBB\xC7", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_KISS, 1.0f },
{ "\xB2\xB8\xBE\xC8\xB1\xE2", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_SHORT_HUG, 1.0f },
{ "\xC6\xF7\xBF\xCB", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_LONG_HUG, 1.0f },
{ "\xBE\xEE\xB1\xFA\xB5\xBF\xB9\xAB", NEED_PC | SELF_DISARM, MOTION_ACTION_PUT_ARMS_SHOULDER, 0.0f },
{ "\xC6\xC8\xC2\xAF", NEED_PC | WOMAN_ONLY | SELF_DISARM, MOTION_ACTION_FOLD_ARM, 0.0f },
{ "\xB5\xFB\xB1\xCD", 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 },
{ "\xC8\xD6\xC6\xC4\xB6\xF7", 0, MOTION_ACTION_CHEER_01, 0.0f },
{ "\xB8\xB8\xBC\xBC", 0, MOTION_ACTION_CHEER_02, 0.0f },
{ "\xB9\xDA\xBC\xF6", 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 },
{ "\xC8\xA3\xC8\xA3", 0, MOTION_ACTION_LAUGH_01, 0.0f },
{ "\xC5\xB1\xC5\xB1", 0, MOTION_ACTION_LAUGH_02, 0.0f },
{ "\xBF\xEC\xC7\xCF\xC7\xCF", 0, MOTION_ACTION_LAUGH_03, 0.0f },
{ "엉엉", 0, MOTION_ACTION_CRY_01, 0.0f },
{ "흑흑", 0, MOTION_ACTION_CRY_02, 0.0f },
{ "\xBE\xFB\xBE\xFB", 0, MOTION_ACTION_CRY_01, 0.0f },
{ "\xC8\xE6\xC8\xE6", 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 },
{ "\xC0\xCE\xBB\xE7", 0, MOTION_ACTION_GREETING_01, 0.0f },
{ "\xB9\xD9\xC0\xCC", 0, MOTION_ACTION_GREETING_02, 0.0f },
{ "\xC1\xA4\xC1\xDF\xC0\xCE\xBB\xE7", 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 },
{ "\xBA\xF1\xB3\xAD", 0, MOTION_ACTION_INSULT_01, 0.0f },
{ "\xB8\xF0\xBF\xE5", SELF_DISARM, MOTION_ACTION_INSULT_02, 0.0f },
{ "\xBF\xEC\xC0\xA1", 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 },
{ "\xB0\xBC\xBF\xEC\xB6\xD7", 0, MOTION_ACTION_ETC_01, 0.0f },
{ "\xB2\xF4\xB4\xF6\xB2\xF4\xB4\xF6", 0, MOTION_ACTION_ETC_02, 0.0f },
{ "\xB5\xB5\xB8\xAE\xB5\xB5\xB8\xAE", 0, MOTION_ACTION_ETC_03, 0.0f },
{ "\xB1\xDC\xC0\xFB\xB1\xDC\xC0\xFB", 0, MOTION_ACTION_ETC_04, 0.0f },
{ "\xC6\xA1", 0, MOTION_ACTION_ETC_05, 0.0f },
{ "\xBB\xD7", 0, MOTION_ACTION_ETC_06, 0.0f },
*/
};
@ -92,7 +92,7 @@ ACMD(do_emotion_allow)
{
if ( ch->GetArena() )
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot use this in the duel arena."));
return;
}
@ -108,11 +108,11 @@ ACMD(do_emotion_allow)
bool CHARACTER_CanEmotion(CHARACTER& rch)
{
// 결혼식 맵에서는 사용할 수 있다.
// 결혼식 맵에서는 사용할 수 있다.
if (marriage::WeddingManager::instance().IsWeddingMap(rch.GetMapIndex()))
return true;
// 열정의 가면 착용시 사용할 수 있다.
// 열정의 가면 착용시 사용할 수 있다.
if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK))
return true;
@ -128,7 +128,7 @@ ACMD(do_emotion)
{
if (ch->IsRiding())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 탄 상태에서 감정표현을 할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot express emotions whilst riding a horse."));
return;
}
}
@ -150,13 +150,13 @@ ACMD(do_emotion)
if (!CHARACTER_CanEmotion(*ch))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("열정의 가면을 착용시에만 할 수 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can do this when you wear an Emotion Mask."));
return;
}
if (IS_SET(emotion_types[i].flag, WOMAN_ONLY) && SEX_MALE==GET_SEX(ch))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("여자만 할 수 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Only women can do this."));
return;
}
@ -172,7 +172,7 @@ ACMD(do_emotion)
{
if (!victim)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 사람이 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This person does not exist."));
return;
}
}
@ -184,7 +184,7 @@ ACMD(do_emotion)
if (victim->IsRiding())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 탄 상대와 감정표현을 할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot use emotions with a player who is riding on a Horse."));
return;
}
@ -192,13 +192,13 @@ ACMD(do_emotion)
if (distance < 10)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("너무 가까이 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You are too near."));
return;
}
if (distance > 500)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("너무 멀리 있습니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You are too far away."));
return;
}
@ -206,7 +206,7 @@ ACMD(do_emotion)
{
if (GET_SEX(ch)==GET_SEX(victim))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이성간에만 할 수 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This action can only be done with another gender."));
return;
}
}
@ -223,13 +223,13 @@ ACMD(do_emotion)
if (0 == other || other != victim->GetPlayerID())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 행동은 상호동의 하에 가능 합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You need your fellow player's approval for this."));
return;
}
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 행동은 상호동의 하에 가능 합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You need your fellow player's approval for this."));
return;
}
}
@ -246,7 +246,7 @@ ACMD(do_emotion)
if (len < 0 || len >= (int) sizeof(chatbuf))
len = sizeof(chatbuf) - 1;
++len; // \0 문자 포함
++len; // \0 문자 포함
TPacketGCChat pack_chat;
pack_chat.header = HEADER_GC_CHAT;

File diff suppressed because it is too large Load Diff

View File

@ -261,44 +261,44 @@ bool CHARACTER_GoToName(LPCHARACTER ch, BYTE empire, int mapIndex, const char* g
/*
= {
{ "A1|영안읍성", 0, 1, 4693, 9642 },
{ "A3|자양현", 0, 3, 3608, 8776 },
{ "A1|\xBF\xB5\xBE\xC8\xC0\xBE\xBC\xBA", 0, 1, 4693, 9642 },
{ "A3|\xC0\xDA\xBE\xE7\xC7\xF6", 0, 3, 3608, 8776 },
{ "B1|조안읍성", 0, 21, 557, 1579 },
{ "B3|복정현", 0, 23, 1385, 2349 },
{ "B1|\xC1\xB6\xBE\xC8\xC0\xBE\xBC\xBA", 0, 21, 557, 1579 },
{ "B3|\xBA\xB9\xC1\xA4\xC7\xF6", 0, 23, 1385, 2349 },
{ "C1|평무읍성", 0, 41, 9696, 2784 },
{ "C3|박라현", 0, 43, 8731, 2426 },
{ "C1|\xC6\xF2\xB9\xAB\xC0\xBE\xBC\xBA", 0, 41, 9696, 2784 },
{ "C3|\xB9\xDA\xB6\xF3\xC7\xF6", 0, 43, 8731, 2426 },
// Snow
{ "Snow|서한산", 1, 61, 4342, 2906 },
{ "Snow|서한산", 2, 61, 3752, 1749 },
{ "Snow|서한산", 3, 61, 4918, 1736 },
{ "Snow|\xBC\xAD\xC7\xD1\xBB\xEA", 1, 61, 4342, 2906 },
{ "Snow|\xBC\xAD\xC7\xD1\xBB\xEA", 2, 61, 3752, 1749 },
{ "Snow|\xBC\xAD\xC7\xD1\xBB\xEA", 3, 61, 4918, 1736 },
// Flame
{ "Flame|도염화지|화염", 1, 62, 5994, 7563 },
{ "Flame|도염화지|화염", 2, 62, 5978, 6222 },
{ "Flame|도염화지|화염", 3, 62, 7307, 6898 },
{ "Flame|\xB5\xB5\xBF\xB0\xC8\xAD\xC1\xF6|\xC8\xAD\xBF\xB0", 1, 62, 5994, 7563 },
{ "Flame|\xB5\xB5\xBF\xB0\xC8\xAD\xC1\xF6|\xC8\xAD\xBF\xB0", 2, 62, 5978, 6222 },
{ "Flame|\xB5\xB5\xBF\xB0\xC8\xAD\xC1\xF6|\xC8\xAD\xBF\xB0", 3, 62, 7307, 6898 },
// Desert
{ "Desert|영비사막|사막", 1, 63, 2178, 6272 },
{ "Desert|영비사막|사막", 2, 63, 2219, 5027 },
{ "Desert|영비사막|사막", 3, 63, 3440, 5025 },
{ "Desert|\xBF\xB5\xBA\xF1\xBB\xE7\xB8\xB7|\xBB\xE7\xB8\xB7", 1, 63, 2178, 6272 },
{ "Desert|\xBF\xB5\xBA\xF1\xBB\xE7\xB8\xB7|\xBB\xE7\xB8\xB7", 2, 63, 2219, 5027 },
{ "Desert|\xBF\xB5\xBA\xF1\xBB\xE7\xB8\xB7|\xBB\xE7\xB8\xB7", 3, 63, 3440, 5025 },
// Threeway
{ "Three|승룡곡", 1, 64, 4021, 6739 },
{ "Three|승룡곡", 2, 64, 2704, 7399 },
{ "Three|승룡곡", 3, 64, 3213, 8080 },
{ "Three|\xBD\xC2\xB7\xE6\xB0\xEE", 1, 64, 4021, 6739 },
{ "Three|\xBD\xC2\xB7\xE6\xB0\xEE", 2, 64, 2704, 7399 },
{ "Three|\xBD\xC2\xB7\xE6\xB0\xEE", 3, 64, 3213, 8080 },
// 밀교사원
{ "Milgyo|밀교사원", 1, 65, 5536, 1436 },
{ "Milgyo|밀교사원", 2, 65, 5536, 1436 },
{ "Milgyo|밀교사원", 3, 65, 5536, 1436 },
// 밀교사원
{ "Milgyo|\xB9\xD0\xB1\xB3\xBB\xE7\xBF\xF8", 1, 65, 5536, 1436 },
{ "Milgyo|\xB9\xD0\xB1\xB3\xBB\xE7\xBF\xF8", 2, 65, 5536, 1436 },
{ "Milgyo|\xB9\xD0\xB1\xB3\xBB\xE7\xBF\xF8", 3, 65, 5536, 1436 },
// 사귀타워입구
{ "사귀타워입구", 1, 65, 5905, 1108 },
{ "사귀타워입구", 2, 65, 5905, 1108 },
{ "사귀타워입구", 3, 65, 5905, 1108 },
// 사귀타워입구
{ "\xBB\xE7\xB1\xCD\xC5\xB8\xBF\xF6\xC0\xD4\xB1\xB8", 1, 65, 5905, 1108 },
{ "\xBB\xE7\xB1\xCD\xC5\xB8\xBF\xF6\xC0\xD4\xB1\xB8", 2, 65, 5905, 1108 },
{ "\xBB\xE7\xB1\xCD\xC5\xB8\xBF\xF6\xC0\xD4\xB1\xB8", 3, 65, 5905, 1108 },
{ NULL, 0, 0, 0, 0 },
};
@ -500,7 +500,7 @@ ACMD(do_item)
M2_DESTROY_ITEM(item);
if (!ch->DragonSoul_IsQualified())
{
ch->ChatPacket(CHAT_TYPE_INFO, "인벤이 활성화 되지 않음.");
ch->ChatPacket(CHAT_TYPE_INFO, "Before you open the Cor Draconis, you have to complete the Dragon Stone quest and activate the Dragon Stone Alchemy.");
}
else
ch->ChatPacket(CHAT_TYPE_INFO, "Not enough inventory space.");
@ -841,7 +841,7 @@ struct FuncPurge
int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY());
if (!m_bAll && iDist >= 1000) // 10미터 이상에 있는 것들은 purge 하지 않는다.
if (!m_bAll && iDist >= 1000) // 10미터 이상에 있는 것들은 purge 하지 않는다.
return;
SPDLOG_DEBUG("PURGE: {} {}", pkChr->GetName(), iDist);
@ -1215,7 +1215,7 @@ ACMD(do_monarch_notice)
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("군주만이 사용 가능한 기능입니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This function can only be used by the emperor."));
}
}
@ -1555,12 +1555,12 @@ ACMD(do_makeguild)
if (!check_name(cp.name))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("적합하지 않은 길드 이름 입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This guild name is invalid."));
return;
}
gm.CreateGuild(cp);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) 길드가 생성되었습니다. [임시]"), cp.name);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("(%s) guild has been created. [Temporary]"), cp.name);
}
ACMD(do_deleteguild)
@ -1765,7 +1765,7 @@ LPCHARACTER chHori, chForge, chLib, chTemple, chTraining, chTree, chPortal, chBa
ACMD(do_b1)
{
//호리병 478 579
//호리병 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);
@ -1812,25 +1812,25 @@ ACMD(do_b2)
ACMD(do_b3)
{
// 포지 492 547
// 포지 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 -> 도서관
//높은탑 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 -> 힘의신전
//욕조 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
//권투장 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
//나무 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
//포탈 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
// 구슬 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);
}
@ -2069,7 +2069,7 @@ ACMD(do_reload)
break;
//END_RELOAD_ADMIN
case 'c': // cube
// 로컬 프로세스만 갱산한다.
// 로컬 프로세스만 갱산한다.
Cube_init ();
break;
}
@ -2110,7 +2110,7 @@ ACMD(do_level)
ACMD(do_gwlist)
{
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("현재 전쟁중인 길드 입니다"));
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("This guild is at war."));
CGuildManager::instance().ShowGuildWarList(ch);
}
@ -2168,7 +2168,7 @@ ACMD(do_guild_state)
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s: 존재하지 않는 길드 입니다."), arg1);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s: This guild does not exist."), arg1);
}
}
@ -2190,7 +2190,7 @@ struct FuncWeaken
int iDist = DISTANCE_APPROX(pkChr->GetX() - m_pkGM->GetX(), pkChr->GetY() - m_pkGM->GetY());
if (!m_bAll && iDist >= 1000) // 10미터 이상에 있는 것들은 purge 하지 않는다.
if (!m_bAll && iDist >= 1000) // 10미터 이상에 있는 것들은 purge 하지 않는다.
return;
if (pkChr->IsNPC())
@ -2472,7 +2472,7 @@ ACMD(do_priv_empire)
if (duration < 0)
goto USAGE;
// 시간 단위로 변경
// 시간 단위로 변경
duration = duration * (60*60);
SPDLOG_DEBUG("_give_empire_privileage(empire={}, type={}, value={}, duration={}) by command",
@ -2576,10 +2576,10 @@ ACMD(do_xmas)
// BLOCK_CHAT
ACMD(do_block_chat_list)
{
// GM이 아니거나 block_chat_privilege가 없는 사람은 명령어 사용 불가
// GM이 아니거나 block_chat_privilege가 없는 사람은 명령어 사용 불가
if (!ch || (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This command does not exist."));
return;
}
@ -2642,10 +2642,10 @@ ACMD(do_vote_block_chat)
ACMD(do_block_chat)
{
// GM이 아니거나 block_chat_privilege가 없는 사람은 명령어 사용 불가
// GM이 아니거나 block_chat_privilege가 없는 사람은 명령어 사용 불가
if (ch && (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This command does not exist."));
return;
}
@ -2667,8 +2667,8 @@ ACMD(do_block_chat)
{
if (ch)
{
ch->ChatPacket(CHAT_TYPE_INFO, "잘못된 형식의 시간입니다. h, m, s를 붙여서 지정해 주십시오.");
ch->ChatPacket(CHAT_TYPE_INFO, ") 10s, 10m, 1m 30s");
ch->ChatPacket(CHAT_TYPE_INFO, "The time is incorrectly formatted. Please specify it with an h, m, or s.");
ch->ChatPacket(CHAT_TYPE_INFO, "Example) 10s, 10m, 1m 30s");
}
return;
}
@ -2721,8 +2721,8 @@ ACMD(do_build)
CLand * pkLand = CManager::instance().FindLand(ch->GetMapIndex(), ch->GetX(), ch->GetY());
// NOTE: 조건 체크들은 클라이언트와 서버가 함께 하기 때문에 문제가 있을 때는
// 메세지를 전송하지 않고 에러를 출력한다.
// NOTE: 조건 체크들은 클라이언트와 서버가 함께 하기 때문에 문제가 있을 때는
// 메세지를 전송하지 않고 에러를 출력한다.
if (!pkLand)
{
SPDLOG_ERROR("{} trying to build on not buildable area.", ch->GetName());
@ -2735,17 +2735,17 @@ ACMD(do_build)
return;
}
// 건설 권한 체크
// 건설 권한 체크
if (GMLevel == GM_PLAYER)
{
// 플레이어가 집을 지을 때는 땅이 내껀지 확인해야 한다.
// 플레이어가 집을 지을 때는 땅이 내껀지 확인해야 한다.
if ((!ch->GetGuild() || ch->GetGuild()->GetID() != pkLand->GetOwner()))
{
SPDLOG_ERROR("{} trying to build on not owned land.", ch->GetName());
return;
}
// 내가 길마인가?
// 내가 길마인가?
if (ch->GetGuild()->GetMasterPID() != ch->GetPlayerID())
{
SPDLOG_ERROR("{} trying to build while not the guild master.", ch->GetName());
@ -2776,7 +2776,7 @@ ACMD(do_build)
const TObjectProto * t = CManager::instance().GetObjectProto(dwVnum);
if (!t)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("존재하지 않는 건물입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The building does not exist."));
return;
}
@ -2786,21 +2786,21 @@ ACMD(do_build)
{
if (pkLand->FindObjectByGroup(t->dwGroupVnum))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("같이 지을 수 없는 종류의 건물이 지어져 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This type of building can only be erected once."));
return;
}
}
// 건물 종속성 체크 (이 건물이 지어져 있어야함)
// 건물 종속성 체크 (이 건물이 지어져 있어야함)
if (t->dwDependOnGroupVnum)
{
// const TObjectProto * dependent = CManager::instance().GetObjectProto(dwVnum);
// if (dependent)
{
// 지어져있는가?
// 지어져있는가?
if (!pkLand->FindObjectByGroup(t->dwDependOnGroupVnum))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("건설에 필요한 건물이 지어져 있지 않습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The Main Building has to be erected first."));
return;
}
}
@ -2808,21 +2808,21 @@ ACMD(do_build)
if (test_server || GMLevel == GM_PLAYER)
{
// GM이 아닐경우만 (테섭에서는 GM도 소모)
// 건설 비용 체크
// GM이 아닐경우만 (테섭에서는 GM도 소모)
// 건설 비용 체크
if (t->dwPrice > BUILDING_MAX_PRICE)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("건물 비용 정보 이상으로 건설 작업에 실패했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Building failed because of incorrect pricing."));
return;
}
if (ch->GetGold() < (int)t->dwPrice)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("건설 비용이 부족합니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Your guild does not have enough Yang to erect this building."));
return;
}
// 아이템 자재 개수 체크
// 아이템 자재 개수 체크
int i;
for (i = 0; i < OBJECT_MATERIAL_MAX_NUM; ++i)
@ -2835,7 +2835,7 @@ ACMD(do_build)
if ((int) dwItemCount > ch->CountSpecifyItem(dwItemVnum))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("자재가 부족하여 건설할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have enough resources to build a building."));
return;
}
}
@ -2844,11 +2844,11 @@ ACMD(do_build)
float x_rot = atof(arg4);
float y_rot = atof(arg5);
float z_rot = atof(arg6);
// 20050811.myevan.건물 회전 기능 봉인 해제
// 20050811.myevan.건물 회전 기능 봉인 해제
/*
if (x_rot != 0.0f || y_rot != 0.0f || z_rot != 0.0f)
{
ch->ChatPacket(CHAT_TYPE_INFO, "건물 회전 기능은 아직 제공되지 않습니다");
ch->ChatPacket(CHAT_TYPE_INFO, "Rotating buildings is not yet available");
return;
}
*/
@ -2869,17 +2869,17 @@ ACMD(do_build)
if (!isSuccess)
{
if (test_server)
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("건물을 지을 수 없는 위치입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot erect a building at this place."));
return;
}
if (test_server || GMLevel == GM_PLAYER)
// 건설 재료 소모하기 (테섭에서는 GM도 소모)
// 건설 재료 소모하기 (테섭에서는 GM도 소모)
{
// 건설 비용 소모
// 건설 비용 소모
ch->PointChange(POINT_GOLD, -t->dwPrice);
// 아이템 자재 사용하기
// 아이템 자재 사용하기
{
int i;
for (i = 0; i < OBJECT_MATERIAL_MAX_NUM; ++i)
@ -2958,8 +2958,8 @@ ACMD(do_build)
break;
case 'W' :
// 담장 세우기
// build (w)all 담장번호 담장크기 대문동 대문서 대문남 대문북
// 담장 세우기
// build (w)all 담장번호 담장크기 대문동 대문서 대문남 대문북
if (GMLevel > GM_PLAYER)
{
@ -2993,8 +2993,8 @@ ACMD(do_build)
break;
case 'E' :
// 담장 지우기
// build (e)rase 담장셋ID
// 담장 지우기
// build (e)rase 담장셋ID
if (GMLevel > GM_PLAYER)
{
one_argument(line, arg1, sizeof(arg1));
@ -3051,7 +3051,7 @@ ACMD(do_horse_level)
if (NULL == victim)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("존재하지 않는 캐릭터 입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This character does not exist."));
return;
}
@ -3235,17 +3235,17 @@ ACMD(do_end_duel)
LPCHARACTER pChar = CHARACTER_MANAGER::instance().FindPC(szName);
if (pChar == NULL)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("존재하지 않는 캐릭터 입니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("This character does not exist."));
return;
}
if (CArenaManager::instance().EndDuel(pChar->GetPlayerID()) == false)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 강제 종료 실패"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Duel has not been successfully cancelled."));
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 강제 종료 성공"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Duel cancelled successfully."));
}
}
@ -3268,7 +3268,7 @@ ACMD(do_duel)
if (!str_to_number(minute, szMinute))
{
// 캐나다는 기본 10분.
// 캐나다는 기본 10분.
if (LC_IsCanada() == true)
{
minute = 10;
@ -3301,7 +3301,7 @@ ACMD(do_duel)
}
else
{
pChar1->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티에서 나가셨습니다."));
pChar1->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Group] You have left the group."));
pParty->Quit(pChar1->GetPlayerID());
}
}
@ -3315,23 +3315,23 @@ ACMD(do_duel)
}
else
{
pChar2->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티에서 나가셨습니다."));
pChar2->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Group] You have left the group."));
pParty->Quit(pChar2->GetPlayerID());
}
}
if (CArenaManager::instance().StartDuel(pChar1, pChar2, set, minute) == true)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련이 성공적으로 시작 되었습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The duel has been successfully started."));
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련 시작에 문제가 있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("There is a problem with initiating the duel."));
}
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련자가 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("There are no combatants."));
}
}
@ -3346,7 +3346,7 @@ ACMD(do_stat_plus_amount)
if (ch->IsPolymorphed())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot change your status while you are transformed."));
return;
}
@ -3354,7 +3354,7 @@ ACMD(do_stat_plus_amount)
if (nRemainPoint <= 0)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("남은 스탯 포인트가 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("No status points left."));
return;
}
@ -3363,40 +3363,40 @@ ACMD(do_stat_plus_amount)
if (nRemainPoint < nPoint)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("남은 스탯 포인트가 적습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Remaining status points are too low."));
return;
}
if (nPoint < 0)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("값을 잘못 입력하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You entered an incorrect value."));
return;
}
switch (subcmd)
{
case POINT_HT : // 체력
case POINT_HT : // 체력
if (nPoint + ch->GetPoint(POINT_HT) > 90)
{
nPoint = 90 - ch->GetPoint(POINT_HT);
}
break;
case POINT_IQ : // 지능
case POINT_IQ : // 지능
if (nPoint + ch->GetPoint(POINT_IQ) > 90)
{
nPoint = 90 - ch->GetPoint(POINT_IQ);
}
break;
case POINT_ST : // 근력
case POINT_ST : // 근력
if (nPoint + ch->GetPoint(POINT_ST) > 90)
{
nPoint = 90 - ch->GetPoint(POINT_ST);
}
break;
case POINT_DX : // 민첩
case POINT_DX : // 민첩
if (nPoint + ch->GetPoint(POINT_DX) > 90)
{
nPoint = 90 - ch->GetPoint(POINT_DX);
@ -3404,7 +3404,7 @@ ACMD(do_stat_plus_amount)
break;
default :
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("명령어의 서브 커맨드가 잘못 되었습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Suborder or the Order is incorrect."));
return;
break;
}
@ -3437,7 +3437,7 @@ ACMD(do_break_marriage)
str_to_number(pids.pid1, arg1);
str_to_number(pids.pid2, arg2);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("플레이어 %d 와 플레이어 %d를 파혼시킵니다.."), pids.pid1, pids.pid2);
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Broken contract between player %d and player %d."), pids.pid1, pids.pid2);
db_clientdesc->DBPacket(HEADER_GD_BREAK_MARRIAGE, 0, &pids, sizeof(pids));
}
@ -3471,8 +3471,8 @@ struct FCountInMap
ACMD(do_threeway_war_info)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("각제국 진행 정보"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("선택 맵 정보 성지 %d 통로 %d %d %d"), GetSungziMapIndex(), GetPassMapIndex(1), GetPassMapIndex(2), GetPassMapIndex(3));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Information for the Kingdoms"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Choose the Map Information of the Holy Land %d Entrance %d %d %d"), GetSungziMapIndex(), GetPassMapIndex(1), GetPassMapIndex(2), GetPassMapIndex(3));
ch->ChatPacket(CHAT_TYPE_INFO, "ThreewayPhase %d", CThreeWayWar::instance().GetRegenFlag());
for (int n = 1; n < 4; ++n)
@ -3495,7 +3495,7 @@ ACMD(do_threeway_war_info)
ACMD(do_threeway_war_myinfo)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("나의 삼거리 진행정보"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Information about the status of the kingdom battle"));
ch->ChatPacket(CHAT_TYPE_INFO, "Deadcount %d",
CThreeWayWar::instance().GetReviveTokenForPlayer(ch->GetPlayerID()));
}
@ -3577,7 +3577,7 @@ ACMD(do_check_monarch_money)
str_to_number(empire, arg1);
int NationMoney = CMonarch::instance().GetMoney(empire);
ch->ChatPacket(CHAT_TYPE_INFO, "국고: %d 원", NationMoney);
ch->ChatPacket(CHAT_TYPE_INFO, "Treasury: %d Yang", NationMoney);
}
ACMD(do_reset_subskill)
@ -3856,13 +3856,13 @@ ACMD(do_set_stat)
{
if (tch->IsPolymorphed())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot change your status while you are transformed."));
return;
}
if (subcmd != POINT_HT && subcmd != POINT_IQ && subcmd != POINT_ST && subcmd != POINT_DX)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("명령어의 서브 커맨드가 잘못 되었습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Suborder or the Order is incorrect."));
return;
}
int nRemainPoint = tch->GetPoint(POINT_STAT);
@ -3916,7 +3916,7 @@ ACMD(do_set_stat)
if (nRemainPoint < nChangeAmount)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("남은 스탯 포인트가 적습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Remaining status points are too low."));
return;
}
@ -4153,8 +4153,8 @@ ACMD (do_attr_full_set)
case JOB_SURA:
case JOB_SHAMAN:
{
// 무사 몸빵 셋팅.
// 이것만 나와 있어서 임시로 모든 직군 다 이런 속성 따름.
// 무사 몸빵 셋팅.
// 이것만 나와 있어서 임시로 모든 직군 다 이런 속성 따름.
item = ch->GetWear(WEAR_HEAD);
if (item != NULL)
{
@ -4261,7 +4261,7 @@ ACMD (do_use_item)
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, "아이템이 없어서 착용할 수 없어.");
ch->ChatPacket(CHAT_TYPE_INFO, "I can't wear it because I don't have the item.");
}
}

View File

@ -19,16 +19,16 @@ ACMD(do_oxevent_log)
{
if ( COXEventManager::instance().LogWinner() == false )
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX이벤트의 나머지 인원을 기록하였습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX The other event participants are being noted down."));
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX이벤트의 나머지 인원 기록을 실패했습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX The other event participants have not been noted down."));
}
}
ACMD(do_oxevent_get_attender)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("현재 남은 참가자수 : %d"), COXEventManager::instance().GetAttenderCount());
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Number of other participants : %d"), COXEventManager::instance().GetAttenderCount());
}

View File

@ -38,7 +38,7 @@ WORD teen_port = 0;
BYTE g_bChannel = 0;
int passes_per_sec = 25;
int save_event_second_cycle = passes_per_sec * 120; // 3분
int save_event_second_cycle = passes_per_sec * 120; // 3분
int ping_event_second_cycle = passes_per_sec * 60;
bool g_bNoMoreClient = false;
bool g_bNoRegen = false;
@ -100,17 +100,17 @@ int SPEEDHACK_LIMIT_COUNT = 50;
int SPEEDHACK_LIMIT_BONUS = 80;
int g_iSyncHackLimitCount = 20; // 10 -> 20 2013 09 11 CYH
//시야 = VIEW_RANGE + VIEW_BONUS_RANGE
//VIEW_BONUSE_RANGE : 클라이언트와 시야 처리에서너무 딱 떨어질경우 문제가 발생할수있어 500CM의 여분을 항상준다.
//시야 = VIEW_RANGE + VIEW_BONUS_RANGE
//VIEW_BONUSE_RANGE : 클라이언트와 시야 처리에서너무 딱 떨어질경우 문제가 발생할수있어 500CM의 여분을 항상준다.
int VIEW_RANGE = 5000;
int VIEW_BONUS_RANGE = 500;
int g_server_id = 0;
string g_strWebMallURL = "www.metin2.de";
unsigned int g_uiSpamBlockDuration = 60 * 15; // 기본 15분
unsigned int g_uiSpamBlockScore = 100; // 기본 100점
unsigned int g_uiSpamReloadCycle = 60 * 10; // 기본 10분
unsigned int g_uiSpamBlockDuration = 60 * 15; // 기본 15분
unsigned int g_uiSpamBlockScore = 100; // 기본 100점
unsigned int g_uiSpamReloadCycle = 60 * 10; // 기본 10분
bool g_bCheckMultiHack = true;
@ -119,8 +119,8 @@ int g_iSpamBlockMaxLevel = 10;
void LoadStateUserCount();
void LoadValidCRCList();
bool LoadClientVersion();
bool g_protectNormalPlayer = false; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
bool g_noticeBattleZone = false; // 중립지대에 입장하면 안내메세지를 알려줌
bool g_protectNormalPlayer = false; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
bool g_noticeBattleZone = false; // 중립지대에 입장하면 안내메세지를 알려줌
int gPlayerMaxLevel = 99;
@ -316,7 +316,7 @@ void config_init(const string& st_localeServiceName)
}
char db_host[2][64], db_user[2][64], db_pwd[2][64], db_db[2][64];
// ... 아... db_port는 이미 있는데... 네이밍 어찌해야함...
// ... 아... db_port는 이미 있는데... 네이밍 어찌해야함...
int mysql_db_port[2];
for (int n = 0; n < 2; ++n)
@ -337,9 +337,9 @@ void config_init(const string& st_localeServiceName)
*log_db = '\0';
// DB에서 로케일정보를 세팅하기위해서는 다른 세팅값보다 선행되어서
// DB정보만 읽어와 로케일 세팅을 한후 다른 세팅을 적용시켜야한다.
// 이유는 로케일관련된 초기화 루틴이 곳곳에 존재하기 때문.
// DB에서 로케일정보를 세팅하기위해서는 다른 세팅값보다 선행되어서
// DB정보만 읽어와 로케일 세팅을 한후 다른 세팅을 적용시켜야한다.
// 이유는 로케일관련된 초기화 루틴이 곳곳에 존재하기 때문.
bool isCommonSQL = false;
bool isPlayerSQL = false;
@ -475,7 +475,7 @@ void config_init(const string& st_localeServiceName)
}
}
//처리가 끝났으니 파일을 닫자.
//처리가 끝났으니 파일을 닫자.
fclose(fpOnlyForDB);
// CONFIG_SQL_INFO_ERROR
@ -501,7 +501,7 @@ void config_init(const string& st_localeServiceName)
exit(EXIT_FAILURE);
}
// Common DB 가 Locale 정보를 가지고 있기 때문에 가장 먼저 접속해야 한다.
// Common DB 가 Locale 정보를 가지고 있기 때문에 가장 먼저 접속해야 한다.
AccountDB::instance().Connect(db_host[1], mysql_db_port[1], db_user[1], db_pwd[1], db_db[1]);
if (false == AccountDB::instance().IsConnected())
@ -512,8 +512,8 @@ void config_init(const string& st_localeServiceName)
SPDLOG_INFO("CommonSQL connected");
// 로케일 정보를 가져오자
// <경고> 쿼리문에 절대 조건문(WHERE) 달지 마세요. (다른 지역에서 문제가 생길수 있습니다)
// 로케일 정보를 가져오자
// <경고> 쿼리문에 절대 조건문(WHERE) 달지 마세요. (다른 지역에서 문제가 생길수 있습니다)
{
char szQuery[512];
snprintf(szQuery, sizeof(szQuery), "SELECT mKey, mValue FROM locale");
@ -530,7 +530,7 @@ void config_init(const string& st_localeServiceName)
while (NULL != (row = mysql_fetch_row(pMsg->Get()->pSQLResult)))
{
// 로케일 세팅
// 로케일 세팅
if (strcasecmp(row[0], "LOCALE") == 0)
{
if (LocaleService_Init(row[1]) == false)
@ -542,15 +542,15 @@ void config_init(const string& st_localeServiceName)
}
}
// 로케일 정보를 COMMON SQL에 세팅해준다.
// 참고로 g_stLocale 정보는 LocaleService_Init() 내부에서 세팅된다.
// 로케일 정보를 COMMON SQL에 세팅해준다.
// 참고로 g_stLocale 정보는 LocaleService_Init() 내부에서 세팅된다.
SPDLOG_INFO("Setting DB to locale {}", g_stLocale.c_str());
AccountDB::instance().SetLocale(g_stLocale);
AccountDB::instance().ConnectAsync(db_host[1], mysql_db_port[1], db_user[1], db_pwd[1], db_db[1], g_stLocale.c_str());
// Player DB 접속
// Player DB 접속
DBManager::instance().Connect(db_host[0], mysql_db_port[0], db_user[0], db_pwd[0], db_db[0]);
if (!DBManager::instance().IsConnected())
@ -561,9 +561,9 @@ void config_init(const string& st_localeServiceName)
SPDLOG_INFO("PlayerSQL connected");
if (false == g_bAuthServer) // 인증 서버가 아닐 경우
if (false == g_bAuthServer) // 인증 서버가 아닐 경우
{
// Log DB 접속
// Log DB 접속
LogManager::instance().Connect(log_host, log_port, log_user, log_pwd, log_db);
if (!LogManager::instance().IsConnected())
@ -578,8 +578,8 @@ void config_init(const string& st_localeServiceName)
}
// SKILL_POWER_BY_LEVEL
// 스트링 비교의 문제로 인해서 AccountDB::instance().SetLocale(g_stLocale) 후부터 한다.
// 물론 국내는 별로 문제가 안된다(해외가 문제)
// 스트링 비교의 문제로 인해서 AccountDB::instance().SetLocale(g_stLocale) 후부터 한다.
// 물론 국내는 별로 문제가 안된다(해외가 문제)
{
char szQuery[256];
snprintf(szQuery, sizeof(szQuery), "SELECT mValue FROM locale WHERE mKey='SKILL_POWER_BY_LEVEL'");
@ -619,13 +619,13 @@ void config_init(const string& st_localeServiceName)
}
}
// 종족별 스킬 세팅
// 종족별 스킬 세팅
for (int job = 0; job < JOB_MAX_NUM * 2; ++job)
{
snprintf(szQuery, sizeof(szQuery), "SELECT mValue from locale where mKey='SKILL_POWER_BY_LEVEL_TYPE%d' ORDER BY CAST(mValue AS unsigned)", job);
std::unique_ptr<SQLMsg> pMsg(AccountDB::instance().DirectQuery(szQuery));
// 세팅이 안되어있으면 기본테이블을 사용한다.
// 세팅이 안되어있으면 기본테이블을 사용한다.
if (pMsg->Get()->uiNumRows == 0)
{
CTableBySkill::instance().SetSkillPowerByLevelFromType(job, aiBaseSkillPowerByLevelTable);
@ -995,7 +995,7 @@ void config_init(const string& st_localeServiceName)
TOKEN("spam_block_reload_cycle")
{
str_to_number(g_uiSpamReloadCycle, value_string);
g_uiSpamReloadCycle = std::max<int>(60, g_uiSpamReloadCycle); // 최소 1분
g_uiSpamReloadCycle = std::max<int>(60, g_uiSpamReloadCycle); // 최소 1분
}
TOKEN("check_multihack")
@ -1205,7 +1205,7 @@ void CheckClientVersion()
//if (0 != g_stClientVersion.compare(d->GetClientVersion()) )
if (version > date)
{
d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("클라이언트 버전이 틀려 로그아웃 됩니다. 정상적으로 패치 후 접속하세요."));
d->GetCharacter()->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("You do not have the correct client version. Please install the normal patch."));
d->DelayedDisconnect(10);
}
}

View File

@ -35,7 +35,7 @@ extern bool china_event_server;
extern bool g_bNoMoreClient;
extern bool g_bNoRegen;
extern bool g_bTrafficProfileOn; ///< true 이면 TrafficProfiler 를 켠다.
extern bool g_bTrafficProfileOn; ///< true 이면 TrafficProfiler 를 켠다.
extern BYTE g_bChannel;
@ -98,8 +98,8 @@ extern int VIEW_RANGE;
extern int VIEW_BONUS_RANGE;
extern bool g_bCheckMultiHack;
extern bool g_protectNormalPlayer; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
extern bool g_noticeBattleZone; // 중립지대에 입장하면 안내메세지를 알려줌
extern bool g_protectNormalPlayer; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
extern bool g_noticeBattleZone; // 중립지대에 입장하면 안내메세지를 알려줌
extern DWORD g_GoldDropTimeLimitValue;

View File

@ -14,7 +14,7 @@ TJobInitialPoints JobInitialPoints[JOB_MAX_NUM] =
}
*/
{
// str con dex int 초기HP 초기SP CON/HP INT/SP HP랜덤/lv MP랜덤/lv 초기stam stam/con stam/lv
// str con dex int 초기HP 초기SP CON/HP INT/SP HP랜덤/lv MP랜덤/lv 초기stam stam/con stam/lv
{ 6, 4, 3, 3, 600, 200, 40, 20, 36, 44, 18, 22, 800, 5, 1, 3 }, // JOB_WARRIOR 16
{ 4, 3, 6, 3, 650, 200, 40, 20, 36, 44, 18, 22, 800, 5, 1, 3 }, // JOB_ASSASSIN 16
{ 5, 3, 3, 5, 650, 200, 40, 20, 36, 44, 18, 22, 800, 5, 1, 3 }, // JOB_SURA 16
@ -162,7 +162,7 @@ const DWORD exp_table_euckr[PLAYER_EXP_TABLE_MAX + 1] =
1676898443,
1844588288,
2029047116,
2100000000, // 99 99레벨일 때 필요경험치 (100레벨이 되기 위한)
2100000000, // 99 99레벨일 때 필요경험치 (100레벨이 되기 위한)
2100000000, // 100
2100000000,
2100000000,
@ -287,7 +287,7 @@ const DWORD exp_table_common[PLAYER_EXP_TABLE_MAX + 1] =
1676898443,
1844588288,
2029047116,
2050000000, // 99레벨 일 때 필요경험치 (100레벨이 되기 위한)
2050000000, // 99레벨 일 때 필요경험치 (100레벨이 되기 위한)
2150000000, // 100
2210000000,
2250000000,
@ -412,7 +412,7 @@ const DWORD exp_table_newcibn[PLAYER_EXP_TABLE_MAX + 1 ] =
2000000000,
2000000000,
2000000000,
2000000000, // 99 99레벨일 때 필요경험치 (100레벨이 되기 위한).. 현재 CIBN이 어떻게 운영하고 있는 지 모르니 신규 테이블을 쓰지 않고 기존값 계속 연장 유지
2000000000, // 99 99레벨일 때 필요경험치 (100레벨이 되기 위한).. 현재 CIBN이 어떻게 운영하고 있는 지 모르니 신규 테이블을 쓰지 않고 기존값 계속 연장 유지
2000000000, // 100
2000000000,
2000000000,
@ -439,8 +439,8 @@ const DWORD exp_table_newcibn[PLAYER_EXP_TABLE_MAX + 1 ] =
const int * aiPercentByDeltaLev = NULL;
const int * aiPercentByDeltaLevForBoss = NULL;
// 적과 나와의 레벨차이에 의한 계산에 사용되는 테이블
// MIN(MAX_EXP_DELTA_OF_LEV - 1, (적렙 + 15) - 내렙))
// 적과 나와의 레벨차이에 의한 계산에 사용되는 테이블
// MIN(MAX_EXP_DELTA_OF_LEV - 1, (적렙 + 15) - 내렙))
const int aiPercentByDeltaLevForBoss_euckr[MAX_EXP_DELTA_OF_LEV] =
{
1, // -15 0
@ -720,7 +720,7 @@ const DWORD guild_exp_table[GUILD_MAX_LEVEL+1] =
42000000UL
};
// INTERNATIONAL_VERSION 길드경험치
// INTERNATIONAL_VERSION 길드경험치
const DWORD guild_exp_table2[GUILD_MAX_LEVEL+1] =
{
0,
@ -745,7 +745,7 @@ const DWORD guild_exp_table2[GUILD_MAX_LEVEL+1] =
4000000UL,
16800000UL
};
// END_OF_INTERNATIONAL_VERSION 길드경험치
// END_OF_INTERNATIONAL_VERSION 길드경험치
const int aiMobEnchantApplyIdx[MOB_ENCHANTS_MAX_NUM] =
{
@ -899,16 +899,16 @@ const TApplyInfo aApplyInfo[MAX_APPLY_NUM] =
{ POINT_PC_BANG_DROP_BONUS }, // 76
// END_PC_BANG_ITEM_ADD
{ POINT_NONE, }, // 77 사용시 HP 소모 APPLY_EXTRACT_HP_PCT
{ POINT_NONE, }, // 77 사용시 HP 소모 APPLY_EXTRACT_HP_PCT
{ POINT_RESIST_WARRIOR, }, // 78 무사에게 저항 APPLY_RESIST_WARRIOR
{ POINT_RESIST_ASSASSIN, }, // 79 자객에게 저항 APPLY_RESIST_ASSASSIN
{ POINT_RESIST_SURA, }, // 80 수라에게 저항 APPLY_RESIST_SURA
{ POINT_RESIST_SHAMAN, }, // 81 무당에게 저항 APPLY_RESIST_SHAMAN
{ POINT_ENERGY }, // 82 기력
{ POINT_DEF_GRADE }, // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
{ POINT_COSTUME_ATTR_BONUS }, // 84 코스튬에 붙은 속성에 대해서만 보너스를 주는 기력
{ POINT_MAGIC_ATT_BONUS_PER }, // 85 마법 공격력 +x%
{ POINT_RESIST_WARRIOR, }, // 78 무사에게 저항 APPLY_RESIST_WARRIOR
{ POINT_RESIST_ASSASSIN, }, // 79 자객에게 저항 APPLY_RESIST_ASSASSIN
{ POINT_RESIST_SURA, }, // 80 수라에게 저항 APPLY_RESIST_SURA
{ POINT_RESIST_SHAMAN, }, // 81 무당에게 저항 APPLY_RESIST_SHAMAN
{ POINT_ENERGY }, // 82 기력
{ POINT_DEF_GRADE }, // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
{ POINT_COSTUME_ATTR_BONUS }, // 84 코스튬에 붙은 속성에 대해서만 보너스를 주는 기력
{ POINT_MAGIC_ATT_BONUS_PER }, // 85 마법 공격력 +x%
{ POINT_MELEE_MAGIC_ATT_BONUS_PER }, // 86 APPLY_MELEE_MAGIC_ATTBONUS_PER
{ POINT_RESIST_ICE, }, // APPLY_RESIST_ICE, 87
{ POINT_RESIST_EARTH, }, // APPLY_RESIST_EARTH, 88
@ -1077,19 +1077,19 @@ const SStoneDropInfo aStoneDrop[STONE_INFO_MAX_NUM] =
const char * c_apszEmpireNames[EMPIRE_MAX_NUM] =
{
"전제국",
"신수국",
"천조국",
"진노국"
"All kingdoms",
"Shinsoo Kingdom",
"Chunjo Kingdom",
"Jinno Kingdom"
};
const char * c_apszPrivNames[MAX_PRIV_NUM] =
{
"",
"아이템이 나올 확률",
"돈이 나올 확률",
"돈 대박이 나올 확률",
"경험치 배율",
"Item drop rate in percent",
"Yang drop rate in percent",
"Yang rain drop rate",
"Experience percentage",
};
const int aiPolymorphPowerByLevel[SKILL_MAX_LEVEL + 1] =
@ -1154,28 +1154,28 @@ TGuildWarInfo KOR_aGuildWarInfo[GUILD_WAR_TYPE_MAX_NUM] =
};
//
// 악세서리 소켓용 수치들
// 악세서리 소켓용 수치들
//
// 다이아몬드로 소켓을 추가할 때 확률
// 다이아몬드로 소켓을 추가할 때 확률
const int aiAccessorySocketAddPct[ITEM_ACCESSORY_SOCKET_MAX_NUM] =
{
50, 50, 50
};
// 악세서리 수치 값의 몇%만큼의 성능을 추가하는지
// 악세서리 수치 값의 몇%만큼의 성능을 추가하는지
const int aiAccessorySocketEffectivePct[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1] =
{
0, 10, 20, 40
};
// 소켓 지속시간 24, 12, 6
// 소켓 지속시간 24, 12, 6
const int aiAccessorySocketDegradeTime[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1] =
{
0, 3600 * 24, 3600 * 12, 3600 * 6
};
// 소켓 장착 성공률
// 소켓 장착 성공률
const int aiAccessorySocketPutPct[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1] =
{
90, 80, 70, 0
@ -1275,10 +1275,10 @@ TValueName c_aApplyTypeNames[] =
{ "RESIST_ASSASSIN", APPLY_RESIST_ASSASSIN},
{ "RESIST_SURA", APPLY_RESIST_SURA},
{ "RESIST_SHAMAN", APPLY_RESIST_SHAMAN},
// by mhh game/affect.h 정의되어있음. INFINITE_AFFECT_DURATION = 0x1FFFFFFF
// by mhh game/affect.h 정의되어있음. INFINITE_AFFECT_DURATION = 0x1FFFFFFF
{ "INFINITE_AFFECT_DURATION", 0x1FFFFFFF },
{ "ENERGY", APPLY_ENERGY }, // 기력
{ "COSTUME_ATTR_BONUS", APPLY_COSTUME_ATTR_BONUS }, // 기력
{ "ENERGY", APPLY_ENERGY }, // 기력
{ "COSTUME_ATTR_BONUS", APPLY_COSTUME_ATTR_BONUS }, // 기력
{ "MAGIC_ATTBONUS_PER", APPLY_MAGIC_ATTBONUS_PER },
{ "MELEE_MAGIC_ATTBONUS_PER", APPLY_MELEE_MAGIC_ATTBONUS_PER },

View File

@ -13,7 +13,7 @@ enum EMonsterChatState
typedef struct SMobRankStat
{
int iGoldPercent; // 돈이 나올 확률
int iGoldPercent; // 돈이 나올 확률
} TMobRankStat;
typedef struct SMobStat
@ -124,7 +124,7 @@ extern const int aiMobResistsApplyIdx[MOB_RESISTS_MAX_NUM];
extern const int aSkillAttackAffectProbByRank[MOB_RANK_MAX_NUM];
extern const int aiItemMagicAttributePercentHigh[ITEM_ATTRIBUTE_MAX_LEVEL]; // 1개까지
extern const int aiItemMagicAttributePercentHigh[ITEM_ATTRIBUTE_MAX_LEVEL]; // 1개까지
extern const int aiItemMagicAttributePercentLow[ITEM_ATTRIBUTE_MAX_LEVEL];
extern const int aiItemAttributeAddPercent[ITEM_ATTRIBUTE_MAX_NUM];

View File

@ -37,11 +37,11 @@ static bool s_isInitializedCubeMaterialInformation = false;
/*--------------------------------------------------------*/
enum ECubeResultCategory
{
CUBE_CATEGORY_POTION, // 약초, 진액 등등.. (포션으로 특정할 수 없으니 사용 안 함. 약초같은건 다 걍 기타)
CUBE_CATEGORY_WEAPON, // 무기
CUBE_CATEGORY_ARMOR, // 방어구
CUBE_CATEGORY_ACCESSORY, // 장신구
CUBE_CATEGORY_ETC, // 기타 등등...
CUBE_CATEGORY_POTION, // 약초, 진액 등등.. (포션으로 특정할 수 없으니 사용 안 함. 약초같은건 다 걍 기타)
CUBE_CATEGORY_WEAPON, // 무기
CUBE_CATEGORY_ARMOR, // 방어구
CUBE_CATEGORY_ACCESSORY, // 장신구
CUBE_CATEGORY_ETC, // 기타 등등...
};
typedef std::vector<CUBE_VALUE> TCubeValueVector;
@ -53,12 +53,12 @@ struct SCubeMaterialInfo
bHaveComplicateMaterial = false;
};
CUBE_VALUE reward; // 보상이 뭐냐
TCubeValueVector material; // 재료들은 뭐냐
DWORD gold; // 돈은 얼마드냐
TCubeValueVector complicateMaterial; // 복잡한-_- 재료들
CUBE_VALUE reward; // 보상이 뭐냐
TCubeValueVector material; // 재료들은 뭐냐
DWORD gold; // 돈은 얼마드냐
TCubeValueVector complicateMaterial; // 복잡한-_- 재료들
// .. 클라이언트에서 재료를 보여주기 위하여 약속한 포맷
// .. 클라이언트에서 재료를 보여주기 위하여 약속한 포맷
// 72723,1&72724,2&72730,1
// 52001,1|52002,1|52003,1&72723,1&72724,5
// => ( 52001,1 or 52002,1 or 52003,1 ) and 72723,1 and 72724,5
@ -75,13 +75,13 @@ struct SItemNameAndLevel
};
// 자료구조나 이런거 병신인건 이해좀... 누구땜에 영혼이 없는 상태에서 만들었씀
// 자료구조나 이런거 병신인건 이해좀... 누구땜에 영혼이 없는 상태에서 만들었씀
typedef std::vector<SCubeMaterialInfo> TCubeResultList;
typedef std::unordered_map<DWORD, TCubeResultList> TCubeMapByNPC; // 각각의 NPC별로 어떤 걸 만들 수 있고 재료가 뭔지...
typedef std::unordered_map<DWORD, std::string> TCubeResultInfoTextByNPC; // 각각의 NPC별로 만들 수 있는 목록을 정해진 포맷으로 정리한 정보
typedef std::unordered_map<DWORD, TCubeResultList> TCubeMapByNPC; // 각각의 NPC별로 어떤 걸 만들 수 있고 재료가 뭔지...
typedef std::unordered_map<DWORD, std::string> TCubeResultInfoTextByNPC; // 각각의 NPC별로 만들 수 있는 목록을 정해진 포맷으로 정리한 정보
TCubeMapByNPC cube_info_map;
TCubeResultInfoTextByNPC cube_result_info_map_by_npc; // 네이밍 존나 병신같다 ㅋㅋㅋ
TCubeResultInfoTextByNPC cube_result_info_map_by_npc; // 네이밍 존나 병신같다 ㅋㅋㅋ
class CCubeMaterialInfoHelper
{
@ -92,7 +92,7 @@ public:
/*--------------------------------------------------------*/
/* STATIC FUNCTIONS */
/*--------------------------------------------------------*/
// 필요한 아이템 개수를 가지고있는가?
// 필요한 아이템 개수를 가지고있는가?
static bool FN_check_item_count (LPITEM *items, DWORD item_vnum, int need_count)
{
int count = 0;
@ -111,7 +111,7 @@ static bool FN_check_item_count (LPITEM *items, DWORD item_vnum, int need_count)
return (count>=need_count);
}
// 큐브내의 재료를 지운다.
// 큐브내의 재료를 지운다.
static void FN_remove_material (LPITEM *items, DWORD item_vnum, int need_count)
{
int count = 0;
@ -170,7 +170,7 @@ static bool FN_check_valid_npc( WORD vnum )
return false;
}
// 큐브데이타가 올바르게 초기화 되었는지 체크한다.
// 큐브데이타가 올바르게 초기화 되었는지 체크한다.
static bool FN_check_cube_data (CUBE_DATA *cube_data)
{
DWORD i = 0;
@ -204,10 +204,10 @@ CUBE_DATA::CUBE_DATA()
this->gold = 0;
}
// 필요한 재료의 수량을 만족하는지 체크한다.
// 필요한 재료의 수량을 만족하는지 체크한다.
bool CUBE_DATA::can_make_item (LPITEM *items, WORD npc_vnum)
{
// 필요한 재료, 수량을 만족하는지 체크한다.
// 필요한 재료, 수량을 만족하는지 체크한다.
DWORD i, end_index;
DWORD need_vnum;
int need_count;
@ -235,7 +235,7 @@ bool CUBE_DATA::can_make_item (LPITEM *items, WORD npc_vnum)
return true;
}
// 큐브를 돌렸을때 나오는 아이템의 종류를 결정함
// 큐브를 돌렸을때 나오는 아이템의 종류를 결정함
CUBE_VALUE* CUBE_DATA::reward_value ()
{
int end_index = 0;
@ -248,7 +248,7 @@ CUBE_VALUE* CUBE_DATA::reward_value ()
return &this->reward[reward_index];
}
// 큐브에 들어있는 재료를 지운다
// 큐브에 들어있는 재료를 지운다
void CUBE_DATA::remove_material (LPCHARACTER ch)
{
DWORD i, end_index;
@ -281,7 +281,7 @@ void Cube_clean_item (LPCHARACTER ch)
}
}
// 큐브창 열기
// 큐브창 열기
void Cube_open (LPCHARACTER ch)
{
if (false == s_isInitializedCubeMaterialInformation)
@ -308,12 +308,12 @@ void Cube_open (LPCHARACTER ch)
if (ch->IsCubeOpen())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이미 제조창이 열려있습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The Build window is already open."));
return;
}
if ( ch->GetExchange() || ch->GetMyShop() || ch->GetShopOwner() || ch->IsOpenSafebox() || ch->IsCubeOpen() )
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중(창고,교환,상점)에는 사용할 수 없습니다."));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot build something while another trade/storeroom window is open."));
return;
}
@ -331,7 +331,7 @@ void Cube_open (LPCHARACTER ch)
ch->ChatPacket(CHAT_TYPE_COMMAND, "cube open %d", npc->GetRaceNum());
}
// 큐브 캔슬
// 큐브 캔슬
void Cube_close (LPCHARACTER ch)
{
RETURN_IF_CUBE_IS_NOT_OPENED(ch);
@ -426,7 +426,7 @@ bool Cube_load (const char *file)
}
else TOKEN("gold")
{
// 제조에 필요한 금액
// 제조에 필요한 금액
cube_data->gold = value1;
}
else TOKEN("end")
@ -504,10 +504,10 @@ static bool FN_update_cube_status(LPCHARACTER ch)
// return new item
bool Cube_make (LPCHARACTER ch)
{
// 주어진 아이템을 필요로하는 조합을 찾는다. (큐브데이타로 칭함)
// 큐브 데이타가 있다면 아이템의 재료를 체크한다.
// 새로운 아이템을 만든다.
// 새로운 아이템 지급
// 주어진 아이템을 필요로하는 조합을 찾는다. (큐브데이타로 칭함)
// 큐브 데이타가 있다면 아이템의 재료를 체크한다.
// 새로운 아이템을 만든다.
// 새로운 아이템 지급
LPCHARACTER npc;
int percent_number = 0;
@ -517,7 +517,7 @@ bool Cube_make (LPCHARACTER ch)
if (!(ch)->IsCubeOpen())
{
(ch)->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("제조창이 열려있지 않습니다"));
(ch)->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The build window is not open."));
return false;
}
@ -532,29 +532,29 @@ bool Cube_make (LPCHARACTER ch)
if (NULL == cube_proto)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("제조 재료가 부족합니다"));
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You do not have the right material."));
return false;
}
if (ch->GetGold() < cube_proto->gold)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("돈이 부족하거나 아이템이 제자리에 없습니다.")); // 이 텍스트는 이미 널리 쓰이는거라 추가번역 필요 없음
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Not enough Yang or not enough space in the inventory.")); // 이 텍스트는 이미 널리 쓰이는거라 추가번역 필요 없음
return false;
}
CUBE_VALUE *reward_value = cube_proto->reward_value();
// 사용되었던 재료아이템 삭제
// 사용되었던 재료아이템 삭제
cube_proto->remove_material (ch);
// 제조시 필요한 골드 차감
// 제조시 필요한 골드 차감
if (0 < cube_proto->gold)
ch->PointChange(POINT_GOLD, -(cube_proto->gold), false);
percent_number = Random::get(1,100);
if ( percent_number<=cube_proto->percent)
{
// 성공
// 성공
ch->ChatPacket(CHAT_TYPE_COMMAND, "cube success %d %d", reward_value->vnum, reward_value->count);
new_item = ch->AutoGiveItem(reward_value->vnum, reward_value->count);
@ -564,8 +564,8 @@ bool Cube_make (LPCHARACTER ch)
}
else
{
// 실패
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("제조에 실패하였습니다.")); // 2012.11.12 새로 추가된 메세지 (locale_string.txt 에 추가해야 함)
// 실패
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Manufacturing failed.")); // 2012.11.12 새로 추가된 메세지 (locale_string.txt 에 추가해야 함)
ch->ChatPacket(CHAT_TYPE_COMMAND, "cube fail");
LogManager::instance().CubeLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(),
reward_value->vnum, 0, 0, 0);
@ -576,7 +576,7 @@ bool Cube_make (LPCHARACTER ch)
}
// 큐브에 있는 아이템들을 표시
// 큐브에 있는 아이템들을 표시
void Cube_show_list (LPCHARACTER ch)
{
LPITEM *cube_item;
@ -597,13 +597,13 @@ void Cube_show_list (LPCHARACTER ch)
}
// 인벤토리에 있는 아이템을 큐브에 등록
// 인벤토리에 있는 아이템을 큐브에 등록
void Cube_add_item (LPCHARACTER ch, int cube_index, int inven_index)
{
// 아이템이 있는가?
// 큐브내의 빈자리 찾기
// 큐브세팅
// 메시지 전송
// 아이템이 있는가?
// 큐브내의 빈자리 찾기
// 큐브세팅
// 메시지 전송
LPITEM item;
LPITEM *cube_item;
@ -620,7 +620,7 @@ void Cube_add_item (LPCHARACTER ch, int cube_index, int inven_index)
cube_item = ch->GetCubeItem();
// 이미 다른위치에 등록되었던 아이템이면 기존 indext삭제
// 이미 다른위치에 등록되었던 아이템이면 기존 indext삭제
for (int i=0; i<CUBE_MAX_NUM; ++i)
{
if (item==cube_item[i])
@ -636,14 +636,14 @@ void Cube_add_item (LPCHARACTER ch, int cube_index, int inven_index)
ch->ChatPacket(CHAT_TYPE_INFO, "cube[%d]: inventory[%d]: %s added",
cube_index, inven_index, item->GetName());
// 현재 상자에 올라온 아이템들로 무엇을 만들 수 있는지 클라이언트에 정보 전달
// 을 하고싶었으나 그냥 필요한 골드가 얼마인지 전달
// 현재 상자에 올라온 아이템들로 무엇을 만들 수 있는지 클라이언트에 정보 전달
// 을 하고싶었으나 그냥 필요한 골드가 얼마인지 전달
FN_update_cube_status(ch);
return;
}
// 큐브에있는 아이템을 제거
// 큐브에있는 아이템을 제거
void Cube_delete_item (LPCHARACTER ch, int cube_index)
{
LPITEM item;
@ -664,14 +664,14 @@ void Cube_delete_item (LPCHARACTER ch, int cube_index)
ch->ChatPacket(CHAT_TYPE_INFO, "cube[%d]: cube[%d]: %s deleted",
cube_index, item->GetCell(), item->GetName());
// 현재 상자에 올라온 아이템들로 무엇을 만들 수 있는지 클라이언트에 정보 전달
// 을 하고싶었으나 그냥 필요한 골드가 얼마인지 전달
// 현재 상자에 올라온 아이템들로 무엇을 만들 수 있는지 클라이언트에 정보 전달
// 을 하고싶었으나 그냥 필요한 골드가 얼마인지 전달
FN_update_cube_status(ch);
return;
}
// 아이템 이름을 통해서 순수 이름과 강화레벨을 분리하는 함수 (무쌍검+5 -> 무쌍검, 5)
// 아이템 이름을 통해서 순수 이름과 강화레벨을 분리하는 함수 (무쌍검+5 -> 무쌍검, 5)
SItemNameAndLevel SplitItemNameAndLevelFromName(const std::string& name)
{
int level = 0;
@ -705,7 +705,7 @@ bool FIsLessCubeValue(const CUBE_VALUE& a, const CUBE_VALUE& b)
void Cube_MakeCubeInformationText()
{
// 이제 정리된 큐브 결과 및 재료들의 정보로 클라이언트에 보내 줄 정보로 변환함.
// 이제 정리된 큐브 결과 및 재료들의 정보로 클라이언트에 보내 줄 정보로 변환함.
for (TCubeMapByNPC::iterator iter = cube_info_map.begin(); cube_info_map.end() != iter; ++iter)
{
const DWORD& npcVNUM = iter->first;
@ -717,13 +717,13 @@ void Cube_MakeCubeInformationText()
std::string& infoText = materialInfo.infoText;
// 이놈이 나쁜놈이야
// 이놈이 나쁜놈이야
if (0 < materialInfo.complicateMaterial.size())
{
std::sort(materialInfo.complicateMaterial.begin(), materialInfo.complicateMaterial.end(), FIsLessCubeValue);
std::sort(materialInfo.material.begin(), materialInfo.material.end(), FIsLessCubeValue);
//// 중복되는 재료들을 지움
//// 중복되는 재료들을 지움
for (TCubeValueVector::iterator iter = materialInfo.complicateMaterial.begin(); materialInfo.complicateMaterial.end() != iter; ++iter)
{
for (TCubeValueVector::iterator targetIter = materialInfo.material.begin(); materialInfo.material.end() != targetIter; ++targetIter)
@ -735,7 +735,7 @@ void Cube_MakeCubeInformationText()
}
}
// 72723,1 or 72725,1 or ... 이런 식의 약속된 포맷을 지키는 텍스트를 생성
// 72723,1 or 72725,1 or ... 이런 식의 약속된 포맷을 지키는 텍스트를 생성
for (TCubeValueVector::iterator iter = materialInfo.complicateMaterial.begin(); materialInfo.complicateMaterial.end() != iter; ++iter)
{
char tempBuffer[128];
@ -750,7 +750,7 @@ void Cube_MakeCubeInformationText()
infoText.push_back('&');
}
// 중복되지 않는 일반 재료들도 포맷 생성
// 중복되지 않는 일반 재료들도 포맷 생성
for (TCubeValueVector::iterator iter = materialInfo.material.begin(); materialInfo.material.end() != iter; ++iter)
{
char tempBuffer[128];
@ -760,7 +760,7 @@ void Cube_MakeCubeInformationText()
infoText.erase(infoText.size() - 1);
// 만들 때 골드가 필요하다면 골드정보 추가
// 만들 때 골드가 필요하다면 골드정보 추가
if (0 < materialInfo.gold)
{
char temp[128];
@ -779,7 +779,7 @@ bool Cube_InformationInitialize()
const std::vector<CUBE_VALUE>& rewards = cubeData->reward;
// 하드코딩 ㅈㅅ
// 하드코딩 ㅈㅅ
if (1 != rewards.size())
{
SPDLOG_ERROR("[CubeInfo] WARNING! Does not support multiple rewards (count: {})", rewards.size());
@ -807,13 +807,13 @@ bool Cube_InformationInitialize()
{
SCubeMaterialInfo& existInfo = *iter;
// 이미 중복되는 보상이 등록되어 있다면 아예 다른 조합으로 만드는 것인지,
// 거의 같은 조합인데 특정 부분만 틀린 것인지 구분함.
// 예를들면 특정 부분만 틀린 아이템들은 아래처럼 하나로 묶어서 하나의 결과로 보여주기 위함임:
// 용신지검:
// 무쌍검+5 ~ +9 x 1
// 붉은 칼자루 조각 x1
// 녹색 검장식 조각 x1
// 이미 중복되는 보상이 등록되어 있다면 아예 다른 조합으로 만드는 것인지,
// 거의 같은 조합인데 특정 부분만 틀린 것인지 구분함.
// 예를들면 특정 부분만 틀린 아이템들은 아래처럼 하나로 묶어서 하나의 결과로 보여주기 위함임:
// 용신지검:
// 무쌍검+5 ~ +9 x 1
// 붉은 칼자루 조각 x1
// 녹색 검장식 조각 x1
if (reward.vnum == existInfo.reward.vnum)
{
for (TCubeValueVector::iterator existMaterialIter = existInfo.material.begin(); existInfo.material.end() != existMaterialIter; ++existMaterialIter)
@ -828,8 +828,8 @@ bool Cube_InformationInitialize()
if (0 < existItemInfo.level)
{
// 지금 추가하는 큐브 결과물의 재료와, 기존에 등록되어있던 큐브 결과물의 재료 중
// 중복되는 부분이 있는지 검색한다
// 지금 추가하는 큐브 결과물의 재료와, 기존에 등록되어있던 큐브 결과물의 재료 중
// 중복되는 부분이 있는지 검색한다
for (TCubeValueVector::iterator currentMaterialIter = materialInfo.material.begin(); materialInfo.material.end() != currentMaterialIter; ++currentMaterialIter)
{
TItemTable* currentMaterialProto = ITEM_MANAGER::Instance().GetTable(currentMaterialIter->vnum);
@ -845,7 +845,7 @@ bool Cube_InformationInitialize()
//currentMaterialIter = materialInfo.material.erase(currentMaterialIter);
// TODO: 중복되는 아이템 두 개 이상 검출해야 될 수도 있음
// TODO: 중복되는 아이템 두 개 이상 검출해야 될 수도 있음
break;
}
} // for currentMaterialIter
@ -865,7 +865,7 @@ bool Cube_InformationInitialize()
return true;
}
// 클라이언트에서 서버로 : 현재 NPC가 만들 수 있는 아이템들의 정보(목록)를 요청
// 클라이언트에서 서버로 : 현재 NPC가 만들 수 있는 아이템들의 정보(목록)를 요청
void Cube_request_result_list(LPCHARACTER ch)
{
RETURN_IF_CUBE_IS_NOT_OPENED(ch);
@ -879,7 +879,7 @@ void Cube_request_result_list(LPCHARACTER ch)
std::string& resultText = cube_result_info_map_by_npc[npcVNUM];
// 해당 NPC가 만들 수 있는 목록이 정리된 게 없다면 캐시를 생성
// 해당 NPC가 만들 수 있는 목록이 정리된 게 없다면 캐시를 생성
if (resultText.length() == 0)
{
resultText.clear();
@ -898,7 +898,7 @@ void Cube_request_result_list(LPCHARACTER ch)
resultText.erase(resultText.size() - 1);
// 채팅 패킷의 한계를 넘어가면 에러 남김... 기획자 분들 께 조정해달라고 요청하거나, 나중에 다른 방식으로 바꾸거나...
// 채팅 패킷의 한계를 넘어가면 에러 남김... 기획자 분들 께 조정해달라고 요청하거나, 나중에 다른 방식으로 바꾸거나...
if (resultText.size() - 20 >= CHAT_MAX_LEN)
{
SPDLOG_ERROR("[CubeInfo] Too long cube result list text. (NPC: {}, length: {})", npcVNUM, resultText.size());
@ -908,7 +908,7 @@ void Cube_request_result_list(LPCHARACTER ch)
}
// 현재 NPC가 만들 수 있는 아이템들의 목록을 아래 포맷으로 전송한다.
// 현재 NPC가 만들 수 있는 아이템들의 목록을 아래 포맷으로 전송한다.
// (Server -> Client) /cube r_list npcVNUM resultCount vnum1,count1/vnum2,count2,/vnum3,count3/...
// (Server -> Client) /cube r_list 20383 4 123,1/125,1/128,1/130,5

View File

@ -2,7 +2,7 @@
* date : 2006.11.20
* file : cube.h
* author : mhh
* description :
* description :
*/
#ifndef _cube_h_
@ -30,7 +30,7 @@ struct CUBE_DATA
std::vector<CUBE_VALUE> item;
std::vector<CUBE_VALUE> reward;
int percent;
unsigned int gold; // 제조시 필요한 금액
unsigned int gold; // 제조시 필요한 금액
CUBE_DATA();

View File

@ -268,7 +268,7 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
M2_DELETE(pinfo);
break;
}
// Change Location
d->SetLogin(pinfo->login);
SPDLOG_DEBUG("QID_AUTH_LOGIN: START {} {}", qi->dwIdent, (void*) get_pointer(d));
@ -394,7 +394,7 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
if (true == LC_IsBrazil())
{
nPasswordDiff = 0; // 브라질 버전에서는 비밀번호 체크를 하지 않는다.
nPasswordDiff = 0; // 브라질 버전에서는 비밀번호 체크를 하지 않는다.
}
if (nPasswordDiff)
@ -425,7 +425,7 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
{
if (LC_IsEurope())
{
//stBlockData >= 0 == 날짜가 BlockDate 보다 미래
//stBlockData >= 0 == 날짜가 BlockDate 보다 미래
if (strncmp(szCreateDate, g_stBlockDate.c_str(), 8) >= 0)
{
LoginFailure(d, "BLKLOGIN");
@ -493,7 +493,7 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
if (pkItem)
{
SPDLOG_DEBUG("GIVE LOTTO SUCCESS TO {} (pid {})", ch->GetName(), qi->dwIdent);
//ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s"), pkItem->GetName());
//ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s received"), pkItem->GetName());
pkItem->SetSocket(0, pMsg->Get()->uiInsertID);
pkItem->SetSocket(1, pdw[2]);
@ -657,7 +657,7 @@ enum EAccountQID
QID_SPAM_DB,
};
// 10분마다 리로드
// 10분마다 리로드
static LPEVENT s_pkReloadSpamEvent = NULL;
EVENTINFO(reload_spam_event_info)

View File

@ -82,8 +82,8 @@ class DBManager : public singleton<DBManager>
DWORD CountQueryResult() { return m_sql.CountResult(); }
void ResetQueryResult() { m_sql.ResetQueryFinished(); }
template<class Functor> void FuncQuery(Functor f, const char * c_pszFormat, ...); // 결과를 f인자로 호출함 (SQLMsg *) 알아서 해제됨
template<class Functor> void FuncAfterQuery(Functor f, const char * c_pszFormat, ...); // 끝나고 나면 f가 호출됨 void f(void) 형태
template<class Functor> void FuncQuery(Functor f, const char * c_pszFormat, ...); // 결과를 f인자로 호출함 (SQLMsg *) 알아서 해제됨
template<class Functor> void FuncAfterQuery(Functor f, const char * c_pszFormat, ...); // 끝나고 나면 f가 호출됨 void f(void) 형태
size_t EscapeString(char* dst, size_t dstSize, const char *src, size_t srcSize);

View File

@ -316,12 +316,12 @@ void DESC::Packet(const void * c_pvData, int iSize)
{
assert(iSize > 0);
if (m_iPhase == PHASE_CLOSE) // 끊는 상태면 보내지 않는다.
if (m_iPhase == PHASE_CLOSE) // 끊는 상태면 보내지 않는다.
return;
if (m_stRelayName.length() != 0)
{
// Relay 패킷은 암호화하지 않는다.
// Relay 패킷은 암호화하지 않는다.
TPacketGGRelay p;
p.bHeader = HEADER_GG_RELAY;
@ -367,7 +367,7 @@ void DESC::SetPhase(int _phase)
switch (m_iPhase)
{
case PHASE_CLOSE:
// 메신저가 캐릭터단위가 되면서 삭제
// 메신저가 캐릭터단위가 되면서 삭제
//MessengerManager::instance().Logout(GetAccountTable().login);
m_pInputProcessor = &m_inputClose;
break;
@ -377,8 +377,8 @@ void DESC::SetPhase(int _phase)
break;
case PHASE_SELECT:
// 메신저가 캐릭터단위가 되면서 삭제
//MessengerManager::instance().Logout(GetAccountTable().login); // 의도적으로 break 안검
// 메신저가 캐릭터단위가 되면서 삭제
//MessengerManager::instance().Logout(GetAccountTable().login); // 의도적으로 break 안검
case PHASE_LOGIN:
case PHASE_LOADING:
m_pInputProcessor = &m_inputLogin;
@ -550,7 +550,7 @@ void DESC::DisconnectOfSameLogin()
if (m_pkDisconnectEvent)
return;
GetCharacter()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 컴퓨터에서 로그인 하여 접속을 종료 합니다."));
GetCharacter()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Someone has logged into your account. You will be disconnected from the server."));
DelayedDisconnect(5);
}
else

View File

@ -47,14 +47,14 @@ class CLoginKey
};
// sequence 버그 찾기용 데이타
// sequence 버그 찾기용 데이타
struct seq_t
{
BYTE hdr;
BYTE seq;
};
typedef std::vector<seq_t> seq_vector_t;
// sequence 버그 찾기용 데이타
// sequence 버그 찾기용 데이타
extern void DescReadHandler(bufferevent *bev, void *ctx);
extern void DescWriteHandler(bufferevent *bev, void *ctx);
@ -109,7 +109,7 @@ class DESC
bool IsPhase(int phase) const { return m_iPhase == phase ? true : false; }
// 핸드쉐이크 (시간 동기화)
// 핸드쉐이크 (시간 동기화)
void StartHandshake(DWORD _dw);
void SendHandshake(DWORD dwCurTime, LONG lNewDelta);
bool HandshakeProcess(DWORD dwTime, LONG lDelta, bool bInfiniteRetry=false);
@ -118,7 +118,7 @@ class DESC
DWORD GetHandshake() const { return m_dwHandshake; }
DWORD GetClientTime();
// 제국
// 제국
BYTE GetEmpire();
// for p2p
@ -127,7 +127,7 @@ class DESC
void DisconnectOfSameLogin();
void SetAdminMode();
bool IsAdminMode(); // Handshake 에서 어드민 명령을 쓸수있나?
bool IsAdminMode(); // Handshake 에서 어드민 명령을 쓸수있나?
void SetPong(bool b);
bool IsPong();
@ -197,7 +197,7 @@ class DESC
WORD m_wP2PPort;
BYTE m_bP2PChannel;
bool m_bAdminMode; // Handshake 에서 어드민 명령을 쓸수있나?
bool m_bAdminMode; // Handshake 에서 어드민 명령을 쓸수있나?
bool m_bPong;
int m_iCurrentSequence;
@ -248,7 +248,7 @@ class DESC
void ChatPacket(BYTE type, const char * format, ...);
/* 시퀀스 버그 찾기용 코드 */
/* 시퀀스 버그 찾기용 코드 */
public:
seq_vector_t m_seq_vector;
void push_seq (BYTE hdr, BYTE seq);

View File

@ -96,7 +96,7 @@ bool CLIENT_DESC::Connect(int iPhaseWhenSucceed)
if (iPhaseWhenSucceed != 0)
m_iPhaseWhenSucceed = iPhaseWhenSucceed;
if (get_global_time() - m_LastTryToConnectTime < 3) // 3초
if (get_global_time() - m_LastTryToConnectTime < 3) // 3초
return false;
m_LastTryToConnectTime = get_global_time();
@ -219,7 +219,7 @@ void CLIENT_DESC::SetPhase(int iPhase)
SPDLOG_DEBUG("DB_SETUP current user {} size {}", p.dwLoginCount, buf.size());
// 파티를 처리할 수 있게 됨.
// 파티를 처리할 수 있게 됨.
CPartyManager::instance().EnablePCParty();
//CPartyManager::instance().SendPartyToDB();
}
@ -300,7 +300,7 @@ void CLIENT_DESC::Update(DWORD t)
void CLIENT_DESC::UpdateChannelStatus(DWORD t, bool fForce)
{
enum {
CHANNELSTATUS_UPDATE_PERIOD = 5*60*1000, // 5분마다
CHANNELSTATUS_UPDATE_PERIOD = 5*60*1000, // 5분마다
};
if (fForce || m_tLastChannelStatusUpdateTime+CHANNELSTATUS_UPDATE_PERIOD < t) {
int iTotal;

View File

@ -53,7 +53,7 @@ int IsValidIP(struct valid_ip* ip_table, const char *host)
DESC_MANAGER::DESC_MANAGER() : m_bDestroyed(false)
{
Initialize();
//NOTE : Destroy 끝에서 Initialize 를 부르는건 또 무슨 짓이냐..-_-; 정말
//NOTE : Destroy 끝에서 Initialize 를 부르는건 또 무슨 짓이냐..-_-; 정말
m_pPackageCrypt = new CClientPackageCryptInfo;
}
@ -146,7 +146,7 @@ RETRY:
LPDESC DESC_MANAGER::AcceptDesc(evconnlistener* listener, evutil_socket_t fd, sockaddr* address)
{
if (!IsValidIP(admin_ip, GetSocketHost(address).c_str())) // admin_ip 에 등록된 IP 는 최대 사용자 수에 구애받지 않는다.
if (!IsValidIP(admin_ip, GetSocketHost(address).c_str())) // admin_ip 에 등록된 IP 는 최대 사용자 수에 구애받지 않는다.
{
if (m_iSocketsConnected >= MAX_ALLOW_USER)
{

View File

@ -172,7 +172,7 @@ bool DragonSoulTable::ReadBasicApplys()
TVecApplys vecApplys;
int n = pChild->GetRowCount();
// BasicApply Group은 Key가 1부터 시작함.
// BasicApply Group은 Key가 1부터 시작함.
for (int j = 1; j <= n; j++)
{
std::stringstream ss;
@ -655,7 +655,7 @@ bool DragonSoulTable::GetWeight(BYTE ds_type, BYTE grade_idx, BYTE step_index, B
return true;
}
}
// default group을 살펴봄.
// default group을 살펴봄.
pDragonSoulGroup = m_pWeightTableNode->GetChildNode("default");
if (NULL != pDragonSoulGroup)
{

Some files were not shown because too many files have changed in this diff Show More