diff --git a/Src/3rd_party/mysql/include/my_alloc.h b/Src/3rd_party/mysql/include/my_alloc.h new file mode 100644 index 0000000..1641b3a --- /dev/null +++ b/Src/3rd_party/mysql/include/my_alloc.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Data structures for mysys/my_alloc.c (root memory allocator) +*/ + +#ifndef _my_alloc_h +#define _my_alloc_h + +#define ALLOC_MAX_BLOCK_TO_DROP 4096 +#define ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP 10 + +typedef struct st_used_mem +{ /* struct for once_alloc (block) */ + struct st_used_mem *next; /* Next block in use */ + unsigned int left; /* memory left in block */ + unsigned int size; /* size of block */ +} USED_MEM; + + +typedef struct st_mem_root +{ + USED_MEM *free; /* blocks with free memory in it */ + USED_MEM *used; /* blocks almost without free memory */ + USED_MEM *pre_alloc; /* preallocated block */ + /* if block have less memory it will be put in 'used' list */ + unsigned int min_malloc; + unsigned int block_size; /* initial block size */ + unsigned int block_num; /* allocated blocks counter */ + /* + first free block in queue test counter (if it exceed + MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list) + */ + unsigned int first_block_usage; + + void (*error_handler)(void); +} MEM_ROOT; +#endif diff --git a/Src/3rd_party/mysql/include/my_dbug.h b/Src/3rd_party/mysql/include/my_dbug.h new file mode 100644 index 0000000..b76a3fc --- /dev/null +++ b/Src/3rd_party/mysql/include/my_dbug.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _dbug_h +#define _dbug_h + +#ifdef __cplusplus +extern "C" { +#endif +#if !defined(DBUG_OFF) && !defined(_lint) +extern int _db_on_,_no_db_; +extern FILE *_db_fp_; +extern char *_db_process_; +extern int _db_keyword_(const char *keyword); +extern int _db_strict_keyword_(const char *keyword); +extern void _db_setjmp_(void); +extern void _db_longjmp_(void); +extern void _db_push_(const char *control); +extern void _db_pop_(void); +extern void _db_enter_(const char *_func_,const char *_file_,uint _line_, + const char **_sfunc_,const char **_sfile_, + uint *_slevel_, char ***); +extern void _db_return_(uint _line_,const char **_sfunc_,const char **_sfile_, + uint *_slevel_); +extern void _db_pargs_(uint _line_,const char *keyword); +extern void _db_doprnt_ _VARARGS((const char *format,...)); +extern void _db_dump_(uint _line_,const char *keyword,const char *memory, + uint length); +extern void _db_output_(uint flag); +extern void _db_lock_file(void); +extern void _db_unlock_file(void); + +#define DBUG_ENTER(a) const char *_db_func_, *_db_file_; uint _db_level_; \ + char **_db_framep_; \ + _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_, \ + &_db_framep_) +#define DBUG_LEAVE \ + (_db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_)) +#define DBUG_RETURN(a1) {DBUG_LEAVE; return(a1);} +#define DBUG_VOID_RETURN {DBUG_LEAVE; return;} +#define DBUG_EXECUTE(keyword,a1) \ + {if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }}} +#define DBUG_PRINT(keyword,arglist) \ + {if (_db_on_) {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;}} +#define DBUG_PUSH(a1) _db_push_ (a1) +#define DBUG_POP() _db_pop_ () +#define DBUG_PROCESS(a1) (_db_process_ = a1) +#define DBUG_FILE (_db_fp_) +#define DBUG_SETJMP(a1) (_db_setjmp_ (), setjmp (a1)) +#define DBUG_LONGJMP(a1,a2) (_db_longjmp_ (), longjmp (a1, a2)) +#define DBUG_DUMP(keyword,a1,a2)\ + {if (_db_on_) {_db_dump_(__LINE__,keyword,a1,a2);}} +#define DBUG_IN_USE (_db_fp_ && _db_fp_ != stderr) +#define DEBUGGER_OFF _no_db_=1;_db_on_=0; +#define DEBUGGER_ON _no_db_=0 +#define DBUG_LOCK_FILE { _db_lock_file(); } +#define DBUG_UNLOCK_FILE { _db_unlock_file(); } +#define DBUG_OUTPUT(A) { _db_output_(A); } +#define DBUG_ASSERT(A) assert(A) +#define DBUG_EXECUTE_IF(keyword,a1) \ + {if (_db_on_) {if (_db_strict_keyword_ (keyword)) { a1 }}} +#else /* No debugger */ + +#define DBUG_ENTER(a1) +#define DBUG_RETURN(a1) return(a1) +#define DBUG_VOID_RETURN return +#define DBUG_EXECUTE(keyword,a1) {} +#define DBUG_EXECUTE_IF(keyword,a1) {} +#define DBUG_PRINT(keyword,arglist) {} +#define DBUG_PUSH(a1) {} +#define DBUG_POP() {} +#define DBUG_PROCESS(a1) {} +#define DBUG_FILE (stderr) +#define DBUG_SETJMP setjmp +#define DBUG_LONGJMP longjmp +#define DBUG_DUMP(keyword,a1,a2) {} +#define DBUG_IN_USE 0 +#define DEBUGGER_OFF +#define DEBUGGER_ON +#define DBUG_LOCK_FILE +#define DBUG_UNLOCK_FILE +#define DBUG_OUTPUT(A) +#define DBUG_ASSERT(A) {} +#endif +#ifdef __cplusplus +} +#endif +#endif diff --git a/Src/3rd_party/mysql/include/my_getopt.h b/Src/3rd_party/mysql/include/my_getopt.h new file mode 100644 index 0000000..e6ca113 --- /dev/null +++ b/Src/3rd_party/mysql/include/my_getopt.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _my_getopt_h +#define _my_getopt_h + +C_MODE_START + +#define GET_NO_ARG 1 +#define GET_BOOL 2 +#define GET_INT 3 +#define GET_UINT 4 +#define GET_LONG 5 +#define GET_ULONG 6 +#define GET_LL 7 +#define GET_ULL 8 +#define GET_STR 9 +#define GET_STR_ALLOC 10 +#define GET_DISABLED 11 + +#define GET_ASK_ADDR 128 +#define GET_TYPE_MASK 127 + +enum get_opt_arg_type { NO_ARG, OPT_ARG, REQUIRED_ARG }; + +struct my_option +{ + const char *name; /* Name of the option */ + int id; /* unique id or short option */ + const char *comment; /* option comment, for autom. --help */ + gptr *value; /* The variable value */ + gptr *u_max_value; /* The user def. max variable value */ + const char **str_values; /* Pointer to possible values */ + ulong var_type; + enum get_opt_arg_type arg_type; + longlong def_value; /* Default value */ + longlong min_value; /* Min allowed value */ + longlong max_value; /* Max allowed value */ + longlong sub_size; /* Subtract this from given value */ + long block_size; /* Value should be a mult. of this */ + int app_type; /* To be used by an application */ +}; + +typedef my_bool (* my_get_one_option) (int, const struct my_option *, char * ); +typedef void (* my_error_reporter) (enum loglevel level, const char *format, ... ); + +extern char *disabled_my_option; +extern my_bool my_getopt_print_errors; +extern my_error_reporter my_getopt_error_reporter; + +extern int handle_options (int *argc, char ***argv, + const struct my_option *longopts, my_get_one_option); +extern void my_print_help(const struct my_option *options); +extern void my_print_variables(const struct my_option *options); +extern void my_getopt_register_get_addr(gptr* (*func_addr)(const char *, uint, + const struct my_option *)); + +ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp); +my_bool getopt_compare_strings(const char *s, const char *t, uint length); + +C_MODE_END + +#endif /* _my_getopt_h */ + diff --git a/Src/3rd_party/mysql/include/my_global.h b/Src/3rd_party/mysql/include/my_global.h new file mode 100644 index 0000000..b32a8fe --- /dev/null +++ b/Src/3rd_party/mysql/include/my_global.h @@ -0,0 +1,1276 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* This is the include file that should be included 'first' in every C file. */ + +#ifndef _global_h +#define _global_h + +#ifndef EMBEDDED_LIBRARY +#define HAVE_REPLICATION +#define HAVE_EXTERNAL_CLIENT +#endif + +#if defined( __EMX__) && !defined( MYSQL_SERVER) +/* moved here to use below VOID macro redefinition */ +#define INCL_BASE +#define INCL_NOPMAPI +#include +#endif /* __EMX__ */ + +#ifdef __CYGWIN__ +/* We use a Unix API, so pretend it's not Windows */ +#undef WIN +#undef WIN32 +#undef _WIN +#undef _WIN32 +#undef _WIN64 +#undef __WIN__ +#undef __WIN32__ +#define HAVE_ERRNO_AS_DEFINE +#endif /* __CYGWIN__ */ + +/* to make command line shorter we'll define USE_PRAGMA_INTERFACE here */ +#ifdef USE_PRAGMA_IMPLEMENTATION +#define USE_PRAGMA_INTERFACE +#endif + +#if defined(i386) && !defined(__i386__) +#define __i386__ +#endif + +/* Macros to make switching between C and C++ mode easier */ +#ifdef __cplusplus +#define C_MODE_START extern "C" { +#define C_MODE_END } +#else +#define C_MODE_START +#define C_MODE_END +#endif + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#include +#elif defined(OS2) +#include +#elif defined(__NETWARE__) +#include +#include +#if defined(__cplusplus) && defined(inline) +#undef inline /* fix configure problem */ +#endif +#else +#include +#if defined(__cplusplus) && defined(inline) +#undef inline /* fix configure problem */ +#endif +#endif /* _WIN32... */ + +/* Some defines to avoid ifdefs in the code */ +#ifndef NETWARE_YIELD +#define NETWARE_YIELD +#define NETWARE_SET_SCREEN_MODE(A) +#endif + +/* + The macros below are borrowed from include/linux/compiler.h in the + Linux kernel. Use them to indicate the likelyhood of the truthfulness + of a condition. This serves two purposes - newer versions of gcc will be + able to optimize for branch predication, which could yield siginficant + performance gains in frequently executed sections of the code, and the + other reason to use them is for documentation +*/ + +#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +#define __builtin_expect(x, expected_value) (x) +#endif + +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + + +/* Fix problem with S_ISLNK() on Linux */ +#if defined(TARGET_OS_LINUX) +#undef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +/* + Temporary solution to solve bug#7156. Include "sys/types.h" before + the thread headers, else the function madvise() will not be defined +*/ +#if defined(HAVE_SYS_TYPES_H) && ( defined(sun) || defined(__sun) ) +#include +#endif + +/* The client defines this to avoid all thread code */ +#if defined(UNDEF_THREADS_HACK) +#undef THREAD +#undef HAVE_mit_thread +#undef HAVE_LINUXTHREADS +#undef HAVE_NPTL +#undef HAVE_UNIXWARE7_THREADS +#endif + +#ifdef HAVE_THREADS_WITHOUT_SOCKETS +/* MIT pthreads does not work with unix sockets */ +#undef HAVE_SYS_UN_H +#endif + +#define __EXTENSIONS__ 1 /* We want some extension */ +#ifndef __STDC_EXT__ +#define __STDC_EXT__ 1 /* To get large file support on hpux */ +#endif + +/* + Solaris 9 include file refers to X/Open document + + System Interfaces and Headers, Issue 5 + + saying we should define _XOPEN_SOURCE=500 to get POSIX.1c prototypes, + but apparently other systems (namely FreeBSD) don't agree. + + On a newer Solaris 10, the above file recognizes also _XOPEN_SOURCE=600. + Furthermore, it tests that if a program requires older standard + (_XOPEN_SOURCE<600 or _POSIX_C_SOURCE<200112L) it cannot be + run on a new compiler (that defines _STDC_C99) and issues an #error. + It's also an #error if a program requires new standard (_XOPEN_SOURCE=600 + or _POSIX_C_SOURCE=200112L) and a compiler does not define _STDC_C99. + + To add more to this mess, Sun Studio C compiler defines _STDC_C99 while + C++ compiler does not! + + So, in a desperate attempt to get correct prototypes for both + C and C++ code, we define either _XOPEN_SOURCE=600 or _XOPEN_SOURCE=500 + depending on the compiler's announced C standard support. + + Cleaner solutions are welcome. +*/ +#ifdef __sun +#if __STDC_VERSION__ - 0 >= 199901L +#define _XOPEN_SOURCE 600 +#else +#define _XOPEN_SOURCE 500 +#endif +#endif + +#if defined(THREAD) && !defined(__WIN__) && !defined(OS2) +#ifndef _POSIX_PTHREAD_SEMANTICS +#define _POSIX_PTHREAD_SEMANTICS /* We want posix threads */ +#endif + +#if !defined(SCO) +#define _REENTRANT 1 /* Some thread libraries require this */ +#endif +#if !defined(_THREAD_SAFE) && !defined(_AIX) +#define _THREAD_SAFE /* Required for OSF1 */ +#endif +#ifndef HAVE_mit_thread +#ifdef HAVE_UNIXWARE7_THREADS +#include +#else +#if defined(HPUX10) || defined(HPUX11) +C_MODE_START /* HPUX needs this, signal.h bug */ +#include +C_MODE_END +#else +#include /* AIX must have this included first */ +#endif +#endif /* HAVE_UNIXWARE7_THREADS */ +#endif /* HAVE_mit_thread */ +#if !defined(SCO) && !defined(_REENTRANT) +#define _REENTRANT 1 /* Threads requires reentrant code */ +#endif +#endif /* THREAD */ + +/* Go around some bugs in different OS and compilers */ +#ifdef _AIX /* By soren@t.dk */ +#define _H_STRINGS +#define _SYS_STREAM_H +/* #define _AIX32_CURSES */ /* XXX: this breaks AIX 4.3.3 (others?). */ +#define ulonglong2double(A) my_ulonglong2double(A) +#define my_off_t2double(A) my_ulonglong2double(A) +C_MODE_START +double my_ulonglong2double(unsigned long long A); +C_MODE_END +#endif /* _AIX */ + +#ifdef HAVE_BROKEN_SNPRINTF /* HPUX 10.20 don't have this defined */ +#undef HAVE_SNPRINTF +#endif +#ifdef HAVE_BROKEN_PREAD +/* + pread()/pwrite() are not 64 bit safe on HP-UX 11.0 without + installing the kernel patch PHKL_20349 or greater +*/ +#undef HAVE_PREAD +#undef HAVE_PWRITE +#endif +#if defined(HAVE_BROKEN_INLINE) && !defined(__cplusplus) +#undef inline +#define inline +#endif + +#ifdef UNDEF_HAVE_GETHOSTBYNAME_R /* For OSF4.x */ +#undef HAVE_GETHOSTBYNAME_R +#endif +#ifdef UNDEF_HAVE_INITGROUPS /* For AIX 4.3 */ +#undef HAVE_INITGROUPS +#endif + +/* gcc/egcs issues */ + +#if defined(__GNUC) && defined(__EXCEPTIONS) +#error "Please add -fno-exceptions to CXXFLAGS and reconfigure/recompile" +#endif + + +/* Fix a bug in gcc 2.8.0 on IRIX 6.2 */ +#if SIZEOF_LONG == 4 && defined(__LONG_MAX__) && (__GNUC__ == 2 && __GNUC_MINOR__ == 8) +#undef __LONG_MAX__ /* Is a longlong value in gcc 2.8.0 ??? */ +#define __LONG_MAX__ 2147483647 +#endif + +/* egcs 1.1.2 has a problem with memcpy on Alpha */ +#if defined(__GNUC__) && defined(__alpha__) && ! (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)) +#define BAD_MEMCPY +#endif + +#if defined(_lint) && !defined(lint) +#define lint +#endif +#if SIZEOF_LONG_LONG > 4 && !defined(_LONG_LONG) +#define _LONG_LONG 1 /* For AIX string library */ +#endif + +#ifndef stdin +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDDEF_H +#include +#endif + +#include +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_TIMEB_H +#include /* Avoid warnings on SCO */ +#endif +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif /* TIME_WITH_SYS_TIME */ +#ifdef HAVE_UNISTD_H +#include +#endif +#if defined(__cplusplus) && defined(NO_CPLUSPLUS_ALLOCA) +#undef HAVE_ALLOCA +#undef HAVE_ALLOCA_H +#endif +#ifdef HAVE_ALLOCA_H +#include +#endif +#ifdef HAVE_ATOMIC_ADD +#define new my_arg_new +#define need_to_restore_new 1 +C_MODE_START +#include +C_MODE_END +#ifdef need_to_restore_new /* probably safer than #ifdef new */ +#undef new +#undef need_to_restore_new +#endif +#endif +#include /* Recommended by debian */ +/* We need the following to go around a problem with openssl on solaris */ +#if defined(HAVE_CRYPT_H) +#include +#endif + +/* + A lot of our programs uses asserts, so better to always include it + This also fixes a problem when people uses DBUG_ASSERT without including + assert.h +*/ +#include + +/* Go around some bugs in different OS and compilers */ +#if defined(_HPUX_SOURCE) && defined(HAVE_SYS_STREAM_H) +#include /* HPUX 10.20 defines ulong here. UGLY !!! */ +#define HAVE_ULONG +#endif +#ifdef DONT_USE_FINITE /* HPUX 11.x has is_finite() */ +#undef HAVE_FINITE +#endif +#if defined(HPUX10) && defined(_LARGEFILE64_SOURCE) && defined(THREAD) +/* Fix bug in setrlimit */ +#undef setrlimit +#define setrlimit cma_setrlimit64 +#endif +/* Declare madvise where it is not declared for C++, like Solaris */ +#if HAVE_MADVISE && !HAVE_DECL_MADVISE && defined(__cplusplus) +extern "C" int madvise(void *addr, size_t len, int behav); +#endif + +#ifdef __QNXNTO__ +/* This has to be after include limits.h */ +#define HAVE_ERRNO_AS_DEFINE +#define HAVE_FCNTL_LOCK +#undef HAVE_FINITE +#undef LONGLONG_MIN /* These get wrongly defined in QNX 6.2 */ +#undef LONGLONG_MAX /* standard system library 'limits.h' */ +#ifdef __cplusplus +#ifndef HAVE_RINT +#define HAVE_RINT +#endif /* rint() and isnan() functions are not */ +#define rint(a) std::rint(a) /* visible in C++ scope due to an error */ +#define isnan(a) std::isnan(a) /* in the usr/include/math.h on QNX */ +#endif +#endif + +/* We can not live without the following defines */ + +#define USE_MYFUNC 1 /* Must use syscall indirection */ +#define MASTER 1 /* Compile without unireg */ +#define ENGLISH 1 /* Messages in English */ +#define POSIX_MISTAKE 1 /* regexp: Fix stupid spec error */ +#define USE_REGEX 1 /* We want the use the regex library */ +/* Do not define for ultra sparcs */ +#ifndef OS2 +#define USE_BMOVE512 1 /* Use this unless system bmove is faster */ +#endif + +#define QUOTE_ARG(x) #x /* Quote argument (before cpp) */ +#define STRINGIFY_ARG(x) QUOTE_ARG(x) /* Quote argument, after cpp */ + +/* Paranoid settings. Define I_AM_PARANOID if you are paranoid */ +#ifdef I_AM_PARANOID +#define DONT_ALLOW_USER_CHANGE 1 +#define DONT_USE_MYSQL_PWD 1 +#endif + +/* Does the system remember a signal handler after a signal ? */ +#ifndef HAVE_BSD_SIGNALS +#define DONT_REMEMBER_SIGNAL +#endif + +/* Define void to stop lint from generating "null effekt" comments */ +#ifndef DONT_DEFINE_VOID +#ifdef _lint +int __void__; +#define VOID(X) (__void__ = (int) (X)) +#else +#undef VOID +#define VOID(X) (X) +#endif +#endif /* DONT_DEFINE_VOID */ + +#if defined(_lint) || defined(FORCE_INIT_OF_VARS) +#define LINT_INIT(var) var=0 /* No uninitialize-warning */ +#else +#define LINT_INIT(var) +#endif + +#if defined(_lint) || defined(FORCE_INIT_OF_VARS) || defined(HAVE_purify) +#define PURIFY_OR_LINT_INIT(var) var=0 +#else +#define PURIFY_OR_LINT_INIT(var) +#endif + +/* Define some useful general macros */ +#if !defined(max) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#if defined(__EMX__) || !defined(HAVE_UINT) +#undef HAVE_UINT +#define HAVE_UINT +typedef unsigned int uint; +typedef unsigned short ushort; +#endif + +#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1) +#define sgn(a) (((a) < 0) ? -1 : ((a) > 0) ? 1 : 0) +#define swap_variables(t, a, b) { register t dummy; dummy= a; a= b; b= dummy; } +#define test(a) ((a) ? 1 : 0) +#define set_if_bigger(a,b) do { if ((a) < (b)) (a)=(b); } while(0) +#define set_if_smaller(a,b) do { if ((a) > (b)) (a)=(b); } while(0) +#define test_all_bits(a,b) (((a) & (b)) == (b)) +#define set_bits(type, bit_count) (sizeof(type)*8 <= (bit_count) ? ~(type) 0 : ((((type) 1) << (bit_count)) - (type) 1)) +#define array_elements(A) ((uint) (sizeof(A)/sizeof(A[0]))) +#ifndef HAVE_RINT +#define rint(A) floor((A)+(((A) < 0)? -0.5 : 0.5)) +#endif + +/* Define some general constants */ +#ifndef TRUE +#define TRUE (1) /* Logical true */ +#define FALSE (0) /* Logical false */ +#endif + +#if defined(__GNUC__) +#define function_volatile volatile +#define my_reinterpret_cast(A) reinterpret_cast +#define my_const_cast(A) const_cast +#elif !defined(my_reinterpret_cast) +#define my_reinterpret_cast(A) (A) +#define my_const_cast(A) (A) +#endif +#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) +#define __attribute__(A) +#endif + +/* From old s-system.h */ + +/* + Support macros for non ansi & other old compilers. Since such + things are no longer supported we do nothing. We keep then since + some of our code may still be needed to upgrade old customers. +*/ +#define _VARARGS(X) X +#define _STATIC_VARARGS(X) X +#define _PC(X) X + +#if defined(DBUG_ON) && defined(DBUG_OFF) +#undef DBUG_OFF +#endif + +#if defined(_lint) && !defined(DBUG_OFF) +#define DBUG_OFF +#endif + +#include + +#define MIN_ARRAY_SIZE 0 /* Zero or One. Gcc allows zero*/ +#define ASCII_BITS_USED 8 /* Bit char used */ +#define NEAR_F /* No near function handling */ + +/* Some types that is different between systems */ + +typedef int File; /* File descriptor */ +#ifndef Socket_defined +typedef int my_socket; /* File descriptor for sockets */ +#define INVALID_SOCKET -1 +#endif +/* Type for fuctions that handles signals */ +#define sig_handler RETSIGTYPE +C_MODE_START +typedef void (*sig_return)();/* Returns type from signal */ +C_MODE_END +#if defined(__GNUC__) && !defined(_lint) +typedef char pchar; /* Mixed prototypes can take char */ +typedef char puchar; /* Mixed prototypes can take char */ +typedef char pbool; /* Mixed prototypes can take char */ +typedef short pshort; /* Mixed prototypes can take short int */ +typedef float pfloat; /* Mixed prototypes can take float */ +#else +typedef int pchar; /* Mixed prototypes can't take char */ +typedef uint puchar; /* Mixed prototypes can't take char */ +typedef int pbool; /* Mixed prototypes can't take char */ +typedef int pshort; /* Mixed prototypes can't take short int */ +typedef double pfloat; /* Mixed prototypes can't take float */ +#endif +C_MODE_START +typedef int (*qsort_cmp)(const void *,const void *); +typedef int (*qsort_cmp2)(void*, const void *,const void *); +C_MODE_END +#ifdef HAVE_mit_thread +#define qsort_t void +#undef QSORT_TYPE_IS_VOID +#define QSORT_TYPE_IS_VOID +#else +#define qsort_t RETQSORTTYPE /* Broken GCC cant handle typedef !!!! */ +#endif +#ifdef HAVE_mit_thread +#define size_socket socklen_t /* Type of last arg to accept */ +#else +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +typedef SOCKET_SIZE_TYPE size_socket; +#endif + +#ifndef SOCKOPT_OPTLEN_TYPE +#define SOCKOPT_OPTLEN_TYPE size_socket +#endif + +/* file create flags */ + +#ifndef O_SHARE /* Probably not windows */ +#define O_SHARE 0 /* Flag to my_open for shared files */ +#ifndef O_BINARY +#define O_BINARY 0 /* Flag to my_open for binary files */ +#endif +#ifndef FILE_BINARY +#define FILE_BINARY O_BINARY /* Flag to my_fopen for binary streams */ +#endif +#ifdef HAVE_FCNTL +#define HAVE_FCNTL_LOCK +#define F_TO_EOF 0L /* Param to lockf() to lock rest of file */ +#endif +#endif /* O_SHARE */ + +#ifndef O_TEMPORARY +#define O_TEMPORARY 0 +#endif +#ifndef O_SHORT_LIVED +#define O_SHORT_LIVED 0 +#endif +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + +/* #define USE_RECORD_LOCK */ + + /* Unsigned types supported by the compiler */ +#define UNSINT8 /* unsigned int8 (char) */ +#define UNSINT16 /* unsigned int16 */ +#define UNSINT32 /* unsigned int32 */ + + /* General constants */ +#define SC_MAXWIDTH 256 /* Max width of screen (for error messages) */ +#define FN_LEN 256 /* Max file name len */ +#define FN_HEADLEN 253 /* Max length of filepart of file name */ +#define FN_EXTLEN 20 /* Max length of extension (part of FN_LEN) */ +#ifdef PATH_MAX +#define FN_REFLEN PATH_MAX/* Max length of full path-name */ +#else +#define FN_REFLEN 512 /* Max length of full path-name */ +#endif +#define FN_EXTCHAR '.' +#define FN_HOMELIB '~' /* ~/ is used as abbrev for home dir */ +#define FN_CURLIB '.' /* ./ is used as abbrev for current dir */ +#define FN_PARENTDIR ".." /* Parent directory; Must be a string */ +#define FN_DEVCHAR ':' + +#ifndef FN_LIBCHAR +#ifdef __EMX__ +#define FN_LIBCHAR '\\' +#define FN_ROOTDIR "\\" +#else +#define FN_LIBCHAR '/' +#define FN_ROOTDIR "/" +#endif +#endif +#define MY_NFILE 64 /* This is only used to save filenames */ +#ifndef OS_FILE_LIMIT +#define OS_FILE_LIMIT 65535 +#endif + +/* #define EXT_IN_LIBNAME */ +/* #define FN_NO_CASE_SENCE */ +/* #define FN_UPPER_CASE TRUE */ + +/* + Io buffer size; Must be a power of 2 and a multiple of 512. May be + smaller what the disk page size. This influences the speed of the + isam btree library. eg to big to slow. +*/ +#define IO_SIZE 4096 +/* + How much overhead does malloc have. The code often allocates + something like 1024-MALLOC_OVERHEAD bytes +*/ +#ifdef SAFEMALLOC +#define MALLOC_OVERHEAD (8+24+4) +#else +#define MALLOC_OVERHEAD 8 +#endif + /* get memory in huncs */ +#define ONCE_ALLOC_INIT (uint) (4096-MALLOC_OVERHEAD) + /* Typical record cash */ +#define RECORD_CACHE_SIZE (uint) (64*1024-MALLOC_OVERHEAD) + /* Typical key cash */ +#define KEY_CACHE_SIZE (uint) (8*1024*1024-MALLOC_OVERHEAD) + /* Default size of a key cache block */ +#define KEY_CACHE_BLOCK_SIZE (uint) 1024 + + + /* Some things that this system doesn't have */ + +#define NO_HASH /* Not needed anymore */ +#ifdef __WIN__ +#define NO_DIR_LIBRARY /* Not standar dir-library */ +#define USE_MY_STAT_STRUCT /* For my_lib */ +#endif + +/* Some defines of functions for portability */ + +#undef remove /* Crashes MySQL on SCO 5.0.0 */ +#ifndef __WIN__ +#ifdef OS2 +#define closesocket(A) soclose(A) +#else +#define closesocket(A) close(A) +#endif +#ifndef ulonglong2double +#define ulonglong2double(A) ((double) (ulonglong) (A)) +#define my_off_t2double(A) ((double) (my_off_t) (A)) +#endif +#endif + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif +#define ulong_to_double(X) ((double) (ulong) (X)) +#define SET_STACK_SIZE(X) /* Not needed on real machines */ + +#if !defined(HAVE_mit_thread) && !defined(HAVE_STRTOK_R) +#define strtok_r(A,B,C) strtok((A),(B)) +#endif + +/* Remove some things that mit_thread break or doesn't support */ +#if defined(HAVE_mit_thread) && defined(THREAD) +#undef HAVE_PREAD +#undef HAVE_REALPATH +#undef HAVE_MLOCK +#undef HAVE_TEMPNAM /* Use ours */ +#undef HAVE_PTHREAD_SETPRIO +#undef HAVE_FTRUNCATE +#undef HAVE_READLINK +#endif + +/* This is from the old m-machine.h file */ + +#if SIZEOF_LONG_LONG > 4 +#define HAVE_LONG_LONG 1 +#endif + +/* + Some pre-ANSI-C99 systems like AIX 5.1 and Linux/GCC 2.95 define + ULONGLONG_MAX, LONGLONG_MIN, LONGLONG_MAX; we use them if they're defined. + Also on Windows we define these constants by hand in config-win.h. +*/ + +#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN) +#define LONGLONG_MIN ((long long) 0x8000000000000000LL) +#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL) +#endif + +#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX) +/* First check for ANSI C99 definition: */ +#ifdef ULLONG_MAX +#define ULONGLONG_MAX ULLONG_MAX +#else +#define ULONGLONG_MAX ((unsigned long long)(~0ULL)) +#endif +#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/ + +#define INT_MIN32 (~0x7FFFFFFFL) +#define INT_MAX32 0x7FFFFFFFL +#define UINT_MAX32 0xFFFFFFFFL +#define INT_MIN24 (~0x007FFFFF) +#define INT_MAX24 0x007FFFFF +#define UINT_MAX24 0x00FFFFFF +#define INT_MIN16 (~0x7FFF) +#define INT_MAX16 0x7FFF +#define UINT_MAX16 0xFFFF +#define INT_MIN8 (~0x7F) +#define INT_MAX8 0x7F +#define UINT_MAX8 0xFF + +/* From limits.h instead */ +#ifndef DBL_MIN +#define DBL_MIN 4.94065645841246544e-324 +#define FLT_MIN ((float)1.40129846432481707e-45) +#endif +#ifndef DBL_MAX +#define DBL_MAX 1.79769313486231470e+308 +#define FLT_MAX ((float)3.40282346638528860e+38) +#endif + +#if !defined(HAVE_ISINF) && !defined(isinf) +#define isinf(X) 0 +#endif + +/* Define missing math constants. */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_E +#define M_E 2.7182818284590452354 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +/* + Max size that must be added to a so that we know Size to make + adressable obj. +*/ +#if SIZEOF_CHARP == 4 +typedef long my_ptrdiff_t; +#else +typedef long long my_ptrdiff_t; +#endif + +#define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1)) +#define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double)) +/* Size to make adressable obj. */ +#define ALIGN_PTR(A, t) ((t*) MY_ALIGN((A),sizeof(t))) + /* Offset of field f in structure t */ +#define OFFSET(t, f) ((size_t)(char *)&((t *)0)->f) +#define ADD_TO_PTR(ptr,size,type) (type) ((byte*) (ptr)+size) +#define PTR_BYTE_DIFF(A,B) (my_ptrdiff_t) ((byte*) (A) - (byte*) (B)) + +#define NullS (char *) 0 +/* Nowdays we do not support MessyDos */ +#ifndef NEAR +#define NEAR /* Who needs segments ? */ +#define FAR /* On a good machine */ +#ifndef HUGE_PTR +#define HUGE_PTR +#endif +#endif +#if defined(__IBMC__) || defined(__IBMCPP__) +/* This was _System _Export but caused a lot of warnings on _AIX43 */ +#define STDCALL +#elif !defined( STDCALL) +#define STDCALL +#endif + +/* Typdefs for easyier portability */ + +#if defined(VOIDTYPE) +typedef void *gptr; /* Generic pointer */ +#else +typedef char *gptr; /* Generic pointer */ +#endif +#ifndef HAVE_INT_8_16_32 +typedef signed char int8; /* Signed integer >= 8 bits */ +typedef short int16; /* Signed integer >= 16 bits */ +#endif +#ifndef HAVE_UCHAR +typedef unsigned char uchar; /* Short for unsigned char */ +#endif +typedef unsigned char uint8; /* Short for unsigned integer >= 8 bits */ +typedef unsigned short uint16; /* Short for unsigned integer >= 16 bits */ + +#if SIZEOF_INT == 4 +#ifndef HAVE_INT_8_16_32 +typedef int int32; +#endif +typedef unsigned int uint32; /* Short for unsigned integer >= 32 bits */ +#elif SIZEOF_LONG == 4 +#ifndef HAVE_INT_8_16_32 +typedef long int32; +#endif +typedef unsigned long uint32; /* Short for unsigned integer >= 32 bits */ +#else +#error "Neither int or long is of 4 bytes width" +#endif + +#if !defined(HAVE_ULONG) && !defined(TARGET_OS_LINUX) && !defined(__USE_MISC) +typedef unsigned long ulong; /* Short for unsigned long */ +#endif +#ifndef longlong_defined +#if defined(HAVE_LONG_LONG) && SIZEOF_LONG != 8 +typedef unsigned long long int ulonglong; /* ulong or unsigned long long */ +typedef long long int longlong; +#else +typedef unsigned long ulonglong; /* ulong or unsigned long long */ +typedef long longlong; +#endif +#endif + +#if defined(NO_CLIENT_LONG_LONG) +typedef unsigned long my_ulonglong; +#elif defined (__WIN__) +typedef unsigned __int64 my_ulonglong; +#else +typedef unsigned long long my_ulonglong; +#endif + +#ifdef USE_RAID +/* + The following is done with a if to not get problems with pre-processors + with late define evaluation +*/ +#if SIZEOF_OFF_T == 4 +#define SYSTEM_SIZEOF_OFF_T 4 +#else +#define SYSTEM_SIZEOF_OFF_T 8 +#endif +#undef SIZEOF_OFF_T +#define SIZEOF_OFF_T 8 +#else +#define SYSTEM_SIZEOF_OFF_T SIZEOF_OFF_T +#endif /* USE_RAID */ + +#if SIZEOF_OFF_T > 4 +typedef ulonglong my_off_t; +#else +typedef unsigned long my_off_t; +#endif +#define MY_FILEPOS_ERROR (~(my_off_t) 0) +#if !defined(__WIN__) && !defined(OS2) +typedef off_t os_off_t; +#endif + +#if defined(__WIN__) +#define socket_errno WSAGetLastError() +#define SOCKET_EINTR WSAEINTR +#define SOCKET_EAGAIN WSAEINPROGRESS +#define SOCKET_ETIMEDOUT WSAETIMEDOUT +#define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK +#define SOCKET_ENFILE ENFILE +#define SOCKET_EMFILE EMFILE +#elif defined(OS2) +#define socket_errno sock_errno() +#define SOCKET_EINTR SOCEINTR +#define SOCKET_EAGAIN SOCEINPROGRESS +#define SOCKET_ETIMEDOUT SOCKET_EINTR +#define SOCKET_EWOULDBLOCK SOCEWOULDBLOCK +#define SOCKET_ENFILE SOCENFILE +#define SOCKET_EMFILE SOCEMFILE +#define closesocket(A) soclose(A) +#else /* Unix */ +#define socket_errno errno +#define closesocket(A) close(A) +#define SOCKET_EINTR EINTR +#define SOCKET_EAGAIN EAGAIN +#define SOCKET_ETIMEDOUT SOCKET_EINTR +#define SOCKET_EWOULDBLOCK EWOULDBLOCK +#define SOCKET_ENFILE ENFILE +#define SOCKET_EMFILE EMFILE +#endif + +typedef uint8 int7; /* Most effective integer 0 <= x <= 127 */ +typedef short int15; /* Most effective integer 0 <= x <= 32767 */ +typedef char *my_string; /* String of characters */ +typedef unsigned long size_s; /* Size of strings (In string-funcs) */ +typedef int myf; /* Type of MyFlags in my_funcs */ +#ifndef byte_defined +typedef char byte; /* Smallest addressable unit */ +#endif +typedef char my_bool; /* Small bool */ +#if !defined(bool) && !defined(bool_defined) && (!defined(HAVE_BOOL) || !defined(__cplusplus)) +typedef char bool; /* Ordinary boolean values 0 1 */ +#endif + /* Macros for converting *constants* to the right type */ +#define INT8(v) (int8) (v) +#define INT16(v) (int16) (v) +#define INT32(v) (int32) (v) +#define MYF(v) (myf) (v) + +#ifndef LL +#ifdef HAVE_LONG_LONG +#define LL(A) A ## LL +#else +#define LL(A) A ## L +#endif +#endif + +#ifndef ULL +#ifdef HAVE_LONG_LONG +#define ULL(A) A ## ULL +#else +#define ULL(A) A ## UL +#endif +#endif + +/* + Defines to make it possible to prioritize register assignments. No + longer that important with modern compilers. +*/ +#ifndef USING_X +#define reg1 register +#define reg2 register +#define reg3 register +#define reg4 register +#define reg5 register +#define reg6 register +#define reg7 register +#define reg8 register +#define reg9 register +#define reg10 register +#define reg11 register +#define reg12 register +#define reg13 register +#define reg14 register +#define reg15 register +#define reg16 register +#endif + +/* + Sometimes we want to make sure that the variable is not put into + a register in debugging mode so we can see its value in the core +*/ + +#ifndef DBUG_OFF +#define dbug_volatile volatile +#else +#define dbug_volatile +#endif + +/* Defines for time function */ +#define SCALE_SEC 100 +#define SCALE_USEC 10000 +#define MY_HOW_OFTEN_TO_ALARM 2 /* How often we want info on screen */ +#define MY_HOW_OFTEN_TO_WRITE 1000 /* How often we want info on screen */ + +#ifdef HAVE_TIMESPEC_TS_SEC +#ifndef set_timespec +#define set_timespec(ABSTIME,SEC) \ +{ \ + (ABSTIME).ts_sec=time(0) + (time_t) (SEC); \ + (ABSTIME).ts_nsec=0; \ +} +#endif /* !set_timespec */ +#ifndef set_timespec_nsec +#define set_timespec_nsec(ABSTIME,NSEC) \ +{ \ + ulonglong now= my_getsystime() + (NSEC/100); \ + (ABSTIME).ts_sec= (now / ULL(10000000)); \ + (ABSTIME).ts_nsec= (now % ULL(10000000) * 100 + ((NSEC) % 100)); \ +} +#endif /* !set_timespec_nsec */ +#else +#ifndef set_timespec +#define set_timespec(ABSTIME,SEC) \ +{\ + struct timeval tv;\ + gettimeofday(&tv,0);\ + (ABSTIME).tv_sec=tv.tv_sec+(time_t) (SEC);\ + (ABSTIME).tv_nsec=tv.tv_usec*1000;\ +} +#endif /* !set_timespec */ +#ifndef set_timespec_nsec +#define set_timespec_nsec(ABSTIME,NSEC) \ +{\ + ulonglong now= my_getsystime() + (NSEC/100); \ + (ABSTIME).tv_sec= (now / ULL(10000000)); \ + (ABSTIME).tv_nsec= (now % ULL(10000000) * 100 + ((NSEC) % 100)); \ +} +#endif /* !set_timespec_nsec */ +#endif /* HAVE_TIMESPEC_TS_SEC */ + +/* + Define-funktions for reading and storing in machine independent format + (low byte first) +*/ + +/* Optimized store functions for Intel x86 */ +#if defined(__i386__) && !defined(_WIN64) +#define sint2korr(A) (*((int16 *) (A))) +#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ + (((uint32) 255L << 24) | \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0])) : \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0]))) +#define sint4korr(A) (*((long *) (A))) +#define uint2korr(A) (*((uint16 *) (A))) +#ifdef HAVE_purify +#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16)) +#else +/* + ATTENTION ! + + Please, note, uint3korr reads 4 bytes (not 3) ! + It means, that you have to provide enough allocated space ! +*/ +#define uint3korr(A) (long) (*((unsigned int *) (A)) & 0xFFFFFF) +#endif +#define uint4korr(A) (*((unsigned long *) (A))) +#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint8korr(A) (*((ulonglong *) (A))) +#define sint8korr(A) (*((longlong *) (A))) +#define int2store(T,A) *((uint16*) (T))= (uint16) (A) +#define int3store(T,A) do { *(T)= (uchar) ((A));\ + *(T+1)=(uchar) (((uint) (A) >> 8));\ + *(T+2)=(uchar) (((A) >> 16)); } while (0) +#define int4store(T,A) *((long *) (T))= (long) (A) +#define int5store(T,A) do { *(T)= (uchar)((A));\ + *((T)+1)=(uchar) (((A) >> 8));\ + *((T)+2)=(uchar) (((A) >> 16));\ + *((T)+3)=(uchar) (((A) >> 24)); \ + *((T)+4)=(uchar) (((A) >> 32)); } while(0) +#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A) + +typedef union { + double v; + long m[2]; +} doubleget_union; +#define doubleget(V,M) \ +do { doubleget_union _tmp; \ + _tmp.m[0] = *((long*)(M)); \ + _tmp.m[1] = *(((long*) (M))+1); \ + (V) = _tmp.v; } while(0) +#define doublestore(T,V) do { *((long *) T) = ((doubleget_union *)&V)->m[0]; \ + *(((long *) T)+1) = ((doubleget_union *)&V)->m[1]; \ + } while (0) +#define float4get(V,M) do { *((long *) &(V)) = *((long*) (M)); } while(0) +#define float8get(V,M) doubleget((V),(M)) +#define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float)) +#define floatstore(T,V) memcpy((byte*)(T), (byte*)(&V),sizeof(float)) +#define floatget(V,M) memcpy((byte*) &V,(byte*) (M),sizeof(float)) +#define float8store(V,M) doublestore((V),(M)) +#endif /* __i386__ */ + +#ifndef sint2korr +/* + We're here if it's not a IA-32 architecture (Win32 and UNIX IA-32 defines + were done before) +*/ +#define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\ + ((int16) ((int16) (A)[1]) << 8)) +#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ + (((uint32) 255L << 24) | \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0])) : \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0]))) +#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\ + (((int32) ((uchar) (A)[1]) << 8)) +\ + (((int32) ((uchar) (A)[2]) << 16)) +\ + (((int32) ((int16) (A)[3]) << 24))) +#define sint8korr(A) (longlong) uint8korr(A) +#define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\ + ((uint16) ((uchar) (A)[1]) << 8)) +#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16)) +#define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) (((uint32) ((uchar) (A)[4])) +\ + (((uint32) ((uchar) (A)[5])) << 8) +\ + (((uint32) ((uchar) (A)[6])) << 16) +\ + (((uint32) ((uchar) (A)[7])) << 24))) <<\ + 32)) +#define int2store(T,A) do { uint def_temp= (uint) (A) ;\ + *((uchar*) (T))= (uchar)(def_temp); \ + *((uchar*) (T)+1)=(uchar)((def_temp >> 8)); \ + } while(0) +#define int3store(T,A) do { /*lint -save -e734 */\ + *((uchar*)(T))=(uchar) ((A));\ + *((uchar*) (T)+1)=(uchar) (((A) >> 8));\ + *((uchar*)(T)+2)=(uchar) (((A) >> 16)); \ + /*lint -restore */} while(0) +#define int4store(T,A) do { *((char *)(T))=(char) ((A));\ + *(((char *)(T))+1)=(char) (((A) >> 8));\ + *(((char *)(T))+2)=(char) (((A) >> 16));\ + *(((char *)(T))+3)=(char) (((A) >> 24)); } while(0) +#define int5store(T,A) do { *((char *)(T))=((A));\ + *(((char *)(T))+1)=(((A) >> 8));\ + *(((char *)(T))+2)=(((A) >> 16));\ + *(((char *)(T))+3)=(((A) >> 24)); \ + *(((char *)(T))+4)=(((A) >> 32)); } while(0) +#define int8store(T,A) do { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \ + int4store((T),def_temp); \ + int4store((T+4),def_temp2); } while(0) +#ifdef WORDS_BIGENDIAN +#define float4store(T,A) do { *(T)= ((byte *) &A)[3];\ + *((T)+1)=(char) ((byte *) &A)[2];\ + *((T)+2)=(char) ((byte *) &A)[1];\ + *((T)+3)=(char) ((byte *) &A)[0]; } while(0) + +#define float4get(V,M) do { float def_temp;\ + ((byte*) &def_temp)[0]=(M)[3];\ + ((byte*) &def_temp)[1]=(M)[2];\ + ((byte*) &def_temp)[2]=(M)[1];\ + ((byte*) &def_temp)[3]=(M)[0];\ + (V)=def_temp; } while(0) +#define float8store(T,V) do { *(T)= ((byte *) &V)[7];\ + *((T)+1)=(char) ((byte *) &V)[6];\ + *((T)+2)=(char) ((byte *) &V)[5];\ + *((T)+3)=(char) ((byte *) &V)[4];\ + *((T)+4)=(char) ((byte *) &V)[3];\ + *((T)+5)=(char) ((byte *) &V)[2];\ + *((T)+6)=(char) ((byte *) &V)[1];\ + *((T)+7)=(char) ((byte *) &V)[0]; } while(0) + +#define float8get(V,M) do { double def_temp;\ + ((byte*) &def_temp)[0]=(M)[7];\ + ((byte*) &def_temp)[1]=(M)[6];\ + ((byte*) &def_temp)[2]=(M)[5];\ + ((byte*) &def_temp)[3]=(M)[4];\ + ((byte*) &def_temp)[4]=(M)[3];\ + ((byte*) &def_temp)[5]=(M)[2];\ + ((byte*) &def_temp)[6]=(M)[1];\ + ((byte*) &def_temp)[7]=(M)[0];\ + (V) = def_temp; } while(0) +#else +#define float4get(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(float)) +#define float4store(V,M) memcpy_fixed((byte*) V,(byte*) (&M),sizeof(float)) + +#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) +#define doublestore(T,V) do { *(((char*)T)+0)=(char) ((byte *) &V)[4];\ + *(((char*)T)+1)=(char) ((byte *) &V)[5];\ + *(((char*)T)+2)=(char) ((byte *) &V)[6];\ + *(((char*)T)+3)=(char) ((byte *) &V)[7];\ + *(((char*)T)+4)=(char) ((byte *) &V)[0];\ + *(((char*)T)+5)=(char) ((byte *) &V)[1];\ + *(((char*)T)+6)=(char) ((byte *) &V)[2];\ + *(((char*)T)+7)=(char) ((byte *) &V)[3]; }\ + while(0) +#define doubleget(V,M) do { double def_temp;\ + ((byte*) &def_temp)[0]=(M)[4];\ + ((byte*) &def_temp)[1]=(M)[5];\ + ((byte*) &def_temp)[2]=(M)[6];\ + ((byte*) &def_temp)[3]=(M)[7];\ + ((byte*) &def_temp)[4]=(M)[0];\ + ((byte*) &def_temp)[5]=(M)[1];\ + ((byte*) &def_temp)[6]=(M)[2];\ + ((byte*) &def_temp)[7]=(M)[3];\ + (V) = def_temp; } while(0) +#endif /* __FLOAT_WORD_ORDER */ + +#define float8get(V,M) doubleget((V),(M)) +#define float8store(V,M) doublestore((V),(M)) +#endif /* WORDS_BIGENDIAN */ + +#endif /* sint2korr */ + +/* + Macro for reading 32-bit integer from network byte order (big-endian) + from unaligned memory location. +*/ +#define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) |\ + (((uint32) ((uchar) (A)[2])) << 8) |\ + (((uint32) ((uchar) (A)[1])) << 16) |\ + (((uint32) ((uchar) (A)[0])) << 24)) +/* + Define-funktions for reading and storing in machine format from/to + short/long to/from some place in memory V should be a (not + register) variable, M is a pointer to byte +*/ + +#ifdef WORDS_BIGENDIAN + +#define ushortget(V,M) do { V = (uint16) (((uint16) ((uchar) (M)[1]))+\ + ((uint16) ((uint16) (M)[0]) << 8)); } while(0) +#define shortget(V,M) do { V = (short) (((short) ((uchar) (M)[1]))+\ + ((short) ((short) (M)[0]) << 8)); } while(0) +#define longget(V,M) do { int32 def_temp;\ + ((byte*) &def_temp)[0]=(M)[0];\ + ((byte*) &def_temp)[1]=(M)[1];\ + ((byte*) &def_temp)[2]=(M)[2];\ + ((byte*) &def_temp)[3]=(M)[3];\ + (V)=def_temp; } while(0) +#define ulongget(V,M) do { uint32 def_temp;\ + ((byte*) &def_temp)[0]=(M)[0];\ + ((byte*) &def_temp)[1]=(M)[1];\ + ((byte*) &def_temp)[2]=(M)[2];\ + ((byte*) &def_temp)[3]=(M)[3];\ + (V)=def_temp; } while(0) +#define shortstore(T,A) do { uint def_temp=(uint) (A) ;\ + *(((char*)T)+1)=(char)(def_temp); \ + *(((char*)T)+0)=(char)(def_temp >> 8); } while(0) +#define longstore(T,A) do { *(((char*)T)+3)=((A));\ + *(((char*)T)+2)=(((A) >> 8));\ + *(((char*)T)+1)=(((A) >> 16));\ + *(((char*)T)+0)=(((A) >> 24)); } while(0) + +#define floatget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(float)) +#define floatstore(T,V) memcpy_fixed((byte*) (T),(byte*)(&V),sizeof(float)) +#define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double)) +#define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double)) +#define longlongget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(ulonglong)) +#define longlongstore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(ulonglong)) + +#else + +#define ushortget(V,M) do { V = uint2korr(M); } while(0) +#define shortget(V,M) do { V = sint2korr(M); } while(0) +#define longget(V,M) do { V = sint4korr(M); } while(0) +#define ulongget(V,M) do { V = uint4korr(M); } while(0) +#define shortstore(T,V) int2store(T,V) +#define longstore(T,V) int4store(T,V) +#ifndef floatstore +#define floatstore(T,V) memcpy_fixed((byte*) (T),(byte*) (&V),sizeof(float)) +#define floatget(V,M) memcpy_fixed((byte*) &V, (byte*) (M), sizeof(float)) +#endif +#ifndef doubleget +#define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double)) +#define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double)) +#endif /* doubleget */ +#define longlongget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(ulonglong)) +#define longlongstore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(ulonglong)) + +#endif /* WORDS_BIGENDIAN */ + +/* sprintf does not always return the number of bytes :- */ +#ifdef SPRINTF_RETURNS_INT +#define my_sprintf(buff,args) sprintf args +#else +#ifdef SPRINTF_RETURNS_PTR +#define my_sprintf(buff,args) ((int)(sprintf args - buff)) +#else +#define my_sprintf(buff,args) ((ulong) sprintf args, (ulong) strlen(buff)) +#endif +#endif + +#ifndef THREAD +#define thread_safe_increment(V,L) (V)++ +#define thread_safe_add(V,C,L) (V)+=(C) +#define thread_safe_sub(V,C,L) (V)-=(C) +#define statistic_increment(V,L) (V)++ +#define statistic_add(V,C,L) (V)+=(C) +#endif + +#ifdef HAVE_CHARSET_utf8 +#define MYSQL_UNIVERSAL_CLIENT_CHARSET "utf8" +#else +#define MYSQL_UNIVERSAL_CLIENT_CHARSET MYSQL_DEFAULT_CHARSET_NAME +#endif + +#if defined(EMBEDDED_LIBRARY) && !defined(HAVE_EMBEDDED_PRIVILEGE_CONTROL) +#define NO_EMBEDDED_ACCESS_CHECKS +#endif + +#endif /* my_global_h */ diff --git a/Src/3rd_party/mysql/include/my_list.h b/Src/3rd_party/mysql/include/my_list.h new file mode 100644 index 0000000..9259869 --- /dev/null +++ b/Src/3rd_party/mysql/include/my_list.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _list_h_ +#define _list_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_list { + struct st_list *prev,*next; + void *data; +} LIST; + +typedef int (*list_walk_action)(void *,void *); + +extern LIST *list_add(LIST *root,LIST *element); +extern LIST *list_delete(LIST *root,LIST *element); +extern LIST *list_cons(void *data,LIST *root); +extern LIST *list_reverse(LIST *root); +extern void list_free(LIST *root,unsigned int free_data); +extern unsigned int list_length(LIST *); +extern int list_walk(LIST *,list_walk_action action,gptr argument); + +#define list_rest(a) ((a)->next) +#define list_push(a,b) (a)=list_cons((b),(a)) +#define list_pop(A) {LIST *old=(A); (A)=list_delete(old,old) ; my_free((gptr) old,MYF(MY_FAE)); } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Src/3rd_party/mysql/include/my_sys.h b/Src/3rd_party/mysql/include/my_sys.h new file mode 100644 index 0000000..7603180 --- /dev/null +++ b/Src/3rd_party/mysql/include/my_sys.h @@ -0,0 +1,903 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _my_sys_h +#define _my_sys_h +C_MODE_START + +#ifdef HAVE_AIOWAIT +#include /* Used by record-cache */ +typedef struct my_aio_result { + aio_result_t result; + int pending; +} my_aio_result; +#endif + +#ifndef THREAD +extern int NEAR my_errno; /* Last error in mysys */ +#else +#include +#endif + +#ifndef _m_ctype_h +#include /* for CHARSET_INFO */ +#endif + +#include +#include + +#define MYSYS_PROGRAM_USES_CURSES() { error_handler_hook = my_message_curses; mysys_uses_curses=1; } +#define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;} +#define MY_INIT(name); { my_progname= name; my_init(); } + +#define ERRMSGSIZE (SC_MAXWIDTH) /* Max length of a error message */ +#define NRERRBUFFS (2) /* Buffers for parameters */ +#define MY_FILE_ERROR ((uint) ~0) + + /* General bitmaps for my_func's */ +#define MY_FFNF 1 /* Fatal if file not found */ +#define MY_FNABP 2 /* Fatal if not all bytes read/writen */ +#define MY_NABP 4 /* Error if not all bytes read/writen */ +#define MY_FAE 8 /* Fatal if any error */ +#define MY_WME 16 /* Write message on error */ +#define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */ +#define MY_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */ +#define MY_RAID 64 /* Support for RAID */ +#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */ +#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */ +#define MY_LINK_WARNING 32 /* my_redel() gives warning if links */ +#define MY_COPYTIME 64 /* my_redel() copys time */ +#define MY_DELETE_OLD 256 /* my_create_with_symlink() */ +#define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */ +#define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */ +#define MY_REDEL_MAKE_BACKUP 256 +#define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */ +#define MY_DONT_WAIT 64 /* my_lock() don't wait if can't lock */ +#define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */ +#define MY_ALLOW_ZERO_PTR 64 /* my_realloc() ; zero ptr -> malloc */ +#define MY_FREE_ON_ERROR 128 /* my_realloc() ; Free old ptr on error */ +#define MY_HOLD_ON_ERROR 256 /* my_realloc() ; Return old ptr on error */ +#define MY_THREADSAFE 128 /* pread/pwrite: Don't allow interrupts */ +#define MY_DONT_OVERWRITE_FILE 1024 /* my_copy: Don't overwrite file */ + +#define MY_CHECK_ERROR 1 /* Params to my_end; Check open-close */ +#define MY_GIVE_INFO 2 /* Give time info about process*/ + +#define ME_HIGHBYTE 8 /* Shift for colours */ +#define ME_NOCUR 1 /* Don't use curses message */ +#define ME_OLDWIN 2 /* Use old window */ +#define ME_BELL 4 /* Ring bell then printing message */ +#define ME_HOLDTANG 8 /* Don't delete last keys */ +#define ME_WAITTOT 16 /* Wait for errtime secs of for a action */ +#define ME_WAITTANG 32 /* Wait for a user action */ +#define ME_NOREFRESH 64 /* Dont refresh screen */ +#define ME_NOINPUT 128 /* Dont use the input libary */ +#define ME_COLOUR1 ((1 << ME_HIGHBYTE)) /* Possibly error-colours */ +#define ME_COLOUR2 ((2 << ME_HIGHBYTE)) +#define ME_COLOUR3 ((3 << ME_HIGHBYTE)) + + /* Bits in last argument to fn_format */ +#define MY_REPLACE_DIR 1 /* replace dir in name with 'dir' */ +#define MY_REPLACE_EXT 2 /* replace extension with 'ext' */ +#define MY_UNPACK_FILENAME 4 /* Unpack name (~ -> home) */ +#define MY_PACK_FILENAME 8 /* Pack name (home -> ~) */ +#define MY_RESOLVE_SYMLINKS 16 /* Resolve all symbolic links */ +#define MY_RETURN_REAL_PATH 32 /* return full path for file */ +#define MY_SAFE_PATH 64 /* Return NULL if too long path */ +#define MY_RELATIVE_PATH 128 /* name is relative to 'dir' */ + + /* My seek flags */ +#define MY_SEEK_SET 0 +#define MY_SEEK_CUR 1 +#define MY_SEEK_END 2 + + /* Some constants */ +#define MY_WAIT_FOR_USER_TO_FIX_PANIC 60 /* in seconds */ +#define MY_WAIT_GIVE_USER_A_MESSAGE 10 /* Every 10 times of prev */ +#define MIN_COMPRESS_LENGTH 50 /* Don't compress small bl. */ +#define DFLT_INIT_HITS 3 + + /* root_alloc flags */ +#define MY_KEEP_PREALLOC 1 +#define MY_MARK_BLOCKS_FREE 2 /* move used to free list and reuse them */ + + /* Internal error numbers (for assembler functions) */ +#define MY_ERRNO_EDOM 33 +#define MY_ERRNO_ERANGE 34 + + /* Bits for get_date timeflag */ +#define GETDATE_DATE_TIME 1 +#define GETDATE_SHORT_DATE 2 +#define GETDATE_HHMMSSTIME 4 +#define GETDATE_GMT 8 +#define GETDATE_FIXEDLENGTH 16 + + /* defines when allocating data */ +#ifdef SAFEMALLOC +#define my_malloc(SZ,FLAG) _mymalloc((SZ), __FILE__, __LINE__, FLAG ) +#define my_malloc_ci(SZ,FLAG) _mymalloc((SZ), sFile, uLine, FLAG ) +#define my_realloc(PTR,SZ,FLAG) _myrealloc((PTR), (SZ), __FILE__, __LINE__, FLAG ) +#define my_checkmalloc() _sanity( __FILE__, __LINE__ ) +#define my_free(PTR,FLAG) _myfree((PTR), __FILE__, __LINE__,FLAG) +#define my_memdup(A,B,C) _my_memdup((A),(B), __FILE__,__LINE__,C) +#define my_strdup(A,C) _my_strdup((A), __FILE__,__LINE__,C) +#define my_strdup_with_length(A,B,C) _my_strdup_with_length((A),(B),__FILE__,__LINE__,C) +#define TRASH(A,B) bfill(A, B, 0x8F) +#define QUICK_SAFEMALLOC sf_malloc_quick=1 +#define NORMAL_SAFEMALLOC sf_malloc_quick=0 +extern uint sf_malloc_prehunc,sf_malloc_endhunc,sf_malloc_quick; +extern ulonglong sf_malloc_mem_limit; + +#define CALLER_INFO_PROTO , const char *sFile, uint uLine +#define CALLER_INFO , __FILE__, __LINE__ +#define ORIG_CALLER_INFO , sFile, uLine +#else +#define my_checkmalloc() +#undef TERMINATE +#define TERMINATE(A) {} +#define QUICK_SAFEMALLOC +#define NORMAL_SAFEMALLOC +extern gptr my_malloc(uint Size,myf MyFlags); +#define my_malloc_ci(SZ,FLAG) my_malloc( SZ, FLAG ) +extern gptr my_realloc(gptr oldpoint,uint Size,myf MyFlags); +extern void my_no_flags_free(gptr ptr); +extern gptr my_memdup(const byte *from,uint length,myf MyFlags); +extern char *my_strdup(const char *from,myf MyFlags); +extern char *my_strdup_with_length(const byte *from, uint length, + myf MyFlags); +/* we do use FG (as a no-op) in below so that a typo on FG is caught */ +#define my_free(PTR,FG) ((void)FG,my_no_flags_free(PTR)) +#define CALLER_INFO_PROTO /* nothing */ +#define CALLER_INFO /* nothing */ +#define ORIG_CALLER_INFO /* nothing */ +#define TRASH(A,B) /* nothing */ +#endif + +#ifdef HAVE_LARGE_PAGES +extern uint my_get_large_page_size(void); +extern gptr my_large_malloc(uint size, myf my_flags); +extern void my_large_free(gptr ptr, myf my_flags); +#else +#define my_get_large_page_size() (0) +#define my_large_malloc(A,B) my_malloc_lock((A),(B)) +#define my_large_free(A,B) my_free_lock((A),(B)) +#endif /* HAVE_LARGE_PAGES */ + +#ifdef HAVE_ALLOCA +#if defined(_AIX) && !defined(__GNUC__) && !defined(_AIX43) +#pragma alloca +#endif /* _AIX */ +#if defined(__MWERKS__) +#undef alloca +#define alloca _alloca +#endif /* __MWERKS__ */ +#if defined(__GNUC__) && !defined(HAVE_ALLOCA_H) && ! defined(alloca) +#define alloca __builtin_alloca +#endif /* GNUC */ +#define my_alloca(SZ) alloca((size_t) (SZ)) +#define my_afree(PTR) {} +#else +#define my_alloca(SZ) my_malloc(SZ,MYF(0)) +#define my_afree(PTR) my_free(PTR,MYF(MY_WME)) +#endif /* HAVE_ALLOCA */ + +#ifdef MSDOS +#ifdef __ZTC__ +void * __CDECL halloc(long count,size_t length); +void __CDECL hfree(void *ptr); +#endif +#if defined(USE_HALLOC) +#if defined(_VCM_) || defined(M_IC80386) +#undef USE_HALLOC +#endif +#endif +#ifdef USE_HALLOC +#define malloc(a) halloc((long) (a),1) +#define free(a) hfree(a) +#endif +#endif /* MSDOS */ + +#ifndef errno /* did we already get it? */ +#ifdef HAVE_ERRNO_AS_DEFINE +#include /* errno is a define */ +#else +extern int errno; /* declare errno */ +#endif +#endif /* #ifndef errno */ +extern char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; +extern char *home_dir; /* Home directory for user */ +extern const char *my_progname; /* program-name (printed in errors) */ +extern char NEAR curr_dir[]; /* Current directory for user */ +extern int (*error_handler_hook)(uint my_err, const char *str,myf MyFlags); +extern int (*fatal_error_handler_hook)(uint my_err, const char *str, + myf MyFlags); +extern uint my_file_limit; + +#ifdef HAVE_LARGE_PAGES +extern my_bool my_use_large_pages; +extern uint my_large_page_size; +#endif + +/* charsets */ +extern CHARSET_INFO *default_charset_info; +extern CHARSET_INFO *all_charsets[256]; +extern CHARSET_INFO compiled_charsets[]; + +/* statistics */ +extern ulong my_file_opened,my_stream_opened, my_tmp_file_created; +extern uint mysys_usage_id; +extern my_bool my_init_done; + + /* Point to current my_message() */ +extern void (*my_sigtstp_cleanup)(void), + /* Executed before jump to shell */ + (*my_sigtstp_restart)(void), + (*my_abort_hook)(int); + /* Executed when comming from shell */ +extern int NEAR my_umask, /* Default creation mask */ + NEAR my_umask_dir, + NEAR my_recived_signals, /* Signals we have got */ + NEAR my_safe_to_handle_signal, /* Set when allowed to SIGTSTP */ + NEAR my_dont_interrupt; /* call remember_intr when set */ +extern my_bool NEAR mysys_uses_curses, my_use_symdir; +extern ulong sf_malloc_cur_memory, sf_malloc_max_memory; + +extern ulong my_default_record_cache_size; +extern my_bool NEAR my_disable_locking,NEAR my_disable_async_io, + NEAR my_disable_flush_key_blocks, NEAR my_disable_symlinks; +extern char wild_many,wild_one,wild_prefix; +extern const char *charsets_dir; +extern char *defaults_extra_file; +extern const char *defaults_group_suffix; +extern const char *defaults_file; + +extern my_bool timed_mutexes; + +typedef struct wild_file_pack /* Struct to hold info when selecting files */ +{ + uint wilds; /* How many wildcards */ + uint not_pos; /* Start of not-theese-files */ + my_string *wild; /* Pointer to wildcards */ +} WF_PACK; + +enum loglevel { + ERROR_LEVEL, + WARNING_LEVEL, + INFORMATION_LEVEL +}; + +enum cache_type +{ + TYPE_NOT_SET= 0, READ_CACHE, WRITE_CACHE, + SEQ_READ_APPEND /* sequential read or append */, + READ_FIFO, READ_NET,WRITE_NET}; + +enum flush_type +{ + FLUSH_KEEP, FLUSH_RELEASE, FLUSH_IGNORE_CHANGED, FLUSH_FORCE_WRITE +}; + +typedef struct st_record_cache /* Used when cacheing records */ +{ + File file; + int rc_seek,error,inited; + uint rc_length,read_length,reclength; + my_off_t rc_record_pos,end_of_file; + byte *rc_buff,*rc_buff2,*rc_pos,*rc_end,*rc_request_pos; +#ifdef HAVE_AIOWAIT + int use_async_io; + my_aio_result aio_result; +#endif + enum cache_type type; +} RECORD_CACHE; + +enum file_type +{ + UNOPEN = 0, FILE_BY_OPEN, FILE_BY_CREATE, STREAM_BY_FOPEN, STREAM_BY_FDOPEN, + FILE_BY_MKSTEMP, FILE_BY_DUP +}; + +struct st_my_file_info +{ + my_string name; + enum file_type type; +#if defined(THREAD) && !defined(HAVE_PREAD) + pthread_mutex_t mutex; +#endif +}; + +extern struct st_my_file_info *my_file_info; + +typedef struct st_my_tmpdir +{ + char **list; + uint cur, max; +#ifdef THREAD + pthread_mutex_t mutex; +#endif +} MY_TMPDIR; + +typedef struct st_dynamic_array +{ + char *buffer; + uint elements,max_element; + uint alloc_increment; + uint size_of_element; +} DYNAMIC_ARRAY; + +typedef struct st_dynamic_string +{ + char *str; + uint length,max_length,alloc_increment; +} DYNAMIC_STRING; + +struct st_io_cache; +typedef int (*IO_CACHE_CALLBACK)(struct st_io_cache*); + +#ifdef THREAD +typedef struct st_io_cache_share +{ + /* to sync on reads into buffer */ + pthread_mutex_t mutex; + pthread_cond_t cond; + int count, total; + /* actual IO_CACHE that filled the buffer */ + struct st_io_cache *active; +#ifdef NOT_YET_IMPLEMENTED + /* whether the structure should be free'd */ + my_bool alloced; +#endif +} IO_CACHE_SHARE; +#endif + +typedef struct st_io_cache /* Used when cacheing files */ +{ + /* Offset in file corresponding to the first byte of byte* buffer. */ + my_off_t pos_in_file; + /* + The offset of end of file for READ_CACHE and WRITE_CACHE. + For SEQ_READ_APPEND it the maximum of the actual end of file and + the position represented by read_end. + */ + my_off_t end_of_file; + /* Points to current read position in the buffer */ + byte *read_pos; + /* the non-inclusive boundary in the buffer for the currently valid read */ + byte *read_end; + byte *buffer; /* The read buffer */ + /* Used in ASYNC_IO */ + byte *request_pos; + + /* Only used in WRITE caches and in SEQ_READ_APPEND to buffer writes */ + byte *write_buffer; + /* + Only used in SEQ_READ_APPEND, and points to the current read position + in the write buffer. Note that reads in SEQ_READ_APPEND caches can + happen from both read buffer (byte* buffer) and write buffer + (byte* write_buffer). + */ + byte *append_read_pos; + /* Points to current write position in the write buffer */ + byte *write_pos; + /* The non-inclusive boundary of the valid write area */ + byte *write_end; + + /* + Current_pos and current_end are convenience variables used by + my_b_tell() and other routines that need to know the current offset + current_pos points to &write_pos, and current_end to &write_end in a + WRITE_CACHE, and &read_pos and &read_end respectively otherwise + */ + byte **current_pos, **current_end; +#ifdef THREAD + /* + The lock is for append buffer used in SEQ_READ_APPEND cache + need mutex copying from append buffer to read buffer. + */ + pthread_mutex_t append_buffer_lock; + /* + The following is used when several threads are reading the + same file in parallel. They are synchronized on disk + accesses reading the cached part of the file asynchronously. + It should be set to NULL to disable the feature. Only + READ_CACHE mode is supported. + */ + IO_CACHE_SHARE *share; +#endif + /* + A caller will use my_b_read() macro to read from the cache + if the data is already in cache, it will be simply copied with + memcpy() and internal variables will be accordinging updated with + no functions invoked. However, if the data is not fully in the cache, + my_b_read() will call read_function to fetch the data. read_function + must never be invoked directly. + */ + int (*read_function)(struct st_io_cache *,byte *,uint); + /* + Same idea as in the case of read_function, except my_b_write() needs to + be replaced with my_b_append() for a SEQ_READ_APPEND cache + */ + int (*write_function)(struct st_io_cache *,const byte *,uint); + /* + Specifies the type of the cache. Depending on the type of the cache + certain operations might not be available and yield unpredicatable + results. Details to be documented later + */ + enum cache_type type; + /* + Callbacks when the actual read I/O happens. These were added and + are currently used for binary logging of LOAD DATA INFILE - when a + block is read from the file, we create a block create/append event, and + when IO_CACHE is closed, we create an end event. These functions could, + of course be used for other things + */ + IO_CACHE_CALLBACK pre_read; + IO_CACHE_CALLBACK post_read; + IO_CACHE_CALLBACK pre_close; + /* + Counts the number of times, when we were forced to use disk. We use it to + increase the binlog_cache_disk_use status variable. + */ + ulong disk_writes; + void* arg; /* for use by pre/post_read */ + char *file_name; /* if used with 'open_cached_file' */ + char *dir,*prefix; + File file; /* file descriptor */ + /* + seek_not_done is set by my_b_seek() to inform the upcoming read/write + operation that a seek needs to be preformed prior to the actual I/O + error is 0 if the cache operation was successful, -1 if there was a + "hard" error, and the actual number of I/O-ed bytes if the read/write was + partial. + */ + int seek_not_done,error; + /* buffer_length is memory size allocated for buffer or write_buffer */ + uint buffer_length; + /* read_length is the same as buffer_length except when we use async io */ + uint read_length; + myf myflags; /* Flags used to my_read/my_write */ + /* + alloced_buffer is 1 if the buffer was allocated by init_io_cache() and + 0 if it was supplied by the user. + Currently READ_NET is the only one that will use a buffer allocated + somewhere else + */ + my_bool alloced_buffer; +#ifdef HAVE_AIOWAIT + /* + As inidicated by ifdef, this is for async I/O, which is not currently + used (because it's not reliable on all systems) + */ + uint inited; + my_off_t aio_read_pos; + my_aio_result aio_result; +#endif +} IO_CACHE; + +typedef int (*qsort2_cmp)(const void *, const void *, const void *); + + /* defines for mf_iocache */ + + /* Test if buffer is inited */ +#define my_b_clear(info) (info)->buffer=0 +#define my_b_inited(info) (info)->buffer +#define my_b_EOF INT_MIN + +#define my_b_read(info,Buffer,Count) \ + ((info)->read_pos + (Count) <= (info)->read_end ?\ + (memcpy(Buffer,(info)->read_pos,(size_t) (Count)), \ + ((info)->read_pos+=(Count)),0) :\ + (*(info)->read_function)((info),Buffer,Count)) + +#define my_b_write(info,Buffer,Count) \ + ((info)->write_pos + (Count) <=(info)->write_end ?\ + (memcpy((info)->write_pos, (Buffer), (size_t)(Count)),\ + ((info)->write_pos+=(Count)),0) : \ + (*(info)->write_function)((info),(Buffer),(Count))) + +#define my_b_get(info) \ + ((info)->read_pos != (info)->read_end ?\ + ((info)->read_pos++, (int) (uchar) (info)->read_pos[-1]) :\ + _my_b_get(info)) + + /* my_b_write_byte dosn't have any err-check */ +#define my_b_write_byte(info,chr) \ + (((info)->write_pos < (info)->write_end) ?\ + ((*(info)->write_pos++)=(chr)) :\ + (_my_b_write(info,0,0) , ((*(info)->write_pos++)=(chr)))) + +#define my_b_fill_cache(info) \ + (((info)->read_end=(info)->read_pos),(*(info)->read_function)(info,0,0)) + +#define my_b_tell(info) ((info)->pos_in_file + \ + (uint) (*(info)->current_pos - (info)->request_pos)) + +/* tell write offset in the SEQ_APPEND cache */ +my_off_t my_b_append_tell(IO_CACHE* info); +my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */ + +#define my_b_bytes_in_cache(info) (uint) (*(info)->current_end - \ + *(info)->current_pos) + +typedef uint32 ha_checksum; + +/* Define the type of function to be passed to process_default_option_files */ +typedef int (*Process_option_func)(void *ctx, const char *group_name, + const char *option); + +#include + + /* Prototypes for mysys and my_func functions */ + +extern int my_copy(const char *from,const char *to,myf MyFlags); +extern int my_append(const char *from,const char *to,myf MyFlags); +extern int my_delete(const char *name,myf MyFlags); +extern int my_getwd(my_string buf,uint size,myf MyFlags); +extern int my_setwd(const char *dir,myf MyFlags); +extern int my_lock(File fd,int op,my_off_t start, my_off_t length,myf MyFlags); +extern gptr my_once_alloc(uint Size,myf MyFlags); +extern void my_once_free(void); +extern char *my_once_strdup(const char *src,myf myflags); +extern char *my_once_memdup(const char *src, uint len, myf myflags); +extern File my_open(const char *FileName,int Flags,myf MyFlags); +extern File my_register_filename(File fd, const char *FileName, + enum file_type type_of_file, + uint error_message_number, myf MyFlags); +extern File my_create(const char *FileName,int CreateFlags, + int AccsesFlags, myf MyFlags); +extern int my_close(File Filedes,myf MyFlags); +extern File my_dup(File file, myf MyFlags); +extern int my_mkdir(const char *dir, int Flags, myf MyFlags); +extern int my_readlink(char *to, const char *filename, myf MyFlags); +extern int my_realpath(char *to, const char *filename, myf MyFlags); +extern File my_create_with_symlink(const char *linkname, const char *filename, + int createflags, int access_flags, + myf MyFlags); +extern int my_delete_with_symlink(const char *name, myf MyFlags); +extern int my_rename_with_symlink(const char *from,const char *to,myf MyFlags); +extern int my_symlink(const char *content, const char *linkname, myf MyFlags); +extern uint my_read(File Filedes,byte *Buffer,uint Count,myf MyFlags); +extern uint my_pread(File Filedes,byte *Buffer,uint Count,my_off_t offset, + myf MyFlags); +extern int my_rename(const char *from,const char *to,myf MyFlags); +extern my_off_t my_seek(File fd,my_off_t pos,int whence,myf MyFlags); +extern my_off_t my_tell(File fd,myf MyFlags); +extern uint my_write(File Filedes,const byte *Buffer,uint Count, + myf MyFlags); +extern uint my_pwrite(File Filedes,const byte *Buffer,uint Count, + my_off_t offset,myf MyFlags); +extern uint my_fread(FILE *stream,byte *Buffer,uint Count,myf MyFlags); +extern uint my_fwrite(FILE *stream,const byte *Buffer,uint Count, + myf MyFlags); +extern my_off_t my_fseek(FILE *stream,my_off_t pos,int whence,myf MyFlags); +extern my_off_t my_ftell(FILE *stream,myf MyFlags); +extern gptr _mymalloc(uint uSize,const char *sFile, + uint uLine, myf MyFlag); +extern gptr _myrealloc(gptr pPtr,uint uSize,const char *sFile, + uint uLine, myf MyFlag); +extern gptr my_multi_malloc _VARARGS((myf MyFlags, ...)); +extern void _myfree(gptr pPtr,const char *sFile,uint uLine, myf MyFlag); +extern int _sanity(const char *sFile,unsigned int uLine); +extern gptr _my_memdup(const byte *from,uint length, + const char *sFile, uint uLine,myf MyFlag); +extern my_string _my_strdup(const char *from, const char *sFile, uint uLine, + myf MyFlag); +extern char *_my_strdup_with_length(const byte *from, uint length, + const char *sFile, uint uLine, + myf MyFlag); + +#ifdef __WIN__ +extern int my_access(const char *path, int amode); +#else +#define my_access access +#endif +extern int check_if_legal_filename(const char *path); + +#ifndef TERMINATE +extern void TERMINATE(FILE *file); +#endif +extern void init_glob_errs(void); +extern FILE *my_fopen(const char *FileName,int Flags,myf MyFlags); +extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags); +extern int my_fclose(FILE *fd,myf MyFlags); +extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags); +extern int my_sync(File fd, myf my_flags); +extern int my_error _VARARGS((int nr,myf MyFlags, ...)); +extern int my_printf_error _VARARGS((uint my_err, const char *format, + myf MyFlags, ...) + __attribute__ ((format (printf, 2, 4)))); +extern int my_error_register(const char **errmsgs, int first, int last); +extern const char **my_error_unregister(int first, int last); +extern int my_message(uint my_err, const char *str,myf MyFlags); +extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags); +extern int my_message_curses(uint my_err, const char *str,myf MyFlags); +extern my_bool my_init(void); +extern void my_end(int infoflag); +extern int my_redel(const char *from, const char *to, int MyFlags); +extern int my_copystat(const char *from, const char *to, int MyFlags); +extern my_string my_filename(File fd); + +#ifndef THREAD +extern void dont_break(void); +extern void allow_break(void); +#else +#define dont_break() +#define allow_break() +#endif + +extern my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist); +extern char *my_tmpdir(MY_TMPDIR *tmpdir); +extern void free_tmpdir(MY_TMPDIR *tmpdir); + +extern void my_remember_signal(int signal_number,sig_handler (*func)(int)); +extern uint dirname_part(my_string to,const char *name); +extern uint dirname_length(const char *name); +#define base_name(A) (A+dirname_length(A)) +extern int test_if_hard_path(const char *dir_name); +extern my_bool has_path(const char *name); +extern char *convert_dirname(char *to, const char *from, const char *from_end); +extern void to_unix_path(my_string name); +extern my_string fn_ext(const char *name); +extern my_string fn_same(my_string toname,const char *name,int flag); +extern my_string fn_format(my_string to,const char *name,const char *dir, + const char *form, uint flag); +extern size_s strlength(const char *str); +extern void pack_dirname(my_string to,const char *from); +extern uint unpack_dirname(my_string to,const char *from); +extern uint cleanup_dirname(my_string to,const char *from); +extern uint system_filename(my_string to,const char *from); +extern uint unpack_filename(my_string to,const char *from); +extern my_string intern_filename(my_string to,const char *from); +extern my_string directory_file_name(my_string dst, const char *src); +extern int pack_filename(my_string to, const char *name, size_s max_length); +extern my_string my_path(my_string to,const char *progname, + const char *own_pathname_part); +extern my_string my_load_path(my_string to, const char *path, + const char *own_path_prefix); +extern int wild_compare(const char *str,const char *wildstr,pbool str_is_pattern); +extern WF_PACK *wf_comp(my_string str); +extern int wf_test(struct wild_file_pack *wf_pack,const char *name); +extern void wf_end(struct wild_file_pack *buffer); +extern size_s strip_sp(my_string str); +extern void get_date(my_string to,int timeflag,time_t use_time); +extern void soundex(CHARSET_INFO *, my_string out_pntr, my_string in_pntr,pbool remove_garbage); +extern int init_record_cache(RECORD_CACHE *info,uint cachesize,File file, + uint reclength,enum cache_type type, + pbool use_async_io); +extern int read_cache_record(RECORD_CACHE *info,byte *to); +extern int end_record_cache(RECORD_CACHE *info); +extern int write_cache_record(RECORD_CACHE *info,my_off_t filepos, + const byte *record,uint length); +extern int flush_write_cache(RECORD_CACHE *info); +extern long my_clock(void); +extern sig_handler sigtstp_handler(int signal_number); +extern void handle_recived_signals(void); + +extern sig_handler my_set_alarm_variable(int signo); +extern void my_string_ptr_sort(void *base,uint items,size_s size); +extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements, + size_s size_of_element,uchar *buffer[]); +extern qsort_t qsort2(void *base_ptr, size_t total_elems, size_t size, + qsort2_cmp cmp, void *cmp_argument); +extern qsort2_cmp get_ptr_compare(uint); +void my_store_ptr(byte *buff, uint pack_length, my_off_t pos); +my_off_t my_get_ptr(byte *ptr, uint pack_length); +extern int init_io_cache(IO_CACHE *info,File file,uint cachesize, + enum cache_type type,my_off_t seek_offset, + pbool use_async_io, myf cache_myflags); +extern my_bool reinit_io_cache(IO_CACHE *info,enum cache_type type, + my_off_t seek_offset,pbool use_async_io, + pbool clear_cache); +extern void setup_io_cache(IO_CACHE* info); +extern int _my_b_read(IO_CACHE *info,byte *Buffer,uint Count); +#ifdef THREAD +extern int _my_b_read_r(IO_CACHE *info,byte *Buffer,uint Count); +extern void init_io_cache_share(IO_CACHE *info, + IO_CACHE_SHARE *s, uint num_threads); +extern void remove_io_thread(IO_CACHE *info); +#endif +extern int _my_b_seq_read(IO_CACHE *info,byte *Buffer,uint Count); +extern int _my_b_net_read(IO_CACHE *info,byte *Buffer,uint Count); +extern int _my_b_get(IO_CACHE *info); +extern int _my_b_async_read(IO_CACHE *info,byte *Buffer,uint Count); +extern int _my_b_write(IO_CACHE *info,const byte *Buffer,uint Count); +extern int my_b_append(IO_CACHE *info,const byte *Buffer,uint Count); +extern int my_b_safe_write(IO_CACHE *info,const byte *Buffer,uint Count); + +extern int my_block_write(IO_CACHE *info, const byte *Buffer, + uint Count, my_off_t pos); +extern int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock); + +#define flush_io_cache(info) my_b_flush_io_cache((info),1) + +extern int end_io_cache(IO_CACHE *info); +extern uint my_b_fill(IO_CACHE *info); +extern void my_b_seek(IO_CACHE *info,my_off_t pos); +extern uint my_b_gets(IO_CACHE *info, char *to, uint max_length); +extern my_off_t my_b_filelength(IO_CACHE *info); +extern uint my_b_printf(IO_CACHE *info, const char* fmt, ...); +extern uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list ap); +extern my_bool open_cached_file(IO_CACHE *cache,const char *dir, + const char *prefix, uint cache_size, + myf cache_myflags); +extern my_bool real_open_cached_file(IO_CACHE *cache); +extern void close_cached_file(IO_CACHE *cache); +File create_temp_file(char *to, const char *dir, const char *pfx, + int mode, myf MyFlags); +#define my_init_dynamic_array(A,B,C,D) init_dynamic_array(A,B,C,D CALLER_INFO) +#define my_init_dynamic_array_ci(A,B,C,D) init_dynamic_array(A,B,C,D ORIG_CALLER_INFO) +extern my_bool init_dynamic_array(DYNAMIC_ARRAY *array,uint element_size, + uint init_alloc,uint alloc_increment + CALLER_INFO_PROTO); +extern my_bool insert_dynamic(DYNAMIC_ARRAY *array,gptr element); +extern byte *alloc_dynamic(DYNAMIC_ARRAY *array); +extern byte *pop_dynamic(DYNAMIC_ARRAY*); +extern my_bool set_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index); +extern void get_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index); +extern void delete_dynamic(DYNAMIC_ARRAY *array); +extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index); +extern void freeze_size(DYNAMIC_ARRAY *array); +#define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element) +#define dynamic_element(array,array_index,type) ((type)((array)->buffer) +(array_index)) +#define push_dynamic(A,B) insert_dynamic(A,B) +#define reset_dynamic(array) ((array)->elements= 0) + +extern my_bool init_dynamic_string(DYNAMIC_STRING *str, const char *init_str, + uint init_alloc,uint alloc_increment); +extern my_bool dynstr_append(DYNAMIC_STRING *str, const char *append); +my_bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append, + uint length); +extern my_bool dynstr_set(DYNAMIC_STRING *str, const char *init_str); +extern my_bool dynstr_realloc(DYNAMIC_STRING *str, ulong additional_size); +extern void dynstr_free(DYNAMIC_STRING *str); +#ifdef HAVE_MLOCK +extern byte *my_malloc_lock(uint length,myf flags); +extern void my_free_lock(byte *ptr,myf flags); +#else +#define my_malloc_lock(A,B) my_malloc((A),(B)) +#define my_free_lock(A,B) my_free((A),(B)) +#endif +#define alloc_root_inited(A) ((A)->min_malloc != 0) +#define ALLOC_ROOT_MIN_BLOCK_SIZE (MALLOC_OVERHEAD + sizeof(USED_MEM) + 8) +#define clear_alloc_root(A) do { (A)->free= (A)->used= (A)->pre_alloc= 0; (A)->min_malloc=0;} while(0) +extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size, + uint pre_alloc_size); +extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size); +extern gptr multi_alloc_root(MEM_ROOT *mem_root, ...); +extern void free_root(MEM_ROOT *root, myf MyFLAGS); +extern void set_prealloc_root(MEM_ROOT *root, char *ptr); +extern void reset_root_defaults(MEM_ROOT *mem_root, uint block_size, + uint prealloc_size); +extern char *strdup_root(MEM_ROOT *root,const char *str); +extern char *strmake_root(MEM_ROOT *root,const char *str,uint len); +extern char *memdup_root(MEM_ROOT *root,const char *str,uint len); +extern int get_defaults_options(int argc, char **argv, + char **defaults, char **extra_defaults, + char **group_suffix); +extern int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv); +extern int modify_defaults_file(const char *file_location, const char *option, + const char *option_value, + const char *section_name, int remove_option); +extern int my_search_option_files(const char *conf_file, int *argc, + char ***argv, uint *args_used, + Process_option_func func, void *func_ctx); +extern void free_defaults(char **argv); +extern void my_print_default_files(const char *conf_file); +extern void print_defaults(const char *conf_file, const char **groups); +extern my_bool my_compress(byte *, ulong *, ulong *); +extern my_bool my_uncompress(byte *, ulong *, ulong *); +extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen); +extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count); +extern uint my_bit_log2(ulong value); +extern uint my_count_bits(ulonglong v); +extern uint my_count_bits_ushort(ushort v); +extern void my_sleep(ulong m_seconds); +extern ulong crc32(ulong crc, const uchar *buf, uint len); +extern uint my_set_max_open_files(uint files); +void my_free_open_file_info(void); + +ulonglong my_getsystime(void); +my_bool my_gethwaddr(uchar *to); + +#ifdef HAVE_SYS_MMAN_H +#include + +#ifndef MAP_NOSYNC +#define MAP_NOSYNC 0 +#endif + +#define my_mmap(a,b,c,d,e,f) mmap(a,b,c,d,e,f) +#ifdef HAVE_GETPAGESIZE +#define my_getpagesize() getpagesize() +#else +/* qnx ? */ +#define my_getpagesize() 8192 +#endif +#define my_munmap(a,b) munmap((a),(b)) + +#else +/* not a complete set of mmap() flags, but only those that nesessary */ +#define PROT_READ 1 +#define PROT_WRITE 2 +#define MAP_SHARED 0x0001 +#define MAP_NOSYNC 0x0800 +#define MAP_FAILED ((void *)-1) +#define MS_SYNC 0x0000 + +#ifndef __NETWARE__ +#define HAVE_MMAP +#endif + +int my_getpagesize(void); +void *my_mmap(void *, size_t, int, int, int, my_off_t); +int my_munmap(void *, size_t); +#endif + +int my_msync(int, void *, size_t, int); + +/* character sets */ +extern uint get_charset_number(const char *cs_name, uint cs_flags); +extern uint get_collation_number(const char *name); +extern const char *get_charset_name(uint cs_number); + +extern CHARSET_INFO *get_charset(uint cs_number, myf flags); +extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags); +extern CHARSET_INFO *get_charset_by_csname(const char *cs_name, + uint cs_flags, myf my_flags); +extern void free_charsets(void); +extern char *get_charsets_dir(char *buf); +extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2); +extern my_bool init_compiled_charsets(myf flags); +extern void add_compiled_collation(CHARSET_INFO *cs); +extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, + const char *from, ulong length); +#ifdef __WIN__ +#define BACKSLASH_MBTAIL +/* File system character set */ +extern CHARSET_INFO *fs_character_set(void); +#endif +extern ulong escape_quotes_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, + const char *from, ulong length); + +extern void thd_increment_bytes_sent(ulong length); +extern void thd_increment_bytes_received(ulong length); +extern void thd_increment_net_big_packet_count(ulong length); + +#ifdef __WIN__ +extern my_bool have_tcpip; /* Is set if tcpip is used */ + +/* implemented in my_windac.c */ + +int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror, + DWORD owner_rights, DWORD everybody_rights); + +void my_security_attr_free(SECURITY_ATTRIBUTES *sa); + +/* implemented in my_conio.c */ +char* my_cgets(char *string, unsigned long clen, unsigned long* plen); + +#endif +#ifdef __NETWARE__ +void netware_reg_user(const char *ip, const char *user, + const char *application); +#endif + +C_MODE_END +#include "raid.h" +#endif /* _my_sys_h */ diff --git a/Src/3rd_party/mysql/include/mysql.h b/Src/3rd_party/mysql/include/mysql.h new file mode 100644 index 0000000..c4b4e02 --- /dev/null +++ b/Src/3rd_party/mysql/include/mysql.h @@ -0,0 +1,839 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _mysql_h +#define _mysql_h + +#ifdef __CYGWIN__ /* CYGWIN implements a UNIX API */ +#undef WIN +#undef _WIN +#undef _WIN32 +#undef _WIN64 +#undef __WIN__ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _global_h /* If not standard header */ +#include +#ifdef __LCC__ +#include /* For windows */ +#endif +typedef char my_bool; +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__WIN__) +#define __WIN__ +#endif +#if !defined(__WIN__) +#define STDCALL +#else +#define STDCALL __stdcall +#endif +typedef char * gptr; + +#ifndef my_socket_defined +#ifdef __WIN__ +#define my_socket SOCKET +#else +typedef int my_socket; +#endif /* __WIN__ */ +#endif /* my_socket_defined */ +#endif /* _global_h */ + +#include "mysql_com.h" +#include "mysql_time.h" +#include "mysql_version.h" +#include "typelib.h" + +#include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ + +extern unsigned int mysql_port; +extern char *mysql_unix_port; + +#define CLIENT_NET_READ_TIMEOUT 365*24*3600 /* Timeout on read */ +#define CLIENT_NET_WRITE_TIMEOUT 365*24*3600 /* Timeout on write */ + +#ifdef __NETWARE__ +#pragma pack(push, 8) /* 8 byte alignment */ +#endif + +#define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG) +#define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG) +#define IS_BLOB(n) ((n) & BLOB_FLAG) +#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL) +#define IS_NUM_FIELD(f) ((f)->flags & NUM_FLAG) +#define INTERNAL_NUM_FIELD(f) (((f)->type <= FIELD_TYPE_INT24 && ((f)->type != FIELD_TYPE_TIMESTAMP || (f)->length == 14 || (f)->length == 8)) || (f)->type == FIELD_TYPE_YEAR) + + +typedef struct st_mysql_field { + char *name; /* Name of column */ + char *org_name; /* Original column name, if an alias */ + char *table; /* Table of column if column was a field */ + char *org_table; /* Org table name, if table was an alias */ + char *db; /* Database for table */ + char *catalog; /* Catalog for table */ + char *def; /* Default value (set by mysql_list_fields) */ + unsigned long length; /* Width of column (create length) */ + unsigned long max_length; /* Max width for selected set */ + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; /* Div flags */ + unsigned int decimals; /* Number of decimals in field */ + unsigned int charsetnr; /* Character set */ + enum enum_field_types type; /* Type of field. See mysql_com.h for types */ +} MYSQL_FIELD; + +typedef char **MYSQL_ROW; /* return data as array of strings */ +typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */ + +#ifndef _global_h +#if defined(NO_CLIENT_LONG_LONG) +typedef unsigned long my_ulonglong; +#elif defined (__WIN__) +typedef unsigned __int64 my_ulonglong; +#else +typedef unsigned long long my_ulonglong; +#endif +#endif + +#define MYSQL_COUNT_ERROR (~(my_ulonglong) 0) + +typedef struct st_mysql_rows { + struct st_mysql_rows *next; /* list of rows */ + MYSQL_ROW data; + unsigned long length; +} MYSQL_ROWS; + +typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */ + +#include "my_alloc.h" + +typedef struct st_mysql_data { + my_ulonglong rows; + unsigned int fields; + MYSQL_ROWS *data; + MEM_ROOT alloc; +#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY) + MYSQL_ROWS **prev_ptr; +#endif +} MYSQL_DATA; + +enum mysql_option +{ + MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE, + MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, + MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE, + MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT, + MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, + MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, + MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, + MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT +}; + +struct st_mysql_options { + unsigned int connect_timeout, read_timeout, write_timeout; + unsigned int port, protocol; + unsigned long client_flag; + char *host,*user,*password,*unix_socket,*db; + struct st_dynamic_array *init_commands; + char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name; + char *ssl_key; /* PEM key file */ + char *ssl_cert; /* PEM cert file */ + char *ssl_ca; /* PEM CA file */ + char *ssl_capath; /* PEM directory of CA-s? */ + char *ssl_cipher; /* cipher to use */ + char *shared_memory_base_name; + unsigned long max_allowed_packet; + my_bool use_ssl; /* if to use SSL or not */ + my_bool compress,named_pipe; + /* + On connect, find out the replication role of the server, and + establish connections to all the peers + */ + my_bool rpl_probe; + /* + Each call to mysql_real_query() will parse it to tell if it is a read + or a write, and direct it to the slave or the master + */ + my_bool rpl_parse; + /* + If set, never read from a master, only from slave, when doing + a read that is replication-aware + */ + my_bool no_master_reads; +#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY) + my_bool separate_thread; +#endif + enum mysql_option methods_to_use; + char *client_ip; + /* Refuse client connecting to server if it uses old (pre-4.1.1) protocol */ + my_bool secure_auth; + /* 0 - never report, 1 - always report (default) */ + my_bool report_data_truncation; + + /* function pointers for local infile support */ + int (*local_infile_init)(void **, const char *, void *); + int (*local_infile_read)(void *, char *, unsigned int); + void (*local_infile_end)(void *); + int (*local_infile_error)(void *, char *, unsigned int); + void *local_infile_userdata; +}; + +enum mysql_status +{ + MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,MYSQL_STATUS_USE_RESULT +}; + +enum mysql_protocol_type +{ + MYSQL_PROTOCOL_DEFAULT, MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET, + MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY +}; +/* + There are three types of queries - the ones that have to go to + the master, the ones that go to a slave, and the adminstrative + type which must happen on the pivot connectioin +*/ +enum mysql_rpl_type +{ + MYSQL_RPL_MASTER, MYSQL_RPL_SLAVE, MYSQL_RPL_ADMIN +}; + +typedef struct character_set +{ + unsigned int number; /* character set number */ + unsigned int state; /* character set state */ + const char *csname; /* collation name */ + const char *name; /* character set name */ + const char *comment; /* comment */ + const char *dir; /* character set directory */ + unsigned int mbminlen; /* min. length for multibyte strings */ + unsigned int mbmaxlen; /* max. length for multibyte strings */ +} MY_CHARSET_INFO; + +struct st_mysql_methods; + +typedef struct st_mysql +{ + NET net; /* Communication parameters */ + gptr connector_fd; /* ConnectorFd for SSL */ + char *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info; + char *db; + struct charset_info_st *charset; + MYSQL_FIELD *fields; + MEM_ROOT field_alloc; + my_ulonglong affected_rows; + my_ulonglong insert_id; /* id if insert on table with NEXTNR */ + my_ulonglong extra_info; /* Not used */ + unsigned long thread_id; /* Id for connection in server */ + unsigned long packet_length; + unsigned int port; + unsigned long client_flag,server_capabilities; + unsigned int protocol_version; + unsigned int field_count; + unsigned int server_status; + unsigned int server_language; + unsigned int warning_count; + struct st_mysql_options options; + enum mysql_status status; + my_bool free_me; /* If free in mysql_close */ + my_bool reconnect; /* set to 1 if automatic reconnect */ + + /* session-wide random string */ + char scramble[SCRAMBLE_LENGTH+1]; + + /* + Set if this is the original connection, not a master or a slave we have + added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + */ + my_bool rpl_pivot; + /* + Pointers to the master, and the next slave connections, points to + itself if lone connection. + */ + struct st_mysql* master, *next_slave; + + struct st_mysql* last_used_slave; /* needed for round-robin slave pick */ + /* needed for send/read/store/use result to work correctly with replication */ + struct st_mysql* last_used_con; + + LIST *stmts; /* list of all statements */ + const struct st_mysql_methods *methods; + void *thd; + /* + Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + from mysql_stmt_close if close had to cancel result set of this object. + */ + my_bool *unbuffered_fetch_owner; +} MYSQL; + +typedef struct st_mysql_res { + my_ulonglong row_count; + MYSQL_FIELD *fields; + MYSQL_DATA *data; + MYSQL_ROWS *data_cursor; + unsigned long *lengths; /* column lengths of current row */ + MYSQL *handle; /* for unbuffered reads */ + MEM_ROOT field_alloc; + unsigned int field_count, current_field; + MYSQL_ROW row; /* If unbuffered read */ + MYSQL_ROW current_row; /* buffer to current row */ + my_bool eof; /* Used by mysql_fetch_row */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; + const struct st_mysql_methods *methods; +} MYSQL_RES; + +#define MAX_MYSQL_MANAGER_ERR 256 +#define MAX_MYSQL_MANAGER_MSG 256 + +#define MANAGER_OK 200 +#define MANAGER_INFO 250 +#define MANAGER_ACCESS 401 +#define MANAGER_CLIENT_ERR 450 +#define MANAGER_INTERNAL_ERR 500 + +#if !defined(MYSQL_SERVER) && !defined(MYSQL_CLIENT) +#define MYSQL_CLIENT +#endif + + +typedef struct st_mysql_manager +{ + NET net; + char *host,*user,*passwd; + unsigned int port; + my_bool free_me; + my_bool eof; + int cmd_status; + int last_errno; + char* net_buf,*net_buf_pos,*net_data_end; + int net_buf_size; + char last_error[MAX_MYSQL_MANAGER_ERR]; +} MYSQL_MANAGER; + +typedef struct st_mysql_parameters +{ + unsigned long *p_max_allowed_packet; + unsigned long *p_net_buffer_length; +} MYSQL_PARAMETERS; + +#if !defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY) +#define max_allowed_packet (*mysql_get_parameters()->p_max_allowed_packet) +#define net_buffer_length (*mysql_get_parameters()->p_net_buffer_length) +#endif + +/* + Set up and bring down the server; to ensure that applications will + work when linked against either the standard client library or the + embedded server library, these functions should be called. +*/ +int STDCALL mysql_server_init(int argc, char **argv, char **groups); +void STDCALL mysql_server_end(void); +/* + mysql_server_init/end need to be called when using libmysqld or + libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so + you don't need to call it explicitely; but you need to call + mysql_server_end() to free memory). The names are a bit misleading + (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general + names which suit well whether you're using libmysqld or libmysqlclient. We + intend to promote these aliases over the mysql_server* ones. +*/ +#define mysql_library_init mysql_server_init +#define mysql_library_end mysql_server_end + +MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void); + +/* + Set up and bring down a thread; these function should be called + for each thread in an application which opens at least one MySQL + connection. All uses of the connection(s) should be between these + function calls. +*/ +my_bool STDCALL mysql_thread_init(void); +void STDCALL mysql_thread_end(void); + +/* + Functions to get information from the MYSQL and MYSQL_RES structures + Should definitely be used if one uses shared libraries. +*/ + +my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res); +unsigned int STDCALL mysql_num_fields(MYSQL_RES *res); +my_bool STDCALL mysql_eof(MYSQL_RES *res); +MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res, + unsigned int fieldnr); +MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res); +MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res); +MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res); + +unsigned int STDCALL mysql_field_count(MYSQL *mysql); +my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql); +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql); +unsigned int STDCALL mysql_errno(MYSQL *mysql); +const char * STDCALL mysql_error(MYSQL *mysql); +const char *STDCALL mysql_sqlstate(MYSQL *mysql); +unsigned int STDCALL mysql_warning_count(MYSQL *mysql); +const char * STDCALL mysql_info(MYSQL *mysql); +unsigned long STDCALL mysql_thread_id(MYSQL *mysql); +const char * STDCALL mysql_character_set_name(MYSQL *mysql); +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname); + +MYSQL * STDCALL mysql_init(MYSQL *mysql); +my_bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, + const char *cert, const char *ca, + const char *capath, const char *cipher); +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db); +MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, + const char *user, + const char *passwd, + const char *db, + unsigned int port, + const char *unix_socket, + unsigned long clientflag); +int STDCALL mysql_select_db(MYSQL *mysql, const char *db); +int STDCALL mysql_query(MYSQL *mysql, const char *q); +int STDCALL mysql_send_query(MYSQL *mysql, const char *q, + unsigned long length); +int STDCALL mysql_real_query(MYSQL *mysql, const char *q, + unsigned long length); +MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql); +MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql); + +/* perform query on master */ +my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q, + unsigned long length); +my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned long length); +/* perform query on slave */ +my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q, + unsigned long length); +my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned long length); +void STDCALL mysql_get_character_set_info(MYSQL *mysql, + MY_CHARSET_INFO *charset); + +/* local infile support */ + +#define LOCAL_INFILE_ERROR_LEN 512 + +void +mysql_set_local_infile_handler(MYSQL *mysql, + int (*local_infile_init)(void **, const char *, + void *), + int (*local_infile_read)(void *, char *, + unsigned int), + void (*local_infile_end)(void *), + int (*local_infile_error)(void *, char*, + unsigned int), + void *); + +void +mysql_set_local_infile_default(MYSQL *mysql); + + +/* + enable/disable parsing of all queries to decide if they go on master or + slave +*/ +void STDCALL mysql_enable_rpl_parse(MYSQL* mysql); +void STDCALL mysql_disable_rpl_parse(MYSQL* mysql); +/* get the value of the parse flag */ +int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql); + +/* enable/disable reads from master */ +void STDCALL mysql_enable_reads_from_master(MYSQL* mysql); +void STDCALL mysql_disable_reads_from_master(MYSQL* mysql); +/* get the value of the master read flag */ +my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql); + +enum mysql_rpl_type STDCALL mysql_rpl_query_type(const char* q, int len); + +/* discover the master and its slaves */ +my_bool STDCALL mysql_rpl_probe(MYSQL* mysql); + +/* set the master, close/free the old one, if it is not a pivot */ +int STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); +int STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); + +int STDCALL mysql_shutdown(MYSQL *mysql, + enum mysql_enum_shutdown_level + shutdown_level); +int STDCALL mysql_dump_debug_info(MYSQL *mysql); +int STDCALL mysql_refresh(MYSQL *mysql, + unsigned int refresh_options); +int STDCALL mysql_kill(MYSQL *mysql,unsigned long pid); +int STDCALL mysql_set_server_option(MYSQL *mysql, + enum enum_mysql_set_option + option); +int STDCALL mysql_ping(MYSQL *mysql); +const char * STDCALL mysql_stat(MYSQL *mysql); +const char * STDCALL mysql_get_server_info(MYSQL *mysql); +const char * STDCALL mysql_get_client_info(void); +unsigned long STDCALL mysql_get_client_version(void); +const char * STDCALL mysql_get_host_info(MYSQL *mysql); +unsigned long STDCALL mysql_get_server_version(MYSQL *mysql); +unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql); +MYSQL_RES * STDCALL mysql_list_dbs(MYSQL *mysql,const char *wild); +MYSQL_RES * STDCALL mysql_list_tables(MYSQL *mysql,const char *wild); +MYSQL_RES * STDCALL mysql_list_processes(MYSQL *mysql); +int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, + const char *arg); +void STDCALL mysql_free_result(MYSQL_RES *result); +void STDCALL mysql_data_seek(MYSQL_RES *result, + my_ulonglong offset); +MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result, + MYSQL_ROW_OFFSET offset); +MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result, + MYSQL_FIELD_OFFSET offset); +MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result); +unsigned long * STDCALL mysql_fetch_lengths(MYSQL_RES *result); +MYSQL_FIELD * STDCALL mysql_fetch_field(MYSQL_RES *result); +MYSQL_RES * STDCALL mysql_list_fields(MYSQL *mysql, const char *table, + const char *wild); +unsigned long STDCALL mysql_escape_string(char *to,const char *from, + unsigned long from_length); +unsigned long STDCALL mysql_hex_string(char *to,const char *from, + unsigned long from_length); +unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql, + char *to,const char *from, + unsigned long length); +void STDCALL mysql_debug(const char *debug); +char * STDCALL mysql_odbc_escape_string(MYSQL *mysql, + char *to, + unsigned long to_length, + const char *from, + unsigned long from_length, + void *param, + char * + (*extend_buffer) + (void *, char *to, + unsigned long *length)); +void STDCALL myodbc_remove_escape(MYSQL *mysql,char *name); +unsigned int STDCALL mysql_thread_safe(void); +my_bool STDCALL mysql_embedded(void); +MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con); +MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con, + const char* host, + const char* user, + const char* passwd, + unsigned int port); +void STDCALL mysql_manager_close(MYSQL_MANAGER* con); +int STDCALL mysql_manager_command(MYSQL_MANAGER* con, + const char* cmd, int cmd_len); +int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con, + char* res_buf, + int res_buf_size); +my_bool STDCALL mysql_read_query_result(MYSQL *mysql); + + +/* + The following definitions are added for the enhanced + client-server protocol +*/ + +/* statement state */ +enum enum_mysql_stmt_state +{ + MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE, + MYSQL_STMT_FETCH_DONE +}; + + +/* + This structure is used to define bind information, and + internally by the client library. + Public members with their descriptions are listed below + (conventionally `On input' refers to the binds given to + mysql_stmt_bind_param, `On output' refers to the binds given + to mysql_stmt_bind_result): + + buffer_type - One of the MYSQL_* types, used to describe + the host language type of buffer. + On output: if column type is different from + buffer_type, column value is automatically converted + to buffer_type before it is stored in the buffer. + buffer - On input: points to the buffer with input data. + On output: points to the buffer capable to store + output data. + The type of memory pointed by buffer must correspond + to buffer_type. See the correspondence table in + the comment to mysql_stmt_bind_param. + + The two above members are mandatory for any kind of bind. + + buffer_length - the length of the buffer. You don't have to set + it for any fixed length buffer: float, double, + int, etc. It must be set however for variable-length + types, such as BLOBs or STRINGs. + + length - On input: in case when lengths of input values + are different for each execute, you can set this to + point at a variable containining value length. This + way the value length can be different in each execute. + If length is not NULL, buffer_length is not used. + Note, length can even point at buffer_length if + you keep bind structures around while fetching: + this way you can change buffer_length before + each execution, everything will work ok. + On output: if length is set, mysql_stmt_fetch will + write column length into it. + + is_null - On input: points to a boolean variable that should + be set to TRUE for NULL values. + This member is useful only if your data may be + NULL in some but not all cases. + If your data is never NULL, is_null should be set to 0. + If your data is always NULL, set buffer_type + to MYSQL_TYPE_NULL, and is_null will not be used. + + is_unsigned - On input: used to signify that values provided for one + of numeric types are unsigned. + On output describes signedness of the output buffer. + If, taking into account is_unsigned flag, column data + is out of range of the output buffer, data for this column + is regarded truncated. Note that this has no correspondence + to the sign of result set column, if you need to find it out + use mysql_stmt_result_metadata. + error - where to write a truncation error if it is present. + possible error value is: + 0 no truncation + 1 value is out of range or buffer is too small + + Please note that MYSQL_BIND also has internals members. +*/ + +typedef struct st_mysql_bind +{ + unsigned long *length; /* output length pointer */ + my_bool *is_null; /* Pointer to null indicator */ + void *buffer; /* buffer to get/put data */ + /* set this if you want to track data truncations happened during fetch */ + my_bool *error; + enum enum_field_types buffer_type; /* buffer type */ + /* output buffer length, must be set when fetching str/binary */ + unsigned long buffer_length; + unsigned char *row_ptr; /* for the current data position */ + unsigned long offset; /* offset position for char/binary fetch */ + unsigned long length_value; /* Used if length is 0 */ + unsigned int param_number; /* For null count and error messages */ + unsigned int pack_length; /* Internal length for packed data */ + my_bool error_value; /* used if error is 0 */ + my_bool is_unsigned; /* set if integer type is unsigned */ + my_bool long_data_used; /* If used with mysql_send_long_data */ + my_bool is_null_value; /* Used if is_null is 0 */ + void (*store_param_func)(NET *net, struct st_mysql_bind *param); + void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *, + unsigned char **row); + void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *, + unsigned char **row); +} MYSQL_BIND; + + +/* statement handler */ +typedef struct st_mysql_stmt +{ + MEM_ROOT mem_root; /* root allocations */ + LIST list; /* list to keep track of all stmts */ + MYSQL *mysql; /* connection handle */ + MYSQL_BIND *params; /* input parameters */ + MYSQL_BIND *bind; /* output parameters */ + MYSQL_FIELD *fields; /* result set metadata */ + MYSQL_DATA result; /* cached result set */ + MYSQL_ROWS *data_cursor; /* current row in cached result */ + /* copy of mysql->affected_rows after statement execution */ + my_ulonglong affected_rows; + my_ulonglong insert_id; /* copy of mysql->insert_id */ + /* + mysql_stmt_fetch() calls this function to fetch one row (it's different + for buffered, unbuffered and cursor fetch). + */ + int (*read_row_func)(struct st_mysql_stmt *stmt, + unsigned char **row); + unsigned long stmt_id; /* Id for prepared statement */ + unsigned long flags; /* i.e. type of cursor to open */ + unsigned long prefetch_rows; /* number of rows per one COM_FETCH */ + /* + Copied from mysql->server_status after execute/fetch to know + server-side cursor status for this statement. + */ + unsigned int server_status; + unsigned int last_errno; /* error code */ + unsigned int param_count; /* input parameter count */ + unsigned int field_count; /* number of columns in result set */ + enum enum_mysql_stmt_state state; /* statement state */ + char last_error[MYSQL_ERRMSG_SIZE]; /* error message */ + char sqlstate[SQLSTATE_LENGTH+1]; + /* Types of input parameters should be sent to server */ + my_bool send_types_to_server; + my_bool bind_param_done; /* input buffers were supplied */ + unsigned char bind_result_done; /* output buffers were supplied */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; + /* + Is set to true if we need to calculate field->max_length for + metadata fields when doing mysql_stmt_store_result. + */ + my_bool update_max_length; +} MYSQL_STMT; + +enum enum_stmt_attr_type +{ + /* + When doing mysql_stmt_store_result calculate max_length attribute + of statement metadata. This is to be consistent with the old API, + where this was done automatically. + In the new API we do that only by request because it slows down + mysql_stmt_store_result sufficiently. + */ + STMT_ATTR_UPDATE_MAX_LENGTH, + /* + unsigned long with combination of cursor flags (read only, for update, + etc) + */ + STMT_ATTR_CURSOR_TYPE, + /* + Amount of rows to retrieve from server per one fetch if using cursors. + Accepts unsigned long attribute in the range 1 - ulong_max + */ + STMT_ATTR_PREFETCH_ROWS +}; + + +typedef struct st_mysql_methods +{ + my_bool (*read_query_result)(MYSQL *mysql); + my_bool (*advanced_command)(MYSQL *mysql, + enum enum_server_command command, + const char *header, + unsigned long header_length, + const char *arg, + unsigned long arg_length, + my_bool skip_check); + MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + unsigned int fields); + MYSQL_RES * (*use_result)(MYSQL *mysql); + void (*fetch_lengths)(unsigned long *to, + MYSQL_ROW column, unsigned int field_count); + void (*flush_use_result)(MYSQL *mysql); +#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) + MYSQL_FIELD * (*list_fields)(MYSQL *mysql); + my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); + int (*stmt_execute)(MYSQL_STMT *stmt); + int (*read_binary_rows)(MYSQL_STMT *stmt); + int (*unbuffered_fetch)(MYSQL *mysql, char **row); + void (*free_embedded_thd)(MYSQL *mysql); + const char *(*read_statistics)(MYSQL *mysql); + my_bool (*next_result)(MYSQL *mysql); + int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd); +#endif +} MYSQL_METHODS; + + +MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql); +int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, + unsigned long length); +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, + unsigned int column, + unsigned long offset); +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt); +unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, + enum enum_stmt_attr_type attr_type, + const void *attr); +my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, + enum enum_stmt_attr_type attr_type, + void *attr); +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +my_bool STDCALL mysql_stmt_close(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt); +my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, + unsigned int param_number, + const char *data, + unsigned long length); +MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt); +MYSQL_RES *STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt); +unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT * stmt); +const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt); +const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT * stmt); +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt, + MYSQL_ROW_OFFSET offset); +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt); +void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset); +my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt); +my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt); +my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt); +unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt); + +my_bool STDCALL mysql_commit(MYSQL * mysql); +my_bool STDCALL mysql_rollback(MYSQL * mysql); +my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode); +my_bool STDCALL mysql_more_results(MYSQL *mysql); +int STDCALL mysql_next_result(MYSQL *mysql); +void STDCALL mysql_close(MYSQL *sock); + + +/* status return codes */ +#define MYSQL_NO_DATA 100 +#define MYSQL_DATA_TRUNCATED 101 + +#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT) + +#ifdef USE_OLD_FUNCTIONS +MYSQL * STDCALL mysql_connect(MYSQL *mysql, const char *host, + const char *user, const char *passwd); +int STDCALL mysql_create_db(MYSQL *mysql, const char *DB); +int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); +#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT) +#endif +#define HAVE_MYSQL_REAL_CONNECT + +/* + The following functions are mainly exported because of mysqlbinlog; + They are not for general usage +*/ + +#define simple_command(mysql, command, arg, length, skip_check) \ + (*(mysql)->methods->advanced_command)(mysql, command, \ + NullS, 0, arg, length, skip_check) +unsigned long net_safe_read(MYSQL* mysql); + +#ifdef __NETWARE__ +#pragma pack(pop) /* restore alignment */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _mysql_h */ diff --git a/Src/3rd_party/mysql/include/mysql_com.h b/Src/3rd_party/mysql/include/mysql_com.h new file mode 100644 index 0000000..1e595cd --- /dev/null +++ b/Src/3rd_party/mysql/include/mysql_com.h @@ -0,0 +1,444 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* +** Common definition between mysql server & client +*/ + +#ifndef _mysql_com_h +#define _mysql_com_h + +#define NAME_LEN 64 /* Field/table name length */ +#define HOSTNAME_LENGTH 60 +#define USERNAME_LENGTH 16 +#define SERVER_VERSION_LENGTH 60 +#define SQLSTATE_LENGTH 5 + +#define LOCAL_HOST "localhost" +#define LOCAL_HOST_NAMEDPIPE "." + + +#if defined(__WIN__) && !defined( _CUSTOMCONFIG_) +#define MYSQL_NAMEDPIPE "MySQL" +#define MYSQL_SERVICENAME "MySQL" +#endif /* __WIN__ */ + +/* + You should add new commands to the end of this list, otherwise old + servers won't be able to handle them as 'unsupported'. +*/ + +enum enum_server_command +{ + COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, + COM_CREATE_DB, COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, + COM_PROCESS_INFO, COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, + COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, + COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE, + COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, + COM_STMT_RESET, COM_SET_OPTION, COM_STMT_FETCH, + /* don't forget to update const char *command_name[] in sql_parse.cc */ + + /* Must be last */ + COM_END +}; + + +/* + Length of random string sent by server on handshake; this is also length of + obfuscated password, recieved from client +*/ +#define SCRAMBLE_LENGTH 20 +#define SCRAMBLE_LENGTH_323 8 +/* length of password stored in the db: new passwords are preceeded with '*' */ +#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1) +#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2) + + +#define NOT_NULL_FLAG 1 /* Field can't be NULL */ +#define PRI_KEY_FLAG 2 /* Field is part of a primary key */ +#define UNIQUE_KEY_FLAG 4 /* Field is part of a unique key */ +#define MULTIPLE_KEY_FLAG 8 /* Field is part of a key */ +#define BLOB_FLAG 16 /* Field is a blob */ +#define UNSIGNED_FLAG 32 /* Field is unsigned */ +#define ZEROFILL_FLAG 64 /* Field is zerofill */ +#define BINARY_FLAG 128 /* Field is binary */ + +/* The following are only sent to new clients */ +#define ENUM_FLAG 256 /* field is an enum */ +#define AUTO_INCREMENT_FLAG 512 /* field is a autoincrement field */ +#define TIMESTAMP_FLAG 1024 /* Field is a timestamp */ +#define SET_FLAG 2048 /* field is a set */ +#define NO_DEFAULT_VALUE_FLAG 4096 /* Field doesn't have default value */ +#define NUM_FLAG 32768 /* Field is num (for clients) */ +#define PART_KEY_FLAG 16384 /* Intern; Part of some key */ +#define GROUP_FLAG 32768 /* Intern: Group field */ +#define UNIQUE_FLAG 65536 /* Intern: Used by sql_yacc */ +#define BINCMP_FLAG 131072 /* Intern: Used by sql_yacc */ + +#define REFRESH_GRANT 1 /* Refresh grant tables */ +#define REFRESH_LOG 2 /* Start on new log file */ +#define REFRESH_TABLES 4 /* close all tables */ +#define REFRESH_HOSTS 8 /* Flush host cache */ +#define REFRESH_STATUS 16 /* Flush status variables */ +#define REFRESH_THREADS 32 /* Flush thread cache */ +#define REFRESH_SLAVE 64 /* Reset master info and restart slave + thread */ +#define REFRESH_MASTER 128 /* Remove all bin logs in the index + and truncate the index */ + +/* The following can't be set with mysql_refresh() */ +#define REFRESH_READ_LOCK 16384 /* Lock tables for read */ +#define REFRESH_FAST 32768 /* Intern flag */ + +/* RESET (remove all queries) from query cache */ +#define REFRESH_QUERY_CACHE 65536 +#define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */ +#define REFRESH_DES_KEY_FILE 0x40000L +#define REFRESH_USER_RESOURCES 0x80000L + +#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ +#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ +#define CLIENT_LONG_FLAG 4 /* Get all column flags */ +#define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */ +#define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */ +#define CLIENT_COMPRESS 32 /* Can use compression protocol */ +#define CLIENT_ODBC 64 /* Odbc client */ +#define CLIENT_LOCAL_FILES 128 /* Can use LOAD DATA LOCAL */ +#define CLIENT_IGNORE_SPACE 256 /* Ignore spaces before '(' */ +#define CLIENT_PROTOCOL_41 512 /* New 4.1 protocol */ +#define CLIENT_INTERACTIVE 1024 /* This is an interactive client */ +#define CLIENT_SSL 2048 /* Switch to SSL after handshake */ +#define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */ +#define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */ +#define CLIENT_RESERVED 16384 /* Old flag for 4.1 protocol */ +#define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ +#define CLIENT_MULTI_STATEMENTS 65536 /* Enable/disable multi-stmt support */ +#define CLIENT_MULTI_RESULTS 131072 /* Enable/disable multi-results */ +#define CLIENT_REMEMBER_OPTIONS (((ulong) 1) << 31) + +#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ +#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ +#define SERVER_STATUS_MORE_RESULTS 4 /* More results on server */ +#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ +#define SERVER_QUERY_NO_GOOD_INDEX_USED 16 +#define SERVER_QUERY_NO_INDEX_USED 32 +/* + The server was able to fulfill the clients request and opened a + read-only non-scrollable cursor for a query. This flag comes + in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. +*/ +#define SERVER_STATUS_CURSOR_EXISTS 64 +/* + This flag is sent when a read-only cursor is exhausted, in reply to + COM_STMT_FETCH command. +*/ +#define SERVER_STATUS_LAST_ROW_SENT 128 +#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ +#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512 + +#define MYSQL_ERRMSG_SIZE 512 +#define NET_READ_TIMEOUT 30 /* Timeout on read */ +#define NET_WRITE_TIMEOUT 60 /* Timeout on write */ +#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */ + +#define ONLY_KILL_QUERY 1 + +struct st_vio; /* Only C */ +typedef struct st_vio Vio; + +#define MAX_TINYINT_WIDTH 3 /* Max width for a TINY w.o. sign */ +#define MAX_SMALLINT_WIDTH 5 /* Max width for a SHORT w.o. sign */ +#define MAX_MEDIUMINT_WIDTH 8 /* Max width for a INT24 w.o. sign */ +#define MAX_INT_WIDTH 10 /* Max width for a LONG w.o. sign */ +#define MAX_BIGINT_WIDTH 20 /* Max width for a LONGLONG */ +#define MAX_CHAR_WIDTH 255 /* Max length for a CHAR colum */ +#define MAX_BLOB_WIDTH 8192 /* Default width for blob */ + +typedef struct st_net { +#if !defined(CHECK_EMBEDDED_DIFFERENCES) || !defined(EMBEDDED_LIBRARY) + Vio* vio; + unsigned char *buff,*buff_end,*write_pos,*read_pos; + my_socket fd; /* For Perl DBI/dbd */ + unsigned long max_packet,max_packet_size; + unsigned int pkt_nr,compress_pkt_nr; + unsigned int write_timeout, read_timeout, retry_count; + int fcntl; + my_bool compress; + /* + The following variable is set if we are doing several queries in one + command ( as in LOAD TABLE ... FROM MASTER ), + and do not want to confuse the client with OK at the wrong time + */ + unsigned long remain_in_buf,length, buf_length, where_b; + unsigned int *return_status; + unsigned char reading_or_writing; + char save_char; + my_bool no_send_ok; /* For SPs and other things that do multiple stmts */ + my_bool no_send_eof; /* For SPs' first version read-only cursors */ + /* + Set if OK packet is already sent, and we do not need to send error + messages + */ + my_bool no_send_error; + /* + Pointer to query object in query cache, do not equal NULL (0) for + queries in cache that have not stored its results yet + */ +#endif + char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1]; + unsigned int last_errno; + unsigned char error; + gptr query_cache_query; + my_bool report_error; /* We should report error (we have unreported error) */ + my_bool return_errno; +} NET; + +#define packet_error (~(unsigned long) 0) + +enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, + MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, + MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, + MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP, + MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24, + MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, + MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR, + MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + MYSQL_TYPE_BIT, + MYSQL_TYPE_NEWDECIMAL=246, + MYSQL_TYPE_ENUM=247, + MYSQL_TYPE_SET=248, + MYSQL_TYPE_TINY_BLOB=249, + MYSQL_TYPE_MEDIUM_BLOB=250, + MYSQL_TYPE_LONG_BLOB=251, + MYSQL_TYPE_BLOB=252, + MYSQL_TYPE_VAR_STRING=253, + MYSQL_TYPE_STRING=254, + MYSQL_TYPE_GEOMETRY=255 + +}; + +/* For backward compatibility */ +#define CLIENT_MULTI_QUERIES CLIENT_MULTI_STATEMENTS +#define FIELD_TYPE_DECIMAL MYSQL_TYPE_DECIMAL +#define FIELD_TYPE_NEWDECIMAL MYSQL_TYPE_NEWDECIMAL +#define FIELD_TYPE_TINY MYSQL_TYPE_TINY +#define FIELD_TYPE_SHORT MYSQL_TYPE_SHORT +#define FIELD_TYPE_LONG MYSQL_TYPE_LONG +#define FIELD_TYPE_FLOAT MYSQL_TYPE_FLOAT +#define FIELD_TYPE_DOUBLE MYSQL_TYPE_DOUBLE +#define FIELD_TYPE_NULL MYSQL_TYPE_NULL +#define FIELD_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP +#define FIELD_TYPE_LONGLONG MYSQL_TYPE_LONGLONG +#define FIELD_TYPE_INT24 MYSQL_TYPE_INT24 +#define FIELD_TYPE_DATE MYSQL_TYPE_DATE +#define FIELD_TYPE_TIME MYSQL_TYPE_TIME +#define FIELD_TYPE_DATETIME MYSQL_TYPE_DATETIME +#define FIELD_TYPE_YEAR MYSQL_TYPE_YEAR +#define FIELD_TYPE_NEWDATE MYSQL_TYPE_NEWDATE +#define FIELD_TYPE_ENUM MYSQL_TYPE_ENUM +#define FIELD_TYPE_SET MYSQL_TYPE_SET +#define FIELD_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB +#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB +#define FIELD_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB +#define FIELD_TYPE_BLOB MYSQL_TYPE_BLOB +#define FIELD_TYPE_VAR_STRING MYSQL_TYPE_VAR_STRING +#define FIELD_TYPE_STRING MYSQL_TYPE_STRING +#define FIELD_TYPE_CHAR MYSQL_TYPE_TINY +#define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM +#define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY +#define FIELD_TYPE_BIT MYSQL_TYPE_BIT + + +/* Shutdown/kill enums and constants */ + +/* Bits for THD::killable. */ +#define MYSQL_SHUTDOWN_KILLABLE_CONNECT (unsigned char)(1 << 0) +#define MYSQL_SHUTDOWN_KILLABLE_TRANS (unsigned char)(1 << 1) +#define MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE (unsigned char)(1 << 2) +#define MYSQL_SHUTDOWN_KILLABLE_UPDATE (unsigned char)(1 << 3) + +enum mysql_enum_shutdown_level { + /* + We want levels to be in growing order of hardness (because we use number + comparisons). Note that DEFAULT does not respect the growing property, but + it's ok. + */ + SHUTDOWN_DEFAULT = 0, + /* wait for existing connections to finish */ + SHUTDOWN_WAIT_CONNECTIONS= MYSQL_SHUTDOWN_KILLABLE_CONNECT, + /* wait for existing trans to finish */ + SHUTDOWN_WAIT_TRANSACTIONS= MYSQL_SHUTDOWN_KILLABLE_TRANS, + /* wait for existing updates to finish (=> no partial MyISAM update) */ + SHUTDOWN_WAIT_UPDATES= MYSQL_SHUTDOWN_KILLABLE_UPDATE, + /* flush InnoDB buffers and other storage engines' buffers*/ + SHUTDOWN_WAIT_ALL_BUFFERS= (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1), + /* don't flush InnoDB buffers, flush other storage engines' buffers*/ + SHUTDOWN_WAIT_CRITICAL_BUFFERS= (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1, + /* Now the 2 levels of the KILL command */ +#if MYSQL_VERSION_ID >= 50000 + KILL_QUERY= 254, +#endif + KILL_CONNECTION= 255 +}; + + +enum enum_cursor_type +{ + CURSOR_TYPE_NO_CURSOR= 0, + CURSOR_TYPE_READ_ONLY= 1, + CURSOR_TYPE_FOR_UPDATE= 2, + CURSOR_TYPE_SCROLLABLE= 4 +}; + + +/* options for mysql_set_option */ +enum enum_mysql_set_option +{ + MYSQL_OPTION_MULTI_STATEMENTS_ON, + MYSQL_OPTION_MULTI_STATEMENTS_OFF +}; + +#define net_new_transaction(net) ((net)->pkt_nr=0) + +#ifdef __cplusplus +extern "C" { +#endif + +my_bool my_net_init(NET *net, Vio* vio); +void my_net_local_init(NET *net); +void net_end(NET *net); +void net_clear(NET *net); +my_bool net_realloc(NET *net, unsigned long length); +my_bool net_flush(NET *net); +my_bool my_net_write(NET *net,const char *packet,unsigned long len); +my_bool net_write_command(NET *net,unsigned char command, + const char *header, unsigned long head_len, + const char *packet, unsigned long len); +int net_real_write(NET *net,const char *packet,unsigned long len); +unsigned long my_net_read(NET *net); + +/* + The following function is not meant for normal usage + Currently it's used internally by manager.c +*/ +struct sockaddr; +int my_connect(my_socket s, const struct sockaddr *name, unsigned int namelen, + unsigned int timeout); + +struct rand_struct { + unsigned long seed1,seed2,max_value; + double max_value_dbl; +}; + +#ifdef __cplusplus +} +#endif + + /* The following is for user defined functions */ + +enum Item_result {STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT, + DECIMAL_RESULT}; + +typedef struct st_udf_args +{ + unsigned int arg_count; /* Number of arguments */ + enum Item_result *arg_type; /* Pointer to item_results */ + char **args; /* Pointer to argument */ + unsigned long *lengths; /* Length of string arguments */ + char *maybe_null; /* Set to 1 for all maybe_null args */ + char **attributes; /* Pointer to attribute name */ + unsigned long *attribute_lengths; /* Length of attribute arguments */ +} UDF_ARGS; + + /* This holds information about the result */ + +typedef struct st_udf_init +{ + my_bool maybe_null; /* 1 if function can return NULL */ + unsigned int decimals; /* for real functions */ + unsigned long max_length; /* For string functions */ + char *ptr; /* free pointer for function data */ + my_bool const_item; /* 0 if result is independent of arguments */ +} UDF_INIT; + + /* Constants when using compression */ +#define NET_HEADER_SIZE 4 /* standard header size */ +#define COMP_HEADER_SIZE 3 /* compression header extra size */ + + /* Prototypes to password functions */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + These functions are used for authentication by client and server and + implemented in sql/password.c +*/ + +void randominit(struct rand_struct *, unsigned long seed1, + unsigned long seed2); +double my_rnd(struct rand_struct *); +void create_random_string(char *to, unsigned int length, struct rand_struct *rand_st); + +void hash_password(unsigned long *to, const char *password, unsigned int password_len); +void make_scrambled_password_323(char *to, const char *password); +void scramble_323(char *to, const char *message, const char *password); +my_bool check_scramble_323(const char *, const char *message, + unsigned long *salt); +void get_salt_from_password_323(unsigned long *res, const char *password); +void make_password_from_salt_323(char *to, const unsigned long *salt); + +void make_scrambled_password(char *to, const char *password); +void scramble(char *to, const char *message, const char *password); +my_bool check_scramble(const char *reply, const char *message, + const unsigned char *hash_stage2); +void get_salt_from_password(unsigned char *res, const char *password); +void make_password_from_salt(char *to, const unsigned char *hash_stage2); +char *octet2hex(char *to, const char *str, unsigned int len); + +/* end of password.c */ + +char *get_tty_password(char *opt_message); +const char *mysql_errno_to_sqlstate(unsigned int mysql_errno); + +/* Some other useful functions */ + +my_bool my_init(void); +extern int modify_defaults_file(const char *file_location, const char *option, + const char *option_value, + const char *section_name, int remove_option); +int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv); +my_bool my_thread_init(void); +void my_thread_end(void); + +#ifdef _global_h +ulong STDCALL net_field_length(uchar **packet); +my_ulonglong net_field_length_ll(uchar **packet); +char *net_store_length(char *pkg, ulonglong length); +#endif + +#ifdef __cplusplus +} +#endif + +#define NULL_LENGTH ((unsigned long) ~0) /* For net_store_length */ +#define MYSQL_STMT_HEADER 4 +#define MYSQL_LONG_DATA_HEADER 6 + +#endif diff --git a/Src/3rd_party/mysql/include/mysql_embed.h b/Src/3rd_party/mysql/include/mysql_embed.h new file mode 100644 index 0000000..311d95e --- /dev/null +++ b/Src/3rd_party/mysql/include/mysql_embed.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Defines that are unique to the embedded version of MySQL */ + +#ifdef EMBEDDED_LIBRARY + +/* Things we don't need in the embedded version of MySQL */ +/* TODO HF add #undef HAVE_VIO if we don't want client in embedded library */ + +#undef HAVE_PSTACK /* No stacktrace */ +#undef HAVE_DLOPEN /* No udf functions */ +#undef HAVE_OPENSSL +#undef HAVE_SMEM /* No shared memory */ +#undef HAVE_NDBCLUSTER_DB /* No NDB cluster */ + +#define DONT_USE_RAID + +#endif /* EMBEDDED_LIBRARY */ diff --git a/Src/3rd_party/mysql/include/mysql_time.h b/Src/3rd_party/mysql/include/mysql_time.h new file mode 100644 index 0000000..5f4fc12 --- /dev/null +++ b/Src/3rd_party/mysql/include/mysql_time.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _mysql_time_h_ +#define _mysql_time_h_ + +/* + Time declarations shared between the server and client API: + you should not add anything to this header unless it's used + (and hence should be visible) in mysql.h. + If you're looking for a place to add new time-related declaration, + it's most likely my_time.h. See also "C API Handling of Date + and Time Values" chapter in documentation. +*/ + +enum enum_mysql_timestamp_type +{ + MYSQL_TIMESTAMP_NONE= -2, MYSQL_TIMESTAMP_ERROR= -1, + MYSQL_TIMESTAMP_DATE= 0, MYSQL_TIMESTAMP_DATETIME= 1, MYSQL_TIMESTAMP_TIME= 2 +}; + + +/* + Structure which is used to represent datetime values inside MySQL. + + We assume that values in this structure are normalized, i.e. year <= 9999, + month <= 12, day <= 31, hour <= 23, hour <= 59, hour <= 59. Many functions + in server such as my_system_gmt_sec() or make_time() family of functions + rely on this (actually now usage of make_*() family relies on a bit weaker + restriction). Also functions that produce MYSQL_TIME as result ensure this. + There is one exception to this rule though if this structure holds time + value (time_type == MYSQL_TIMESTAMP_TIME) days and hour member can hold + bigger values. +*/ +typedef struct st_mysql_time +{ + unsigned int year, month, day, hour, minute, second; + unsigned long second_part; + my_bool neg; + enum enum_mysql_timestamp_type time_type; +} MYSQL_TIME; + +#endif /* _mysql_time_h_ */ diff --git a/Src/3rd_party/mysql/include/mysql_version.h b/Src/3rd_party/mysql/include/mysql_version.h new file mode 100644 index 0000000..c0a49de --- /dev/null +++ b/Src/3rd_party/mysql/include/mysql_version.h @@ -0,0 +1,29 @@ +/* Copyright Abandoned 1996, 1999, 2001 MySQL AB + This file is public domain and comes with NO WARRANTY of any kind */ + +/* Version numbers for protocol & mysqld */ + +#ifndef _mysql_version_h +#define _mysql_version_h +#ifdef _CUSTOMCONFIG_ +#include +#else +#define PROTOCOL_VERSION 10 +#define MYSQL_SERVER_VERSION "5.0.15" +#define MYSQL_BASE_VERSION "mysqld-5.0" +#define MYSQL_SERVER_SUFFIX_DEF "" +#define FRM_VER 6 +#define MYSQL_VERSION_ID 50015 +#define MYSQL_PORT 3306 +#define MYSQL_UNIX_ADDR "/tmp/mysql.sock" +#define MYSQL_CONFIG_NAME "my" +#define MYSQL_COMPILATION_COMMENT "Official MySQL binary" + +/* mysqld compile time options */ +#endif /* _CUSTOMCONFIG_ */ + +#ifndef LICENSE +#define LICENSE GPL +#endif /* LICENSE */ + +#endif /* _mysql_version_h */ diff --git a/Src/3rd_party/mysql/include/mysqld_ername.h b/Src/3rd_party/mysql/include/mysqld_ername.h new file mode 100644 index 0000000..9ebeea1 --- /dev/null +++ b/Src/3rd_party/mysql/include/mysqld_ername.h @@ -0,0 +1,456 @@ +/* Autogenerated file, please don't edit */ + +{ "ER_HASHCHK", 1000 }, +{ "ER_NISAMCHK", 1001 }, +{ "ER_NO", 1002 }, +{ "ER_YES", 1003 }, +{ "ER_CANT_CREATE_FILE", 1004 }, +{ "ER_CANT_CREATE_TABLE", 1005 }, +{ "ER_CANT_CREATE_DB", 1006 }, +{ "ER_DB_CREATE_EXISTS", 1007 }, +{ "ER_DB_DROP_EXISTS", 1008 }, +{ "ER_DB_DROP_DELETE", 1009 }, +{ "ER_DB_DROP_RMDIR", 1010 }, +{ "ER_CANT_DELETE_FILE", 1011 }, +{ "ER_CANT_FIND_SYSTEM_REC", 1012 }, +{ "ER_CANT_GET_STAT", 1013 }, +{ "ER_CANT_GET_WD", 1014 }, +{ "ER_CANT_LOCK", 1015 }, +{ "ER_CANT_OPEN_FILE", 1016 }, +{ "ER_FILE_NOT_FOUND", 1017 }, +{ "ER_CANT_READ_DIR", 1018 }, +{ "ER_CANT_SET_WD", 1019 }, +{ "ER_CHECKREAD", 1020 }, +{ "ER_DISK_FULL", 1021 }, +{ "ER_DUP_KEY", 1022 }, +{ "ER_ERROR_ON_CLOSE", 1023 }, +{ "ER_ERROR_ON_READ", 1024 }, +{ "ER_ERROR_ON_RENAME", 1025 }, +{ "ER_ERROR_ON_WRITE", 1026 }, +{ "ER_FILE_USED", 1027 }, +{ "ER_FILSORT_ABORT", 1028 }, +{ "ER_FORM_NOT_FOUND", 1029 }, +{ "ER_GET_ERRNO", 1030 }, +{ "ER_ILLEGAL_HA", 1031 }, +{ "ER_KEY_NOT_FOUND", 1032 }, +{ "ER_NOT_FORM_FILE", 1033 }, +{ "ER_NOT_KEYFILE", 1034 }, +{ "ER_OLD_KEYFILE", 1035 }, +{ "ER_OPEN_AS_READONLY", 1036 }, +{ "ER_OUTOFMEMORY", 1037 }, +{ "ER_OUT_OF_SORTMEMORY", 1038 }, +{ "ER_UNEXPECTED_EOF", 1039 }, +{ "ER_CON_COUNT_ERROR", 1040 }, +{ "ER_OUT_OF_RESOURCES", 1041 }, +{ "ER_BAD_HOST_ERROR", 1042 }, +{ "ER_HANDSHAKE_ERROR", 1043 }, +{ "ER_DBACCESS_DENIED_ERROR", 1044 }, +{ "ER_ACCESS_DENIED_ERROR", 1045 }, +{ "ER_NO_DB_ERROR", 1046 }, +{ "ER_UNKNOWN_COM_ERROR", 1047 }, +{ "ER_BAD_NULL_ERROR", 1048 }, +{ "ER_BAD_DB_ERROR", 1049 }, +{ "ER_TABLE_EXISTS_ERROR", 1050 }, +{ "ER_BAD_TABLE_ERROR", 1051 }, +{ "ER_NON_UNIQ_ERROR", 1052 }, +{ "ER_SERVER_SHUTDOWN", 1053 }, +{ "ER_BAD_FIELD_ERROR", 1054 }, +{ "ER_WRONG_FIELD_WITH_GROUP", 1055 }, +{ "ER_WRONG_GROUP_FIELD", 1056 }, +{ "ER_WRONG_SUM_SELECT", 1057 }, +{ "ER_WRONG_VALUE_COUNT", 1058 }, +{ "ER_TOO_LONG_IDENT", 1059 }, +{ "ER_DUP_FIELDNAME", 1060 }, +{ "ER_DUP_KEYNAME", 1061 }, +{ "ER_DUP_ENTRY", 1062 }, +{ "ER_WRONG_FIELD_SPEC", 1063 }, +{ "ER_PARSE_ERROR", 1064 }, +{ "ER_EMPTY_QUERY", 1065 }, +{ "ER_NONUNIQ_TABLE", 1066 }, +{ "ER_INVALID_DEFAULT", 1067 }, +{ "ER_MULTIPLE_PRI_KEY", 1068 }, +{ "ER_TOO_MANY_KEYS", 1069 }, +{ "ER_TOO_MANY_KEY_PARTS", 1070 }, +{ "ER_TOO_LONG_KEY", 1071 }, +{ "ER_KEY_COLUMN_DOES_NOT_EXITS", 1072 }, +{ "ER_BLOB_USED_AS_KEY", 1073 }, +{ "ER_TOO_BIG_FIELDLENGTH", 1074 }, +{ "ER_WRONG_AUTO_KEY", 1075 }, +{ "ER_READY", 1076 }, +{ "ER_NORMAL_SHUTDOWN", 1077 }, +{ "ER_GOT_SIGNAL", 1078 }, +{ "ER_SHUTDOWN_COMPLETE", 1079 }, +{ "ER_FORCING_CLOSE", 1080 }, +{ "ER_IPSOCK_ERROR", 1081 }, +{ "ER_NO_SUCH_INDEX", 1082 }, +{ "ER_WRONG_FIELD_TERMINATORS", 1083 }, +{ "ER_BLOBS_AND_NO_TERMINATED", 1084 }, +{ "ER_TEXTFILE_NOT_READABLE", 1085 }, +{ "ER_FILE_EXISTS_ERROR", 1086 }, +{ "ER_LOAD_INFO", 1087 }, +{ "ER_ALTER_INFO", 1088 }, +{ "ER_WRONG_SUB_KEY", 1089 }, +{ "ER_CANT_REMOVE_ALL_FIELDS", 1090 }, +{ "ER_CANT_DROP_FIELD_OR_KEY", 1091 }, +{ "ER_INSERT_INFO", 1092 }, +{ "ER_UPDATE_TABLE_USED", 1093 }, +{ "ER_NO_SUCH_THREAD", 1094 }, +{ "ER_KILL_DENIED_ERROR", 1095 }, +{ "ER_NO_TABLES_USED", 1096 }, +{ "ER_TOO_BIG_SET", 1097 }, +{ "ER_NO_UNIQUE_LOGFILE", 1098 }, +{ "ER_TABLE_NOT_LOCKED_FOR_WRITE", 1099 }, +{ "ER_TABLE_NOT_LOCKED", 1100 }, +{ "ER_BLOB_CANT_HAVE_DEFAULT", 1101 }, +{ "ER_WRONG_DB_NAME", 1102 }, +{ "ER_WRONG_TABLE_NAME", 1103 }, +{ "ER_TOO_BIG_SELECT", 1104 }, +{ "ER_UNKNOWN_ERROR", 1105 }, +{ "ER_UNKNOWN_PROCEDURE", 1106 }, +{ "ER_WRONG_PARAMCOUNT_TO_PROCEDURE", 1107 }, +{ "ER_WRONG_PARAMETERS_TO_PROCEDURE", 1108 }, +{ "ER_UNKNOWN_TABLE", 1109 }, +{ "ER_FIELD_SPECIFIED_TWICE", 1110 }, +{ "ER_INVALID_GROUP_FUNC_USE", 1111 }, +{ "ER_UNSUPPORTED_EXTENSION", 1112 }, +{ "ER_TABLE_MUST_HAVE_COLUMNS", 1113 }, +{ "ER_RECORD_FILE_FULL", 1114 }, +{ "ER_UNKNOWN_CHARACTER_SET", 1115 }, +{ "ER_TOO_MANY_TABLES", 1116 }, +{ "ER_TOO_MANY_FIELDS", 1117 }, +{ "ER_TOO_BIG_ROWSIZE", 1118 }, +{ "ER_STACK_OVERRUN", 1119 }, +{ "ER_WRONG_OUTER_JOIN", 1120 }, +{ "ER_NULL_COLUMN_IN_INDEX", 1121 }, +{ "ER_CANT_FIND_UDF", 1122 }, +{ "ER_CANT_INITIALIZE_UDF", 1123 }, +{ "ER_UDF_NO_PATHS", 1124 }, +{ "ER_UDF_EXISTS", 1125 }, +{ "ER_CANT_OPEN_LIBRARY", 1126 }, +{ "ER_CANT_FIND_DL_ENTRY", 1127 }, +{ "ER_FUNCTION_NOT_DEFINED", 1128 }, +{ "ER_HOST_IS_BLOCKED", 1129 }, +{ "ER_HOST_NOT_PRIVILEGED", 1130 }, +{ "ER_PASSWORD_ANONYMOUS_USER", 1131 }, +{ "ER_PASSWORD_NOT_ALLOWED", 1132 }, +{ "ER_PASSWORD_NO_MATCH", 1133 }, +{ "ER_UPDATE_INFO", 1134 }, +{ "ER_CANT_CREATE_THREAD", 1135 }, +{ "ER_WRONG_VALUE_COUNT_ON_ROW", 1136 }, +{ "ER_CANT_REOPEN_TABLE", 1137 }, +{ "ER_INVALID_USE_OF_NULL", 1138 }, +{ "ER_REGEXP_ERROR", 1139 }, +{ "ER_MIX_OF_GROUP_FUNC_AND_FIELDS", 1140 }, +{ "ER_NONEXISTING_GRANT", 1141 }, +{ "ER_TABLEACCESS_DENIED_ERROR", 1142 }, +{ "ER_COLUMNACCESS_DENIED_ERROR", 1143 }, +{ "ER_ILLEGAL_GRANT_FOR_TABLE", 1144 }, +{ "ER_GRANT_WRONG_HOST_OR_USER", 1145 }, +{ "ER_NO_SUCH_TABLE", 1146 }, +{ "ER_NONEXISTING_TABLE_GRANT", 1147 }, +{ "ER_NOT_ALLOWED_COMMAND", 1148 }, +{ "ER_SYNTAX_ERROR", 1149 }, +{ "ER_DELAYED_CANT_CHANGE_LOCK", 1150 }, +{ "ER_TOO_MANY_DELAYED_THREADS", 1151 }, +{ "ER_ABORTING_CONNECTION", 1152 }, +{ "ER_NET_PACKET_TOO_LARGE", 1153 }, +{ "ER_NET_READ_ERROR_FROM_PIPE", 1154 }, +{ "ER_NET_FCNTL_ERROR", 1155 }, +{ "ER_NET_PACKETS_OUT_OF_ORDER", 1156 }, +{ "ER_NET_UNCOMPRESS_ERROR", 1157 }, +{ "ER_NET_READ_ERROR", 1158 }, +{ "ER_NET_READ_INTERRUPTED", 1159 }, +{ "ER_NET_ERROR_ON_WRITE", 1160 }, +{ "ER_NET_WRITE_INTERRUPTED", 1161 }, +{ "ER_TOO_LONG_STRING", 1162 }, +{ "ER_TABLE_CANT_HANDLE_BLOB", 1163 }, +{ "ER_TABLE_CANT_HANDLE_AUTO_INCREMENT", 1164 }, +{ "ER_DELAYED_INSERT_TABLE_LOCKED", 1165 }, +{ "ER_WRONG_COLUMN_NAME", 1166 }, +{ "ER_WRONG_KEY_COLUMN", 1167 }, +{ "ER_WRONG_MRG_TABLE", 1168 }, +{ "ER_DUP_UNIQUE", 1169 }, +{ "ER_BLOB_KEY_WITHOUT_LENGTH", 1170 }, +{ "ER_PRIMARY_CANT_HAVE_NULL", 1171 }, +{ "ER_TOO_MANY_ROWS", 1172 }, +{ "ER_REQUIRES_PRIMARY_KEY", 1173 }, +{ "ER_NO_RAID_COMPILED", 1174 }, +{ "ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE", 1175 }, +{ "ER_KEY_DOES_NOT_EXITS", 1176 }, +{ "ER_CHECK_NO_SUCH_TABLE", 1177 }, +{ "ER_CHECK_NOT_IMPLEMENTED", 1178 }, +{ "ER_CANT_DO_THIS_DURING_AN_TRANSACTION", 1179 }, +{ "ER_ERROR_DURING_COMMIT", 1180 }, +{ "ER_ERROR_DURING_ROLLBACK", 1181 }, +{ "ER_ERROR_DURING_FLUSH_LOGS", 1182 }, +{ "ER_ERROR_DURING_CHECKPOINT", 1183 }, +{ "ER_NEW_ABORTING_CONNECTION", 1184 }, +{ "ER_DUMP_NOT_IMPLEMENTED", 1185 }, +{ "ER_FLUSH_MASTER_BINLOG_CLOSED", 1186 }, +{ "ER_INDEX_REBUILD", 1187 }, +{ "ER_MASTER", 1188 }, +{ "ER_MASTER_NET_READ", 1189 }, +{ "ER_MASTER_NET_WRITE", 1190 }, +{ "ER_FT_MATCHING_KEY_NOT_FOUND", 1191 }, +{ "ER_LOCK_OR_ACTIVE_TRANSACTION", 1192 }, +{ "ER_UNKNOWN_SYSTEM_VARIABLE", 1193 }, +{ "ER_CRASHED_ON_USAGE", 1194 }, +{ "ER_CRASHED_ON_REPAIR", 1195 }, +{ "ER_WARNING_NOT_COMPLETE_ROLLBACK", 1196 }, +{ "ER_TRANS_CACHE_FULL", 1197 }, +{ "ER_SLAVE_MUST_STOP", 1198 }, +{ "ER_SLAVE_NOT_RUNNING", 1199 }, +{ "ER_BAD_SLAVE", 1200 }, +{ "ER_MASTER_INFO", 1201 }, +{ "ER_SLAVE_THREAD", 1202 }, +{ "ER_TOO_MANY_USER_CONNECTIONS", 1203 }, +{ "ER_SET_CONSTANTS_ONLY", 1204 }, +{ "ER_LOCK_WAIT_TIMEOUT", 1205 }, +{ "ER_LOCK_TABLE_FULL", 1206 }, +{ "ER_READ_ONLY_TRANSACTION", 1207 }, +{ "ER_DROP_DB_WITH_READ_LOCK", 1208 }, +{ "ER_CREATE_DB_WITH_READ_LOCK", 1209 }, +{ "ER_WRONG_ARGUMENTS", 1210 }, +{ "ER_NO_PERMISSION_TO_CREATE_USER", 1211 }, +{ "ER_UNION_TABLES_IN_DIFFERENT_DIR", 1212 }, +{ "ER_LOCK_DEADLOCK", 1213 }, +{ "ER_TABLE_CANT_HANDLE_FT", 1214 }, +{ "ER_CANNOT_ADD_FOREIGN", 1215 }, +{ "ER_NO_REFERENCED_ROW", 1216 }, +{ "ER_ROW_IS_REFERENCED", 1217 }, +{ "ER_CONNECT_TO_MASTER", 1218 }, +{ "ER_QUERY_ON_MASTER", 1219 }, +{ "ER_ERROR_WHEN_EXECUTING_COMMAND", 1220 }, +{ "ER_WRONG_USAGE", 1221 }, +{ "ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT", 1222 }, +{ "ER_CANT_UPDATE_WITH_READLOCK", 1223 }, +{ "ER_MIXING_NOT_ALLOWED", 1224 }, +{ "ER_DUP_ARGUMENT", 1225 }, +{ "ER_USER_LIMIT_REACHED", 1226 }, +{ "ER_SPECIFIC_ACCESS_DENIED_ERROR", 1227 }, +{ "ER_LOCAL_VARIABLE", 1228 }, +{ "ER_GLOBAL_VARIABLE", 1229 }, +{ "ER_NO_DEFAULT", 1230 }, +{ "ER_WRONG_VALUE_FOR_VAR", 1231 }, +{ "ER_WRONG_TYPE_FOR_VAR", 1232 }, +{ "ER_VAR_CANT_BE_READ", 1233 }, +{ "ER_CANT_USE_OPTION_HERE", 1234 }, +{ "ER_NOT_SUPPORTED_YET", 1235 }, +{ "ER_MASTER_FATAL_ERROR_READING_BINLOG", 1236 }, +{ "ER_SLAVE_IGNORED_TABLE", 1237 }, +{ "ER_INCORRECT_GLOBAL_LOCAL_VAR", 1238 }, +{ "ER_WRONG_FK_DEF", 1239 }, +{ "ER_KEY_REF_DO_NOT_MATCH_TABLE_REF", 1240 }, +{ "ER_OPERAND_COLUMNS", 1241 }, +{ "ER_SUBQUERY_NO_1_ROW", 1242 }, +{ "ER_UNKNOWN_STMT_HANDLER", 1243 }, +{ "ER_CORRUPT_HELP_DB", 1244 }, +{ "ER_CYCLIC_REFERENCE", 1245 }, +{ "ER_AUTO_CONVERT", 1246 }, +{ "ER_ILLEGAL_REFERENCE", 1247 }, +{ "ER_DERIVED_MUST_HAVE_ALIAS", 1248 }, +{ "ER_SELECT_REDUCED", 1249 }, +{ "ER_TABLENAME_NOT_ALLOWED_HERE", 1250 }, +{ "ER_NOT_SUPPORTED_AUTH_MODE", 1251 }, +{ "ER_SPATIAL_CANT_HAVE_NULL", 1252 }, +{ "ER_COLLATION_CHARSET_MISMATCH", 1253 }, +{ "ER_SLAVE_WAS_RUNNING", 1254 }, +{ "ER_SLAVE_WAS_NOT_RUNNING", 1255 }, +{ "ER_TOO_BIG_FOR_UNCOMPRESS", 1256 }, +{ "ER_ZLIB_Z_MEM_ERROR", 1257 }, +{ "ER_ZLIB_Z_BUF_ERROR", 1258 }, +{ "ER_ZLIB_Z_DATA_ERROR", 1259 }, +{ "ER_CUT_VALUE_GROUP_CONCAT", 1260 }, +{ "ER_WARN_TOO_FEW_RECORDS", 1261 }, +{ "ER_WARN_TOO_MANY_RECORDS", 1262 }, +{ "ER_WARN_NULL_TO_NOTNULL", 1263 }, +{ "ER_WARN_DATA_OUT_OF_RANGE", 1264 }, +{ "WARN_DATA_TRUNCATED", 1265 }, +{ "ER_WARN_USING_OTHER_HANDLER", 1266 }, +{ "ER_CANT_AGGREGATE_2COLLATIONS", 1267 }, +{ "ER_DROP_USER", 1268 }, +{ "ER_REVOKE_GRANTS", 1269 }, +{ "ER_CANT_AGGREGATE_3COLLATIONS", 1270 }, +{ "ER_CANT_AGGREGATE_NCOLLATIONS", 1271 }, +{ "ER_VARIABLE_IS_NOT_STRUCT", 1272 }, +{ "ER_UNKNOWN_COLLATION", 1273 }, +{ "ER_SLAVE_IGNORED_SSL_PARAMS", 1274 }, +{ "ER_SERVER_IS_IN_SECURE_AUTH_MODE", 1275 }, +{ "ER_WARN_FIELD_RESOLVED", 1276 }, +{ "ER_BAD_SLAVE_UNTIL_COND", 1277 }, +{ "ER_MISSING_SKIP_SLAVE", 1278 }, +{ "ER_UNTIL_COND_IGNORED", 1279 }, +{ "ER_WRONG_NAME_FOR_INDEX", 1280 }, +{ "ER_WRONG_NAME_FOR_CATALOG", 1281 }, +{ "ER_WARN_QC_RESIZE", 1282 }, +{ "ER_BAD_FT_COLUMN", 1283 }, +{ "ER_UNKNOWN_KEY_CACHE", 1284 }, +{ "ER_WARN_HOSTNAME_WONT_WORK", 1285 }, +{ "ER_UNKNOWN_STORAGE_ENGINE", 1286 }, +{ "ER_WARN_DEPRECATED_SYNTAX", 1287 }, +{ "ER_NON_UPDATABLE_TABLE", 1288 }, +{ "ER_FEATURE_DISABLED", 1289 }, +{ "ER_OPTION_PREVENTS_STATEMENT", 1290 }, +{ "ER_DUPLICATED_VALUE_IN_TYPE", 1291 }, +{ "ER_TRUNCATED_WRONG_VALUE", 1292 }, +{ "ER_TOO_MUCH_AUTO_TIMESTAMP_COLS", 1293 }, +{ "ER_INVALID_ON_UPDATE", 1294 }, +{ "ER_UNSUPPORTED_PS", 1295 }, +{ "ER_GET_ERRMSG", 1296 }, +{ "ER_GET_TEMPORARY_ERRMSG", 1297 }, +{ "ER_UNKNOWN_TIME_ZONE", 1298 }, +{ "ER_WARN_INVALID_TIMESTAMP", 1299 }, +{ "ER_INVALID_CHARACTER_STRING", 1300 }, +{ "ER_WARN_ALLOWED_PACKET_OVERFLOWED", 1301 }, +{ "ER_CONFLICTING_DECLARATIONS", 1302 }, +{ "ER_SP_NO_RECURSIVE_CREATE", 1303 }, +{ "ER_SP_ALREADY_EXISTS", 1304 }, +{ "ER_SP_DOES_NOT_EXIST", 1305 }, +{ "ER_SP_DROP_FAILED", 1306 }, +{ "ER_SP_STORE_FAILED", 1307 }, +{ "ER_SP_LILABEL_MISMATCH", 1308 }, +{ "ER_SP_LABEL_REDEFINE", 1309 }, +{ "ER_SP_LABEL_MISMATCH", 1310 }, +{ "ER_SP_UNINIT_VAR", 1311 }, +{ "ER_SP_BADSELECT", 1312 }, +{ "ER_SP_BADRETURN", 1313 }, +{ "ER_SP_BADSTATEMENT", 1314 }, +{ "ER_UPDATE_LOG_DEPRECATED_IGNORED", 1315 }, +{ "ER_UPDATE_LOG_DEPRECATED_TRANSLATED", 1316 }, +{ "ER_QUERY_INTERRUPTED", 1317 }, +{ "ER_SP_WRONG_NO_OF_ARGS", 1318 }, +{ "ER_SP_COND_MISMATCH", 1319 }, +{ "ER_SP_NORETURN", 1320 }, +{ "ER_SP_NORETURNEND", 1321 }, +{ "ER_SP_BAD_CURSOR_QUERY", 1322 }, +{ "ER_SP_BAD_CURSOR_SELECT", 1323 }, +{ "ER_SP_CURSOR_MISMATCH", 1324 }, +{ "ER_SP_CURSOR_ALREADY_OPEN", 1325 }, +{ "ER_SP_CURSOR_NOT_OPEN", 1326 }, +{ "ER_SP_UNDECLARED_VAR", 1327 }, +{ "ER_SP_WRONG_NO_OF_FETCH_ARGS", 1328 }, +{ "ER_SP_FETCH_NO_DATA", 1329 }, +{ "ER_SP_DUP_PARAM", 1330 }, +{ "ER_SP_DUP_VAR", 1331 }, +{ "ER_SP_DUP_COND", 1332 }, +{ "ER_SP_DUP_CURS", 1333 }, +{ "ER_SP_CANT_ALTER", 1334 }, +{ "ER_SP_SUBSELECT_NYI", 1335 }, +{ "ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG", 1336 }, +{ "ER_SP_VARCOND_AFTER_CURSHNDLR", 1337 }, +{ "ER_SP_CURSOR_AFTER_HANDLER", 1338 }, +{ "ER_SP_CASE_NOT_FOUND", 1339 }, +{ "ER_FPARSER_TOO_BIG_FILE", 1340 }, +{ "ER_FPARSER_BAD_HEADER", 1341 }, +{ "ER_FPARSER_EOF_IN_COMMENT", 1342 }, +{ "ER_FPARSER_ERROR_IN_PARAMETER", 1343 }, +{ "ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER", 1344 }, +{ "ER_VIEW_NO_EXPLAIN", 1345 }, +{ "ER_FRM_UNKNOWN_TYPE", 1346 }, +{ "ER_WRONG_OBJECT", 1347 }, +{ "ER_NONUPDATEABLE_COLUMN", 1348 }, +{ "ER_VIEW_SELECT_DERIVED", 1349 }, +{ "ER_VIEW_SELECT_CLAUSE", 1350 }, +{ "ER_VIEW_SELECT_VARIABLE", 1351 }, +{ "ER_VIEW_SELECT_TMPTABLE", 1352 }, +{ "ER_VIEW_WRONG_LIST", 1353 }, +{ "ER_WARN_VIEW_MERGE", 1354 }, +{ "ER_WARN_VIEW_WITHOUT_KEY", 1355 }, +{ "ER_VIEW_INVALID", 1356 }, +{ "ER_SP_NO_DROP_SP", 1357 }, +{ "ER_SP_GOTO_IN_HNDLR", 1358 }, +{ "ER_TRG_ALREADY_EXISTS", 1359 }, +{ "ER_TRG_DOES_NOT_EXIST", 1360 }, +{ "ER_TRG_ON_VIEW_OR_TEMP_TABLE", 1361 }, +{ "ER_TRG_CANT_CHANGE_ROW", 1362 }, +{ "ER_TRG_NO_SUCH_ROW_IN_TRG", 1363 }, +{ "ER_NO_DEFAULT_FOR_FIELD", 1364 }, +{ "ER_DIVISION_BY_ZERO", 1365 }, +{ "ER_TRUNCATED_WRONG_VALUE_FOR_FIELD", 1366 }, +{ "ER_ILLEGAL_VALUE_FOR_TYPE", 1367 }, +{ "ER_VIEW_NONUPD_CHECK", 1368 }, +{ "ER_VIEW_CHECK_FAILED", 1369 }, +{ "ER_PROCACCESS_DENIED_ERROR", 1370 }, +{ "ER_RELAY_LOG_FAIL", 1371 }, +{ "ER_PASSWD_LENGTH", 1372 }, +{ "ER_UNKNOWN_TARGET_BINLOG", 1373 }, +{ "ER_IO_ERR_LOG_INDEX_READ", 1374 }, +{ "ER_BINLOG_PURGE_PROHIBITED", 1375 }, +{ "ER_FSEEK_FAIL", 1376 }, +{ "ER_BINLOG_PURGE_FATAL_ERR", 1377 }, +{ "ER_LOG_IN_USE", 1378 }, +{ "ER_LOG_PURGE_UNKNOWN_ERR", 1379 }, +{ "ER_RELAY_LOG_INIT", 1380 }, +{ "ER_NO_BINARY_LOGGING", 1381 }, +{ "ER_RESERVED_SYNTAX", 1382 }, +{ "ER_WSAS_FAILED", 1383 }, +{ "ER_DIFF_GROUPS_PROC", 1384 }, +{ "ER_NO_GROUP_FOR_PROC", 1385 }, +{ "ER_ORDER_WITH_PROC", 1386 }, +{ "ER_LOGGING_PROHIBIT_CHANGING_OF", 1387 }, +{ "ER_NO_FILE_MAPPING", 1388 }, +{ "ER_WRONG_MAGIC", 1389 }, +{ "ER_PS_MANY_PARAM", 1390 }, +{ "ER_KEY_PART_0", 1391 }, +{ "ER_VIEW_CHECKSUM", 1392 }, +{ "ER_VIEW_MULTIUPDATE", 1393 }, +{ "ER_VIEW_NO_INSERT_FIELD_LIST", 1394 }, +{ "ER_VIEW_DELETE_MERGE_VIEW", 1395 }, +{ "ER_CANNOT_USER", 1396 }, +{ "ER_XAER_NOTA", 1397 }, +{ "ER_XAER_INVAL", 1398 }, +{ "ER_XAER_RMFAIL", 1399 }, +{ "ER_XAER_OUTSIDE", 1400 }, +{ "ER_XAER_RMERR", 1401 }, +{ "ER_XA_RBROLLBACK", 1402 }, +{ "ER_NONEXISTING_PROC_GRANT", 1403 }, +{ "ER_PROC_AUTO_GRANT_FAIL", 1404 }, +{ "ER_PROC_AUTO_REVOKE_FAIL", 1405 }, +{ "ER_DATA_TOO_LONG", 1406 }, +{ "ER_SP_BAD_SQLSTATE", 1407 }, +{ "ER_STARTUP", 1408 }, +{ "ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR", 1409 }, +{ "ER_CANT_CREATE_USER_WITH_GRANT", 1410 }, +{ "ER_WRONG_VALUE_FOR_TYPE", 1411 }, +{ "ER_TABLE_DEF_CHANGED", 1412 }, +{ "ER_SP_DUP_HANDLER", 1413 }, +{ "ER_SP_NOT_VAR_ARG", 1414 }, +{ "ER_SP_NO_RETSET", 1415 }, +{ "ER_CANT_CREATE_GEOMETRY_OBJECT", 1416 }, +{ "ER_FAILED_ROUTINE_BREAK_BINLOG", 1417 }, +{ "ER_BINLOG_UNSAFE_ROUTINE", 1418 }, +{ "ER_BINLOG_CREATE_ROUTINE_NEED_SUPER", 1419 }, +{ "ER_EXEC_STMT_WITH_OPEN_CURSOR", 1420 }, +{ "ER_STMT_HAS_NO_OPEN_CURSOR", 1421 }, +{ "ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG", 1422 }, +{ "ER_NO_DEFAULT_FOR_VIEW_FIELD", 1423 }, +{ "ER_SP_NO_RECURSION", 1424 }, +{ "ER_TOO_BIG_SCALE", 1425 }, +{ "ER_TOO_BIG_PRECISION", 1426 }, +{ "ER_M_BIGGER_THAN_D", 1427 }, +{ "ER_WRONG_LOCK_OF_SYSTEM_TABLE", 1428 }, +{ "ER_CONNECT_TO_FOREIGN_DATA_SOURCE", 1429 }, +{ "ER_QUERY_ON_FOREIGN_DATA_SOURCE", 1430 }, +{ "ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST", 1431 }, +{ "ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE", 1432 }, +{ "ER_FOREIGN_DATA_STRING_INVALID", 1433 }, +{ "ER_CANT_CREATE_FEDERATED_TABLE", 1434 }, +{ "ER_TRG_IN_WRONG_SCHEMA", 1435 }, +{ "ER_STACK_OVERRUN_NEED_MORE", 1436 }, +{ "ER_TOO_LONG_BODY", 1437 }, +{ "ER_WARN_CANT_DROP_DEFAULT_KEYCACHE", 1438 }, +{ "ER_TOO_BIG_DISPLAYWIDTH", 1439 }, +{ "ER_XAER_DUPID", 1440 }, +{ "ER_DATETIME_FUNCTION_OVERFLOW", 1441 }, +{ "ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG", 1442 }, +{ "ER_VIEW_PREVENT_UPDATE", 1443 }, +{ "ER_PS_NO_RECURSION", 1444 }, +{ "ER_SP_CANT_SET_AUTOCOMMIT", 1445 }, +{ "ER_NO_VIEW_USER", 1446 }, +{ "ER_VIEW_FRM_NO_USER", 1447 }, +{ "ER_VIEW_OTHER_USER", 1448 }, +{ "ER_NO_SUCH_USER", 1449 }, +{ "ER_FORBID_SCHEMA_CHANGE", 1450 }, +{ "ER_ROW_IS_REFERENCED_2", 1451 }, +{ "ER_NO_REFERENCED_ROW_2", 1452 }, +{ "ER_SP_BAD_VAR_SHADOW", 1453 }, diff --git a/Src/3rd_party/mysql/include/mysqld_error.h b/Src/3rd_party/mysql/include/mysqld_error.h new file mode 100644 index 0000000..e8bc392 --- /dev/null +++ b/Src/3rd_party/mysql/include/mysqld_error.h @@ -0,0 +1,458 @@ +/* Autogenerated file, please don't edit */ + +#define ER_ERROR_FIRST 1000 +#define ER_HASHCHK 1000 +#define ER_NISAMCHK 1001 +#define ER_NO 1002 +#define ER_YES 1003 +#define ER_CANT_CREATE_FILE 1004 +#define ER_CANT_CREATE_TABLE 1005 +#define ER_CANT_CREATE_DB 1006 +#define ER_DB_CREATE_EXISTS 1007 +#define ER_DB_DROP_EXISTS 1008 +#define ER_DB_DROP_DELETE 1009 +#define ER_DB_DROP_RMDIR 1010 +#define ER_CANT_DELETE_FILE 1011 +#define ER_CANT_FIND_SYSTEM_REC 1012 +#define ER_CANT_GET_STAT 1013 +#define ER_CANT_GET_WD 1014 +#define ER_CANT_LOCK 1015 +#define ER_CANT_OPEN_FILE 1016 +#define ER_FILE_NOT_FOUND 1017 +#define ER_CANT_READ_DIR 1018 +#define ER_CANT_SET_WD 1019 +#define ER_CHECKREAD 1020 +#define ER_DISK_FULL 1021 +#define ER_DUP_KEY 1022 +#define ER_ERROR_ON_CLOSE 1023 +#define ER_ERROR_ON_READ 1024 +#define ER_ERROR_ON_RENAME 1025 +#define ER_ERROR_ON_WRITE 1026 +#define ER_FILE_USED 1027 +#define ER_FILSORT_ABORT 1028 +#define ER_FORM_NOT_FOUND 1029 +#define ER_GET_ERRNO 1030 +#define ER_ILLEGAL_HA 1031 +#define ER_KEY_NOT_FOUND 1032 +#define ER_NOT_FORM_FILE 1033 +#define ER_NOT_KEYFILE 1034 +#define ER_OLD_KEYFILE 1035 +#define ER_OPEN_AS_READONLY 1036 +#define ER_OUTOFMEMORY 1037 +#define ER_OUT_OF_SORTMEMORY 1038 +#define ER_UNEXPECTED_EOF 1039 +#define ER_CON_COUNT_ERROR 1040 +#define ER_OUT_OF_RESOURCES 1041 +#define ER_BAD_HOST_ERROR 1042 +#define ER_HANDSHAKE_ERROR 1043 +#define ER_DBACCESS_DENIED_ERROR 1044 +#define ER_ACCESS_DENIED_ERROR 1045 +#define ER_NO_DB_ERROR 1046 +#define ER_UNKNOWN_COM_ERROR 1047 +#define ER_BAD_NULL_ERROR 1048 +#define ER_BAD_DB_ERROR 1049 +#define ER_TABLE_EXISTS_ERROR 1050 +#define ER_BAD_TABLE_ERROR 1051 +#define ER_NON_UNIQ_ERROR 1052 +#define ER_SERVER_SHUTDOWN 1053 +#define ER_BAD_FIELD_ERROR 1054 +#define ER_WRONG_FIELD_WITH_GROUP 1055 +#define ER_WRONG_GROUP_FIELD 1056 +#define ER_WRONG_SUM_SELECT 1057 +#define ER_WRONG_VALUE_COUNT 1058 +#define ER_TOO_LONG_IDENT 1059 +#define ER_DUP_FIELDNAME 1060 +#define ER_DUP_KEYNAME 1061 +#define ER_DUP_ENTRY 1062 +#define ER_WRONG_FIELD_SPEC 1063 +#define ER_PARSE_ERROR 1064 +#define ER_EMPTY_QUERY 1065 +#define ER_NONUNIQ_TABLE 1066 +#define ER_INVALID_DEFAULT 1067 +#define ER_MULTIPLE_PRI_KEY 1068 +#define ER_TOO_MANY_KEYS 1069 +#define ER_TOO_MANY_KEY_PARTS 1070 +#define ER_TOO_LONG_KEY 1071 +#define ER_KEY_COLUMN_DOES_NOT_EXITS 1072 +#define ER_BLOB_USED_AS_KEY 1073 +#define ER_TOO_BIG_FIELDLENGTH 1074 +#define ER_WRONG_AUTO_KEY 1075 +#define ER_READY 1076 +#define ER_NORMAL_SHUTDOWN 1077 +#define ER_GOT_SIGNAL 1078 +#define ER_SHUTDOWN_COMPLETE 1079 +#define ER_FORCING_CLOSE 1080 +#define ER_IPSOCK_ERROR 1081 +#define ER_NO_SUCH_INDEX 1082 +#define ER_WRONG_FIELD_TERMINATORS 1083 +#define ER_BLOBS_AND_NO_TERMINATED 1084 +#define ER_TEXTFILE_NOT_READABLE 1085 +#define ER_FILE_EXISTS_ERROR 1086 +#define ER_LOAD_INFO 1087 +#define ER_ALTER_INFO 1088 +#define ER_WRONG_SUB_KEY 1089 +#define ER_CANT_REMOVE_ALL_FIELDS 1090 +#define ER_CANT_DROP_FIELD_OR_KEY 1091 +#define ER_INSERT_INFO 1092 +#define ER_UPDATE_TABLE_USED 1093 +#define ER_NO_SUCH_THREAD 1094 +#define ER_KILL_DENIED_ERROR 1095 +#define ER_NO_TABLES_USED 1096 +#define ER_TOO_BIG_SET 1097 +#define ER_NO_UNIQUE_LOGFILE 1098 +#define ER_TABLE_NOT_LOCKED_FOR_WRITE 1099 +#define ER_TABLE_NOT_LOCKED 1100 +#define ER_BLOB_CANT_HAVE_DEFAULT 1101 +#define ER_WRONG_DB_NAME 1102 +#define ER_WRONG_TABLE_NAME 1103 +#define ER_TOO_BIG_SELECT 1104 +#define ER_UNKNOWN_ERROR 1105 +#define ER_UNKNOWN_PROCEDURE 1106 +#define ER_WRONG_PARAMCOUNT_TO_PROCEDURE 1107 +#define ER_WRONG_PARAMETERS_TO_PROCEDURE 1108 +#define ER_UNKNOWN_TABLE 1109 +#define ER_FIELD_SPECIFIED_TWICE 1110 +#define ER_INVALID_GROUP_FUNC_USE 1111 +#define ER_UNSUPPORTED_EXTENSION 1112 +#define ER_TABLE_MUST_HAVE_COLUMNS 1113 +#define ER_RECORD_FILE_FULL 1114 +#define ER_UNKNOWN_CHARACTER_SET 1115 +#define ER_TOO_MANY_TABLES 1116 +#define ER_TOO_MANY_FIELDS 1117 +#define ER_TOO_BIG_ROWSIZE 1118 +#define ER_STACK_OVERRUN 1119 +#define ER_WRONG_OUTER_JOIN 1120 +#define ER_NULL_COLUMN_IN_INDEX 1121 +#define ER_CANT_FIND_UDF 1122 +#define ER_CANT_INITIALIZE_UDF 1123 +#define ER_UDF_NO_PATHS 1124 +#define ER_UDF_EXISTS 1125 +#define ER_CANT_OPEN_LIBRARY 1126 +#define ER_CANT_FIND_DL_ENTRY 1127 +#define ER_FUNCTION_NOT_DEFINED 1128 +#define ER_HOST_IS_BLOCKED 1129 +#define ER_HOST_NOT_PRIVILEGED 1130 +#define ER_PASSWORD_ANONYMOUS_USER 1131 +#define ER_PASSWORD_NOT_ALLOWED 1132 +#define ER_PASSWORD_NO_MATCH 1133 +#define ER_UPDATE_INFO 1134 +#define ER_CANT_CREATE_THREAD 1135 +#define ER_WRONG_VALUE_COUNT_ON_ROW 1136 +#define ER_CANT_REOPEN_TABLE 1137 +#define ER_INVALID_USE_OF_NULL 1138 +#define ER_REGEXP_ERROR 1139 +#define ER_MIX_OF_GROUP_FUNC_AND_FIELDS 1140 +#define ER_NONEXISTING_GRANT 1141 +#define ER_TABLEACCESS_DENIED_ERROR 1142 +#define ER_COLUMNACCESS_DENIED_ERROR 1143 +#define ER_ILLEGAL_GRANT_FOR_TABLE 1144 +#define ER_GRANT_WRONG_HOST_OR_USER 1145 +#define ER_NO_SUCH_TABLE 1146 +#define ER_NONEXISTING_TABLE_GRANT 1147 +#define ER_NOT_ALLOWED_COMMAND 1148 +#define ER_SYNTAX_ERROR 1149 +#define ER_DELAYED_CANT_CHANGE_LOCK 1150 +#define ER_TOO_MANY_DELAYED_THREADS 1151 +#define ER_ABORTING_CONNECTION 1152 +#define ER_NET_PACKET_TOO_LARGE 1153 +#define ER_NET_READ_ERROR_FROM_PIPE 1154 +#define ER_NET_FCNTL_ERROR 1155 +#define ER_NET_PACKETS_OUT_OF_ORDER 1156 +#define ER_NET_UNCOMPRESS_ERROR 1157 +#define ER_NET_READ_ERROR 1158 +#define ER_NET_READ_INTERRUPTED 1159 +#define ER_NET_ERROR_ON_WRITE 1160 +#define ER_NET_WRITE_INTERRUPTED 1161 +#define ER_TOO_LONG_STRING 1162 +#define ER_TABLE_CANT_HANDLE_BLOB 1163 +#define ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 1164 +#define ER_DELAYED_INSERT_TABLE_LOCKED 1165 +#define ER_WRONG_COLUMN_NAME 1166 +#define ER_WRONG_KEY_COLUMN 1167 +#define ER_WRONG_MRG_TABLE 1168 +#define ER_DUP_UNIQUE 1169 +#define ER_BLOB_KEY_WITHOUT_LENGTH 1170 +#define ER_PRIMARY_CANT_HAVE_NULL 1171 +#define ER_TOO_MANY_ROWS 1172 +#define ER_REQUIRES_PRIMARY_KEY 1173 +#define ER_NO_RAID_COMPILED 1174 +#define ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE 1175 +#define ER_KEY_DOES_NOT_EXITS 1176 +#define ER_CHECK_NO_SUCH_TABLE 1177 +#define ER_CHECK_NOT_IMPLEMENTED 1178 +#define ER_CANT_DO_THIS_DURING_AN_TRANSACTION 1179 +#define ER_ERROR_DURING_COMMIT 1180 +#define ER_ERROR_DURING_ROLLBACK 1181 +#define ER_ERROR_DURING_FLUSH_LOGS 1182 +#define ER_ERROR_DURING_CHECKPOINT 1183 +#define ER_NEW_ABORTING_CONNECTION 1184 +#define ER_DUMP_NOT_IMPLEMENTED 1185 +#define ER_FLUSH_MASTER_BINLOG_CLOSED 1186 +#define ER_INDEX_REBUILD 1187 +#define ER_MASTER 1188 +#define ER_MASTER_NET_READ 1189 +#define ER_MASTER_NET_WRITE 1190 +#define ER_FT_MATCHING_KEY_NOT_FOUND 1191 +#define ER_LOCK_OR_ACTIVE_TRANSACTION 1192 +#define ER_UNKNOWN_SYSTEM_VARIABLE 1193 +#define ER_CRASHED_ON_USAGE 1194 +#define ER_CRASHED_ON_REPAIR 1195 +#define ER_WARNING_NOT_COMPLETE_ROLLBACK 1196 +#define ER_TRANS_CACHE_FULL 1197 +#define ER_SLAVE_MUST_STOP 1198 +#define ER_SLAVE_NOT_RUNNING 1199 +#define ER_BAD_SLAVE 1200 +#define ER_MASTER_INFO 1201 +#define ER_SLAVE_THREAD 1202 +#define ER_TOO_MANY_USER_CONNECTIONS 1203 +#define ER_SET_CONSTANTS_ONLY 1204 +#define ER_LOCK_WAIT_TIMEOUT 1205 +#define ER_LOCK_TABLE_FULL 1206 +#define ER_READ_ONLY_TRANSACTION 1207 +#define ER_DROP_DB_WITH_READ_LOCK 1208 +#define ER_CREATE_DB_WITH_READ_LOCK 1209 +#define ER_WRONG_ARGUMENTS 1210 +#define ER_NO_PERMISSION_TO_CREATE_USER 1211 +#define ER_UNION_TABLES_IN_DIFFERENT_DIR 1212 +#define ER_LOCK_DEADLOCK 1213 +#define ER_TABLE_CANT_HANDLE_FT 1214 +#define ER_CANNOT_ADD_FOREIGN 1215 +#define ER_NO_REFERENCED_ROW 1216 +#define ER_ROW_IS_REFERENCED 1217 +#define ER_CONNECT_TO_MASTER 1218 +#define ER_QUERY_ON_MASTER 1219 +#define ER_ERROR_WHEN_EXECUTING_COMMAND 1220 +#define ER_WRONG_USAGE 1221 +#define ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 1222 +#define ER_CANT_UPDATE_WITH_READLOCK 1223 +#define ER_MIXING_NOT_ALLOWED 1224 +#define ER_DUP_ARGUMENT 1225 +#define ER_USER_LIMIT_REACHED 1226 +#define ER_SPECIFIC_ACCESS_DENIED_ERROR 1227 +#define ER_LOCAL_VARIABLE 1228 +#define ER_GLOBAL_VARIABLE 1229 +#define ER_NO_DEFAULT 1230 +#define ER_WRONG_VALUE_FOR_VAR 1231 +#define ER_WRONG_TYPE_FOR_VAR 1232 +#define ER_VAR_CANT_BE_READ 1233 +#define ER_CANT_USE_OPTION_HERE 1234 +#define ER_NOT_SUPPORTED_YET 1235 +#define ER_MASTER_FATAL_ERROR_READING_BINLOG 1236 +#define ER_SLAVE_IGNORED_TABLE 1237 +#define ER_INCORRECT_GLOBAL_LOCAL_VAR 1238 +#define ER_WRONG_FK_DEF 1239 +#define ER_KEY_REF_DO_NOT_MATCH_TABLE_REF 1240 +#define ER_OPERAND_COLUMNS 1241 +#define ER_SUBQUERY_NO_1_ROW 1242 +#define ER_UNKNOWN_STMT_HANDLER 1243 +#define ER_CORRUPT_HELP_DB 1244 +#define ER_CYCLIC_REFERENCE 1245 +#define ER_AUTO_CONVERT 1246 +#define ER_ILLEGAL_REFERENCE 1247 +#define ER_DERIVED_MUST_HAVE_ALIAS 1248 +#define ER_SELECT_REDUCED 1249 +#define ER_TABLENAME_NOT_ALLOWED_HERE 1250 +#define ER_NOT_SUPPORTED_AUTH_MODE 1251 +#define ER_SPATIAL_CANT_HAVE_NULL 1252 +#define ER_COLLATION_CHARSET_MISMATCH 1253 +#define ER_SLAVE_WAS_RUNNING 1254 +#define ER_SLAVE_WAS_NOT_RUNNING 1255 +#define ER_TOO_BIG_FOR_UNCOMPRESS 1256 +#define ER_ZLIB_Z_MEM_ERROR 1257 +#define ER_ZLIB_Z_BUF_ERROR 1258 +#define ER_ZLIB_Z_DATA_ERROR 1259 +#define ER_CUT_VALUE_GROUP_CONCAT 1260 +#define ER_WARN_TOO_FEW_RECORDS 1261 +#define ER_WARN_TOO_MANY_RECORDS 1262 +#define ER_WARN_NULL_TO_NOTNULL 1263 +#define ER_WARN_DATA_OUT_OF_RANGE 1264 +#define WARN_DATA_TRUNCATED 1265 +#define ER_WARN_USING_OTHER_HANDLER 1266 +#define ER_CANT_AGGREGATE_2COLLATIONS 1267 +#define ER_DROP_USER 1268 +#define ER_REVOKE_GRANTS 1269 +#define ER_CANT_AGGREGATE_3COLLATIONS 1270 +#define ER_CANT_AGGREGATE_NCOLLATIONS 1271 +#define ER_VARIABLE_IS_NOT_STRUCT 1272 +#define ER_UNKNOWN_COLLATION 1273 +#define ER_SLAVE_IGNORED_SSL_PARAMS 1274 +#define ER_SERVER_IS_IN_SECURE_AUTH_MODE 1275 +#define ER_WARN_FIELD_RESOLVED 1276 +#define ER_BAD_SLAVE_UNTIL_COND 1277 +#define ER_MISSING_SKIP_SLAVE 1278 +#define ER_UNTIL_COND_IGNORED 1279 +#define ER_WRONG_NAME_FOR_INDEX 1280 +#define ER_WRONG_NAME_FOR_CATALOG 1281 +#define ER_WARN_QC_RESIZE 1282 +#define ER_BAD_FT_COLUMN 1283 +#define ER_UNKNOWN_KEY_CACHE 1284 +#define ER_WARN_HOSTNAME_WONT_WORK 1285 +#define ER_UNKNOWN_STORAGE_ENGINE 1286 +#define ER_WARN_DEPRECATED_SYNTAX 1287 +#define ER_NON_UPDATABLE_TABLE 1288 +#define ER_FEATURE_DISABLED 1289 +#define ER_OPTION_PREVENTS_STATEMENT 1290 +#define ER_DUPLICATED_VALUE_IN_TYPE 1291 +#define ER_TRUNCATED_WRONG_VALUE 1292 +#define ER_TOO_MUCH_AUTO_TIMESTAMP_COLS 1293 +#define ER_INVALID_ON_UPDATE 1294 +#define ER_UNSUPPORTED_PS 1295 +#define ER_GET_ERRMSG 1296 +#define ER_GET_TEMPORARY_ERRMSG 1297 +#define ER_UNKNOWN_TIME_ZONE 1298 +#define ER_WARN_INVALID_TIMESTAMP 1299 +#define ER_INVALID_CHARACTER_STRING 1300 +#define ER_WARN_ALLOWED_PACKET_OVERFLOWED 1301 +#define ER_CONFLICTING_DECLARATIONS 1302 +#define ER_SP_NO_RECURSIVE_CREATE 1303 +#define ER_SP_ALREADY_EXISTS 1304 +#define ER_SP_DOES_NOT_EXIST 1305 +#define ER_SP_DROP_FAILED 1306 +#define ER_SP_STORE_FAILED 1307 +#define ER_SP_LILABEL_MISMATCH 1308 +#define ER_SP_LABEL_REDEFINE 1309 +#define ER_SP_LABEL_MISMATCH 1310 +#define ER_SP_UNINIT_VAR 1311 +#define ER_SP_BADSELECT 1312 +#define ER_SP_BADRETURN 1313 +#define ER_SP_BADSTATEMENT 1314 +#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1315 +#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1316 +#define ER_QUERY_INTERRUPTED 1317 +#define ER_SP_WRONG_NO_OF_ARGS 1318 +#define ER_SP_COND_MISMATCH 1319 +#define ER_SP_NORETURN 1320 +#define ER_SP_NORETURNEND 1321 +#define ER_SP_BAD_CURSOR_QUERY 1322 +#define ER_SP_BAD_CURSOR_SELECT 1323 +#define ER_SP_CURSOR_MISMATCH 1324 +#define ER_SP_CURSOR_ALREADY_OPEN 1325 +#define ER_SP_CURSOR_NOT_OPEN 1326 +#define ER_SP_UNDECLARED_VAR 1327 +#define ER_SP_WRONG_NO_OF_FETCH_ARGS 1328 +#define ER_SP_FETCH_NO_DATA 1329 +#define ER_SP_DUP_PARAM 1330 +#define ER_SP_DUP_VAR 1331 +#define ER_SP_DUP_COND 1332 +#define ER_SP_DUP_CURS 1333 +#define ER_SP_CANT_ALTER 1334 +#define ER_SP_SUBSELECT_NYI 1335 +#define ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 1336 +#define ER_SP_VARCOND_AFTER_CURSHNDLR 1337 +#define ER_SP_CURSOR_AFTER_HANDLER 1338 +#define ER_SP_CASE_NOT_FOUND 1339 +#define ER_FPARSER_TOO_BIG_FILE 1340 +#define ER_FPARSER_BAD_HEADER 1341 +#define ER_FPARSER_EOF_IN_COMMENT 1342 +#define ER_FPARSER_ERROR_IN_PARAMETER 1343 +#define ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER 1344 +#define ER_VIEW_NO_EXPLAIN 1345 +#define ER_FRM_UNKNOWN_TYPE 1346 +#define ER_WRONG_OBJECT 1347 +#define ER_NONUPDATEABLE_COLUMN 1348 +#define ER_VIEW_SELECT_DERIVED 1349 +#define ER_VIEW_SELECT_CLAUSE 1350 +#define ER_VIEW_SELECT_VARIABLE 1351 +#define ER_VIEW_SELECT_TMPTABLE 1352 +#define ER_VIEW_WRONG_LIST 1353 +#define ER_WARN_VIEW_MERGE 1354 +#define ER_WARN_VIEW_WITHOUT_KEY 1355 +#define ER_VIEW_INVALID 1356 +#define ER_SP_NO_DROP_SP 1357 +#define ER_SP_GOTO_IN_HNDLR 1358 +#define ER_TRG_ALREADY_EXISTS 1359 +#define ER_TRG_DOES_NOT_EXIST 1360 +#define ER_TRG_ON_VIEW_OR_TEMP_TABLE 1361 +#define ER_TRG_CANT_CHANGE_ROW 1362 +#define ER_TRG_NO_SUCH_ROW_IN_TRG 1363 +#define ER_NO_DEFAULT_FOR_FIELD 1364 +#define ER_DIVISION_BY_ZERO 1365 +#define ER_TRUNCATED_WRONG_VALUE_FOR_FIELD 1366 +#define ER_ILLEGAL_VALUE_FOR_TYPE 1367 +#define ER_VIEW_NONUPD_CHECK 1368 +#define ER_VIEW_CHECK_FAILED 1369 +#define ER_PROCACCESS_DENIED_ERROR 1370 +#define ER_RELAY_LOG_FAIL 1371 +#define ER_PASSWD_LENGTH 1372 +#define ER_UNKNOWN_TARGET_BINLOG 1373 +#define ER_IO_ERR_LOG_INDEX_READ 1374 +#define ER_BINLOG_PURGE_PROHIBITED 1375 +#define ER_FSEEK_FAIL 1376 +#define ER_BINLOG_PURGE_FATAL_ERR 1377 +#define ER_LOG_IN_USE 1378 +#define ER_LOG_PURGE_UNKNOWN_ERR 1379 +#define ER_RELAY_LOG_INIT 1380 +#define ER_NO_BINARY_LOGGING 1381 +#define ER_RESERVED_SYNTAX 1382 +#define ER_WSAS_FAILED 1383 +#define ER_DIFF_GROUPS_PROC 1384 +#define ER_NO_GROUP_FOR_PROC 1385 +#define ER_ORDER_WITH_PROC 1386 +#define ER_LOGGING_PROHIBIT_CHANGING_OF 1387 +#define ER_NO_FILE_MAPPING 1388 +#define ER_WRONG_MAGIC 1389 +#define ER_PS_MANY_PARAM 1390 +#define ER_KEY_PART_0 1391 +#define ER_VIEW_CHECKSUM 1392 +#define ER_VIEW_MULTIUPDATE 1393 +#define ER_VIEW_NO_INSERT_FIELD_LIST 1394 +#define ER_VIEW_DELETE_MERGE_VIEW 1395 +#define ER_CANNOT_USER 1396 +#define ER_XAER_NOTA 1397 +#define ER_XAER_INVAL 1398 +#define ER_XAER_RMFAIL 1399 +#define ER_XAER_OUTSIDE 1400 +#define ER_XAER_RMERR 1401 +#define ER_XA_RBROLLBACK 1402 +#define ER_NONEXISTING_PROC_GRANT 1403 +#define ER_PROC_AUTO_GRANT_FAIL 1404 +#define ER_PROC_AUTO_REVOKE_FAIL 1405 +#define ER_DATA_TOO_LONG 1406 +#define ER_SP_BAD_SQLSTATE 1407 +#define ER_STARTUP 1408 +#define ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR 1409 +#define ER_CANT_CREATE_USER_WITH_GRANT 1410 +#define ER_WRONG_VALUE_FOR_TYPE 1411 +#define ER_TABLE_DEF_CHANGED 1412 +#define ER_SP_DUP_HANDLER 1413 +#define ER_SP_NOT_VAR_ARG 1414 +#define ER_SP_NO_RETSET 1415 +#define ER_CANT_CREATE_GEOMETRY_OBJECT 1416 +#define ER_FAILED_ROUTINE_BREAK_BINLOG 1417 +#define ER_BINLOG_UNSAFE_ROUTINE 1418 +#define ER_BINLOG_CREATE_ROUTINE_NEED_SUPER 1419 +#define ER_EXEC_STMT_WITH_OPEN_CURSOR 1420 +#define ER_STMT_HAS_NO_OPEN_CURSOR 1421 +#define ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG 1422 +#define ER_NO_DEFAULT_FOR_VIEW_FIELD 1423 +#define ER_SP_NO_RECURSION 1424 +#define ER_TOO_BIG_SCALE 1425 +#define ER_TOO_BIG_PRECISION 1426 +#define ER_M_BIGGER_THAN_D 1427 +#define ER_WRONG_LOCK_OF_SYSTEM_TABLE 1428 +#define ER_CONNECT_TO_FOREIGN_DATA_SOURCE 1429 +#define ER_QUERY_ON_FOREIGN_DATA_SOURCE 1430 +#define ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST 1431 +#define ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE 1432 +#define ER_FOREIGN_DATA_STRING_INVALID 1433 +#define ER_CANT_CREATE_FEDERATED_TABLE 1434 +#define ER_TRG_IN_WRONG_SCHEMA 1435 +#define ER_STACK_OVERRUN_NEED_MORE 1436 +#define ER_TOO_LONG_BODY 1437 +#define ER_WARN_CANT_DROP_DEFAULT_KEYCACHE 1438 +#define ER_TOO_BIG_DISPLAYWIDTH 1439 +#define ER_XAER_DUPID 1440 +#define ER_DATETIME_FUNCTION_OVERFLOW 1441 +#define ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG 1442 +#define ER_VIEW_PREVENT_UPDATE 1443 +#define ER_PS_NO_RECURSION 1444 +#define ER_SP_CANT_SET_AUTOCOMMIT 1445 +#define ER_NO_VIEW_USER 1446 +#define ER_VIEW_FRM_NO_USER 1447 +#define ER_VIEW_OTHER_USER 1448 +#define ER_NO_SUCH_USER 1449 +#define ER_FORBID_SCHEMA_CHANGE 1450 +#define ER_ROW_IS_REFERENCED_2 1451 +#define ER_NO_REFERENCED_ROW_2 1452 +#define ER_SP_BAD_VAR_SHADOW 1453 +#define ER_ERROR_LAST 1453 diff --git a/Src/3rd_party/mysql/include/sched.h b/Src/3rd_party/mysql/include/sched.h new file mode 100644 index 0000000..f36a97a --- /dev/null +++ b/Src/3rd_party/mysql/include/sched.h @@ -0,0 +1,183 @@ +/* + * Module: sched.h + * + * Purpose: + * Provides an implementation of POSIX realtime extensions + * as defined in + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(_SCHED_H) +#define _SCHED_H + +#undef PTW32_SCHED_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_SCHED_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_SCHED_LEVEL_MAX 3 + +#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL) +#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX +/* Include everything */ +#endif + + +#if defined(__GNUC__) && !defined(__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#if !defined(PTW32_STATIC_LIB) +# if defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#if !defined(PTW32_CONFIG_H) +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +#if defined(NEED_ERRNO) +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */ + +#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN) +# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +/* For pid_t */ +# include +/* Required by Unix 98 */ +# include +# else + typedef int pid_t; +# endif +#else + typedef int pid_t; +#endif + +/* Thread scheduling policies */ + +enum { + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +struct sched_param { + int sched_priority; +}; + +#if defined(__cplusplus) +extern "C" +{ +#endif /* __cplusplus */ + +PTW32_DLLPORT int __cdecl sched_yield (void); + +PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy); + +PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy); + +PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy); + +PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid); + +/* + * Note that this macro returns ENOTSUP rather than + * ENOSYS as might be expected. However, returning ENOSYS + * should mean that sched_get_priority_{min,max} are + * not implemented as well as sched_rr_get_interval. + * This is not the case, since we just don't support + * round-robin scheduling. Therefore I have chosen to + * return the same value as sched_setscheduler when + * SCHED_RR is passed to it. + */ +#define sched_rr_get_interval(_pid, _interval) \ + ( errno = ENOTSUP, (int) -1 ) + + +#if defined(__cplusplus) +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#undef PTW32_SCHED_LEVEL +#undef PTW32_SCHED_LEVEL_MAX + +#endif /* !_SCHED_H */ + diff --git a/Src/3rd_party/mysql/include/typelib.h b/Src/3rd_party/mysql/include/typelib.h new file mode 100644 index 0000000..4d6a90a --- /dev/null +++ b/Src/3rd_party/mysql/include/typelib.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifndef _typelib_h +#define _typelib_h + +typedef struct st_typelib { /* Different types saved here */ + unsigned int count; /* How many types */ + const char *name; /* Name of typelib */ + const char **type_names; + unsigned int *type_lengths; +} TYPELIB; + +extern int find_type(char *x,TYPELIB *typelib,unsigned int full_name); +extern void make_type(char *to,unsigned int nr,TYPELIB *typelib); +extern const char *get_type(TYPELIB *typelib,unsigned int nr); + +extern TYPELIB sql_protocol_typelib; + +#endif /* _typelib_h */ diff --git a/Src/3rd_party/mysql/lib/libmysql.dll b/Src/3rd_party/mysql/lib/libmysql.dll new file mode 100644 index 0000000..d18a3fc Binary files /dev/null and b/Src/3rd_party/mysql/lib/libmysql.dll differ diff --git a/Src/3rd_party/mysql/lib/libmysql.lib b/Src/3rd_party/mysql/lib/libmysql.lib new file mode 100644 index 0000000..640c9a3 Binary files /dev/null and b/Src/3rd_party/mysql/lib/libmysql.lib differ diff --git a/Src/3rd_party/pthread/bin/x64/pthreadGC2.dll b/Src/3rd_party/pthread/bin/x64/pthreadGC2.dll new file mode 100644 index 0000000..841d4a2 Binary files /dev/null and b/Src/3rd_party/pthread/bin/x64/pthreadGC2.dll differ diff --git a/Src/3rd_party/pthread/bin/x64/pthreadVC2.dll b/Src/3rd_party/pthread/bin/x64/pthreadVC2.dll new file mode 100644 index 0000000..165b4d2 Binary files /dev/null and b/Src/3rd_party/pthread/bin/x64/pthreadVC2.dll differ diff --git a/Src/3rd_party/pthread/bin/x86/pthreadGC2.dll b/Src/3rd_party/pthread/bin/x86/pthreadGC2.dll new file mode 100644 index 0000000..67b9289 Binary files /dev/null and b/Src/3rd_party/pthread/bin/x86/pthreadGC2.dll differ diff --git a/Src/3rd_party/pthread/bin/x86/pthreadGCE2.dll b/Src/3rd_party/pthread/bin/x86/pthreadGCE2.dll new file mode 100644 index 0000000..9e18ea2 Binary files /dev/null and b/Src/3rd_party/pthread/bin/x86/pthreadGCE2.dll differ diff --git a/Src/3rd_party/pthread/bin/x86/pthreadVC2.dll b/Src/3rd_party/pthread/bin/x86/pthreadVC2.dll new file mode 100644 index 0000000..fcb5d9d Binary files /dev/null and b/Src/3rd_party/pthread/bin/x86/pthreadVC2.dll differ diff --git a/Src/3rd_party/pthread/bin/x86/pthreadVCE2.dll b/Src/3rd_party/pthread/bin/x86/pthreadVCE2.dll new file mode 100644 index 0000000..9d148cc Binary files /dev/null and b/Src/3rd_party/pthread/bin/x86/pthreadVCE2.dll differ diff --git a/Src/3rd_party/pthread/bin/x86/pthreadVSE2.dll b/Src/3rd_party/pthread/bin/x86/pthreadVSE2.dll new file mode 100644 index 0000000..8129116 Binary files /dev/null and b/Src/3rd_party/pthread/bin/x86/pthreadVSE2.dll differ diff --git a/Src/3rd_party/pthread/include/pthread.h b/Src/3rd_party/pthread/include/pthread.h new file mode 100644 index 0000000..770f431 --- /dev/null +++ b/Src/3rd_party/pthread/include/pthread.h @@ -0,0 +1,1368 @@ +/* This is an implementation of the threads API of POSIX 1003.1-2001. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if !defined( PTHREAD_H ) +#define PTHREAD_H + +/* + * See the README file for an explanation of the pthreads-win32 version + * numbering scheme and how the DLL is named etc. + */ +#define PTW32_VERSION 2,9,1,0 +#define PTW32_VERSION_STRING "2, 9, 1, 0\0" + +/* There are three implementations of cancel cleanup. + * Note that pthread.h is included in both application + * compilation units and also internally for the library. + * The code here and within the library aims to work + * for all reasonable combinations of environments. + * + * The three implementations are: + * + * WIN32 SEH + * C + * C++ + * + * Please note that exiting a push/pop block via + * "return", "exit", "break", or "continue" will + * lead to different behaviour amongst applications + * depending upon whether the library was built + * using SEH, C++, or C. For example, a library built + * with SEH will call the cleanup routine, while both + * C++ and C built versions will not. + */ + +/* + * Define defaults for cleanup code. + * Note: Unless the build explicitly defines one of the following, then + * we default to standard C style cleanup. This style uses setjmp/longjmp + * in the cancelation and thread exit implementations and therefore won't + * do stack unwinding if linked to applications that have it (e.g. + * C++ apps). This is currently consistent with most/all commercial Unix + * POSIX threads implementations. + */ +#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C ) +# define __CLEANUP_C +#endif + +#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC)) +#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler. +#endif + +/* + * Stop here if we are being included by the resource compiler. + */ +#if !defined(RC_INVOKED) + +#undef PTW32_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_LEVEL +#define PTW32_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_LEVEL +#define PTW32_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_LEVEL_MAX 3 + +#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_LEVEL) +#define PTW32_LEVEL PTW32_LEVEL_MAX +/* Include everything */ +#endif + +#if defined(_UWIN) +# define HAVE_STRUCT_TIMESPEC 1 +# define HAVE_SIGNAL_H 1 +# undef HAVE_PTW32_CONFIG_H +# pragma comment(lib, "pthread") +#endif + +/* + * ------------------------------------------------------------- + * + * + * Module: pthread.h + * + * Purpose: + * Provides an implementation of PThreads based upon the + * standard: + * + * POSIX 1003.1-2001 + * and + * The Single Unix Specification version 3 + * + * (these two are equivalent) + * + * in order to enhance code portability between Windows, + * various commercial Unix implementations, and Linux. + * + * See the ANNOUNCE file for a full list of conforming + * routines and defined constants, and a list of missing + * routines and constants not defined in this implementation. + * + * Authors: + * There have been many contributors to this library. + * The initial implementation was contributed by + * John Bossom, and several others have provided major + * sections or revisions of parts of the implementation. + * Often significant effort has been contributed to + * find and fix important bugs and other problems to + * improve the reliability of the library, which sometimes + * is not reflected in the amount of code which changed as + * result. + * As much as possible, the contributors are acknowledged + * in the ChangeLog file in the source code distribution + * where their changes are noted in detail. + * + * Contributors are listed in the CONTRIBUTORS file. + * + * As usual, all bouquets go to the contributors, and all + * brickbats go to the project maintainer. + * + * Maintainer: + * The code base for this project is coordinated and + * eventually pre-tested, packaged, and made available by + * + * Ross Johnson + * + * QA Testers: + * Ultimately, the library is tested in the real world by + * a host of competent and demanding scientists and + * engineers who report bugs and/or provide solutions + * which are then fixed or incorporated into subsequent + * versions of the library. Each time a bug is fixed, a + * test case is written to prove the fix and ensure + * that later changes to the code don't reintroduce the + * same error. The number of test cases is slowly growing + * and therefore so is the code reliability. + * + * Compliance: + * See the file ANNOUNCE for the list of implemented + * and not-implemented routines and defined options. + * Of course, these are all defined is this file as well. + * + * Web site: + * The source code and other information about this library + * are available from + * + * http://sources.redhat.com/pthreads-win32/ + * + * ------------------------------------------------------------- + */ + +/* Try to avoid including windows.h */ +#if (defined(__MINGW64__) || defined(__MINGW32__)) && defined(__cplusplus) +#define PTW32_INCLUDE_WINDOWS_H +#endif + +#if defined(PTW32_INCLUDE_WINDOWS_H) +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__) +/* + * VC++6.0 or early compiler's header has no DWORD_PTR type. + */ +typedef unsigned long DWORD_PTR; +typedef unsigned long ULONG_PTR; +#endif +/* + * ----------------- + * autoconf switches + * ----------------- + */ + +#if defined(HAVE_PTW32_CONFIG_H) +#include "config.h" +#endif /* HAVE_PTW32_CONFIG_H */ + +#if !defined(NEED_FTIME) +#include +#else /* NEED_FTIME */ +/* use native WIN32 time API */ +#endif /* NEED_FTIME */ + +#if defined(HAVE_SIGNAL_H) +#include +#endif /* HAVE_SIGNAL_H */ + +#include + +/* + * Boolean values to make us independent of system includes. + */ +enum { + PTW32_FALSE = 0, + PTW32_TRUE = (! PTW32_FALSE) +}; + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#if !defined(PTW32_CONFIG_H) +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +#if defined(NEED_ERRNO) +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Several systems don't define some error numbers. + */ +#if !defined(ENOTSUP) +# define ENOTSUP 48 /* This is the value in Solaris. */ +#endif + +#if !defined(ETIMEDOUT) +# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */ +#endif + +#if !defined(ENOSYS) +# define ENOSYS 140 /* Semi-arbitrary value */ +#endif + +#if !defined(EDEADLK) +# if defined(EDEADLOCK) +# define EDEADLK EDEADLOCK +# else +# define EDEADLK 36 /* This is the value in MSVC. */ +# endif +#endif + +/* POSIX 2008 - related to robust mutexes */ +#if !defined(EOWNERDEAD) +# define EOWNERDEAD 43 +#endif +#if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 44 +#endif + +#include "..\..\mysql\include\sched.h" + +/* + * To avoid including windows.h we define only those things that we + * actually need from it. + */ +#if !defined(PTW32_INCLUDE_WINDOWS_H) +#if !defined(HANDLE) +# define PTW32__HANDLE_DEF +# define HANDLE void * +#endif +#if !defined(DWORD) +# define PTW32__DWORD_DEF +# define DWORD unsigned long +#endif +#endif + +#if !defined(HAVE_STRUCT_TIMESPEC) +#define HAVE_STRUCT_TIMESPEC +#if !defined(_TIMESPEC_DEFINED) +#define _TIMESPEC_DEFINED +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif /* _TIMESPEC_DEFINED */ +#endif /* HAVE_STRUCT_TIMESPEC */ + +#if !defined(SIG_BLOCK) +#define SIG_BLOCK 0 +#endif /* SIG_BLOCK */ + +#if !defined(SIG_UNBLOCK) +#define SIG_UNBLOCK 1 +#endif /* SIG_UNBLOCK */ + +#if !defined(SIG_SETMASK) +#define SIG_SETMASK 2 +#endif /* SIG_SETMASK */ + +#if defined(__cplusplus) +extern "C" +{ +#endif /* __cplusplus */ + +/* + * ------------------------------------------------------------- + * + * POSIX 1003.1-2001 Options + * ========================= + * + * Options are normally set in , which is not provided + * with pthreads-win32. + * + * For conformance with the Single Unix Specification (version 3), all of the + * options below are defined, and have a value of either -1 (not supported) + * or 200112L (supported). + * + * These options can neither be left undefined nor have a value of 0, because + * either indicates that sysconf(), which is not implemented, may be used at + * runtime to check the status of the option. + * + * _POSIX_THREADS (== 200112L) + * If == 200112L, you can use threads + * + * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L) + * If == 200112L, you can control the size of a thread's + * stack + * pthread_attr_getstacksize + * pthread_attr_setstacksize + * + * _POSIX_THREAD_ATTR_STACKADDR (== -1) + * If == 200112L, you can allocate and control a thread's + * stack. If not supported, the following functions + * will return ENOSYS, indicating they are not + * supported: + * pthread_attr_getstackaddr + * pthread_attr_setstackaddr + * + * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1) + * If == 200112L, you can use realtime scheduling. + * This option indicates that the behaviour of some + * implemented functions conforms to the additional TPS + * requirements in the standard. E.g. rwlocks favour + * writers over readers when threads have equal priority. + * + * _POSIX_THREAD_PRIO_INHERIT (== -1) + * If == 200112L, you can create priority inheritance + * mutexes. + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PRIO_PROTECT (== -1) + * If == 200112L, you can create priority ceiling mutexes + * Indicates the availability of: + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutexattr_getprioceiling + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprioceiling + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PROCESS_SHARED (== -1) + * If set, you can create mutexes and condition + * variables that can be shared with another + * process.If set, indicates the availability + * of: + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L) + * If == 200112L you can use the special *_r library + * functions that provide thread-safe behaviour + * + * _POSIX_READER_WRITER_LOCKS (== 200112L) + * If == 200112L, you can use read/write locks + * + * _POSIX_SPIN_LOCKS (== 200112L) + * If == 200112L, you can use spin locks + * + * _POSIX_BARRIERS (== 200112L) + * If == 200112L, you can use barriers + * + * + These functions provide both 'inherit' and/or + * 'protect' protocol, based upon these macro + * settings. + * + * ------------------------------------------------------------- + */ + +/* + * POSIX Options + */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200809L + +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200809L + +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200809L + +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200809L + +#undef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L + +#undef _POSIX_THREAD_ATTR_STACKSIZE +#define _POSIX_THREAD_ATTR_STACKSIZE 200809L + +/* + * The following options are not supported + */ +#undef _POSIX_THREAD_ATTR_STACKADDR +#define _POSIX_THREAD_ATTR_STACKADDR -1 + +#undef _POSIX_THREAD_PRIO_INHERIT +#define _POSIX_THREAD_PRIO_INHERIT -1 + +#undef _POSIX_THREAD_PRIO_PROTECT +#define _POSIX_THREAD_PRIO_PROTECT -1 + +/* TPS is not fully supported. */ +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#define _POSIX_THREAD_PRIORITY_SCHEDULING -1 + +#undef _POSIX_THREAD_PROCESS_SHARED +#define _POSIX_THREAD_PROCESS_SHARED -1 + + +/* + * POSIX 1003.1-2001 Limits + * =========================== + * + * These limits are normally set in , which is not provided with + * pthreads-win32. + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Maximum number of attempts to destroy + * a thread's thread-specific data on + * termination (must be at least 4) + * + * PTHREAD_KEYS_MAX + * Maximum number of thread-specific data keys + * available per process (must be at least 128) + * + * PTHREAD_STACK_MIN + * Minimum supported stack size for a thread + * + * PTHREAD_THREADS_MAX + * Maximum number of threads supported per + * process (must be at least 64). + * + * SEM_NSEMS_MAX + * The maximum number of semaphores a process can have. + * (must be at least 256) + * + * SEM_VALUE_MAX + * The maximum value a semaphore can have. + * (must be at least 32767) + * + */ +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 + +#undef PTHREAD_DESTRUCTOR_ITERATIONS +#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX 128 + +#undef PTHREAD_KEYS_MAX +#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX + +#undef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 0 + +#undef _POSIX_THREAD_THREADS_MAX +#define _POSIX_THREAD_THREADS_MAX 64 + + /* Arbitrary value */ +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + + /* Arbitrary value */ +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +#undef _POSIX_SEM_VALUE_MAX +#define _POSIX_SEM_VALUE_MAX 32767 + +#undef SEM_VALUE_MAX +#define SEM_VALUE_MAX INT_MAX + + +#if defined(__GNUC__) && !defined(__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#if !defined(PTW32_STATIC_LIB) +# if defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * The Open Watcom C/C++ compiler uses a non-standard calling convention + * that passes function args in registers unless __cdecl is explicitly specified + * in exposed function prototypes. + * + * We force all calls to cdecl even though this could slow Watcom code down + * slightly. If you know that the Watcom compiler will be used to build both + * the DLL and application, then you can probably define this as a null string. + * Remember that pthread.h (this file) is used for both the DLL and application builds. + */ +#define PTW32_CDECL __cdecl + +#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX +# include +#else +/* + * Generic handle type - intended to extend uniqueness beyond + * that available with a simple pointer. It should scale for either + * IA-32 or IA-64. + */ +typedef struct { + void * p; /* Pointer to actual object */ + unsigned int x; /* Extra information - reuse count etc */ +} ptw32_handle_t; + +typedef ptw32_handle_t pthread_t; +typedef struct pthread_attr_t_ * pthread_attr_t; +typedef struct pthread_once_t_ pthread_once_t; +typedef struct pthread_key_t_ * pthread_key_t; +typedef struct pthread_mutex_t_ * pthread_mutex_t; +typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t; +typedef struct pthread_cond_t_ * pthread_cond_t; +typedef struct pthread_condattr_t_ * pthread_condattr_t; +#endif +typedef struct pthread_rwlock_t_ * pthread_rwlock_t; +typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t; +typedef struct pthread_spinlock_t_ * pthread_spinlock_t; +typedef struct pthread_barrier_t_ * pthread_barrier_t; +typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t; + +/* + * ==================== + * ==================== + * POSIX Threads + * ==================== + * ==================== + */ + +enum { +/* + * pthread_attr_{get,set}detachstate + */ + PTHREAD_CREATE_JOINABLE = 0, /* Default */ + PTHREAD_CREATE_DETACHED = 1, + +/* + * pthread_attr_{get,set}inheritsched + */ + PTHREAD_INHERIT_SCHED = 0, + PTHREAD_EXPLICIT_SCHED = 1, /* Default */ + +/* + * pthread_{get,set}scope + */ + PTHREAD_SCOPE_PROCESS = 0, + PTHREAD_SCOPE_SYSTEM = 1, /* Default */ + +/* + * pthread_setcancelstate paramters + */ + PTHREAD_CANCEL_ENABLE = 0, /* Default */ + PTHREAD_CANCEL_DISABLE = 1, + +/* + * pthread_setcanceltype parameters + */ + PTHREAD_CANCEL_ASYNCHRONOUS = 0, + PTHREAD_CANCEL_DEFERRED = 1, /* Default */ + +/* + * pthread_mutexattr_{get,set}pshared + * pthread_condattr_{get,set}pshared + */ + PTHREAD_PROCESS_PRIVATE = 0, + PTHREAD_PROCESS_SHARED = 1, + +/* + * pthread_mutexattr_{get,set}robust + */ + PTHREAD_MUTEX_STALLED = 0, /* Default */ + PTHREAD_MUTEX_ROBUST = 1, + +/* + * pthread_barrier_wait + */ + PTHREAD_BARRIER_SERIAL_THREAD = -1 +}; + +/* + * ==================== + * ==================== + * Cancelation + * ==================== + * ==================== + */ +#define PTHREAD_CANCELED ((void *)(size_t) -1) + + +/* + * ==================== + * ==================== + * Once Key + * ==================== + * ==================== + */ +#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0} + +struct pthread_once_t_ +{ + int done; /* indicates if user function has been executed */ + void * lock; + int reserved1; + int reserved2; +}; + + +/* + * ==================== + * ==================== + * Object initialisers + * ==================== + * ==================== + */ +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1) +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2) +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3) + +/* + * Compatibility with LinuxThreads + */ +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER + +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1) + +#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1) + +#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1) + + +/* + * Mutex types. + */ +enum +{ + /* Compatibility with LinuxThreads */ + PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP, + /* For compatibility with POSIX */ + PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + + +typedef struct ptw32_cleanup_t ptw32_cleanup_t; + +#if defined(_MSC_VER) +/* Disable MSVC 'anachronism used' warning */ +#pragma warning( disable : 4229 ) +#endif + +typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *); + +#if defined(_MSC_VER) +#pragma warning( default : 4229 ) +#endif + +struct ptw32_cleanup_t +{ + ptw32_cleanup_callback_t routine; + void *arg; + struct ptw32_cleanup_t *prev; +}; + +#if defined(__CLEANUP_SEH) + /* + * WIN32 SEH version of cancel cleanup. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \ + _cleanup.arg = (_arg); \ + __try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + __finally \ + { \ + if( _execute || AbnormalTermination()) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } \ + } + +#else /* __CLEANUP_SEH */ + +#if defined(__CLEANUP_C) + + /* + * C implementation of PThreads cancel cleanup + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \ + +#define pthread_cleanup_pop( _execute ) \ + (void) ptw32_pop_cleanup( _execute ); \ + } + +#else /* __CLEANUP_C */ + +#if defined(__CLEANUP_CXX) + + /* + * C++ version of cancel cleanup. + * - John E. Bossom. + */ + + class PThreadCleanup { + /* + * PThreadCleanup + * + * Purpose + * This class is a C++ helper class that is + * used to implement pthread_cleanup_push/ + * pthread_cleanup_pop. + * The destructor of this class automatically + * pops the pushed cleanup routine regardless + * of how the code exits the scope + * (i.e. such as by an exception) + */ + ptw32_cleanup_callback_t cleanUpRout; + void * obj; + int executeIt; + + public: + PThreadCleanup() : + cleanUpRout( 0 ), + obj( 0 ), + executeIt( 0 ) + /* + * No cleanup performed + */ + { + } + + PThreadCleanup( + ptw32_cleanup_callback_t routine, + void * arg ) : + cleanUpRout( routine ), + obj( arg ), + executeIt( 1 ) + /* + * Registers a cleanup routine for 'arg' + */ + { + } + + ~PThreadCleanup() + { + if ( executeIt && ((void *) cleanUpRout != (void *) 0) ) + { + (void) (*cleanUpRout)( obj ); + } + } + + void execute( int exec ) + { + executeIt = exec; + } + }; + + /* + * C++ implementation of PThreads cancel cleanup; + * This implementation takes advantage of a helper + * class who's destructor automatically calls the + * cleanup routine if we exit our scope weirdly + */ +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \ + (void *) (_arg) ); + +#define pthread_cleanup_pop( _execute ) \ + cleanup.execute( _execute ); \ + } + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* __CLEANUP_CXX */ + +#endif /* __CLEANUP_C */ + +#endif /* __CLEANUP_SEH */ + +/* + * =============== + * =============== + * Methods + * =============== + * =============== + */ + +/* + * PThread Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr, + int *detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr, + void **stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr, + size_t * stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr, + int detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr, + void *stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr, + size_t stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *, + int *); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr, + int inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr, + int * inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *, + int *); + +/* + * PThread Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(PTW32_CDECL *start) (void *), + void *arg); + +PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid); + +PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1, + pthread_t t2); + +PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr); + +PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread, + void **value_ptr); + +PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state, + int *oldstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type, + int *oldtype); + +PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control, + void (PTW32_CDECL *init_routine) (void)); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute); + +PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup, + ptw32_cleanup_callback_t routine, + void *arg); +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread Specific Data Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key, + void (PTW32_CDECL *destructor) (void *)); + +PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key); + +PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key, + const void *value); + +PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key); + + +/* + * Mutex Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, + int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust( + pthread_mutexattr_t *attr, + int robust); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust( + const pthread_mutexattr_t * attr, + int * robust); + +/* + * Barrier Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, + int pshared); + +/* + * Mutex Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex, + const pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex); + +/* + * Spinlock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock); + +/* + * Barrier Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier, + const pthread_barrierattr_t * attr, + unsigned int count); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier); + +/* + * Condition Variable Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr, + int pshared); + +/* + * Condition Variable Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond, + const pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond, + pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond); + +/* + * Scheduling + */ +PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread, + int policy, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread, + int *policy, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int); + +PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void); + +/* + * Read-Write Lock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, + int pshared); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 + +/* + * Signal Functions. Should be defined in but MSVC and MinGW32 + * already have signal.h that don't define these. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig); + +/* + * Non-portable functions + */ + +/* + * Compatibility with Linux. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, + int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, + int *kind); + +/* + * Possibly supported by other POSIX threads implementations + */ +PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval); +PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void); +PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread); + +/* + * Useful if an application wants to statically link + * the lib rather than load the DLL at run-time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void); + +/* + * Features that are auto-detected at load/run time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int); +enum ptw32_features { + PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */ + PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */ +}; + +/* + * Register a system time change with the library. + * Causes the library to perform various functions + * in response to the change. Should be called whenever + * the application's top level window receives a + * WM_TIMECHANGE message. It can be passed directly to + * pthread_create() as a new thread if desired. + */ +PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *); + +#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* + * Returns the Win32 HANDLE for the POSIX thread. + */ +PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread); +/* + * Returns the win32 thread ID for POSIX thread. + */ +PTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread); + + +/* + * Protected Methods + * + * This function blocks until the given WIN32 handle + * is signaled or pthread_cancel had been called. + * This function allows the caller to hook into the + * PThreads cancel mechanism. It is implemented using + * + * WaitForMultipleObjects + * + * on 'waitHandle' and a manually reset WIN32 Event + * used to implement pthread_cancel. The 'timeout' + * argument to TimedWait is simply passed to + * WaitForMultipleObjects. + */ +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle); +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle, + DWORD timeout); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread-Safe C Runtime Library Mappings. + */ +#if !defined(_UWIN) +# if defined(NEED_ERRNO) + PTW32_DLLPORT int * PTW32_CDECL _errno( void ); +# else +# if !defined(errno) +# if (defined(_MT) || defined(_DLL)) + __declspec(dllimport) extern int * __cdecl _errno(void); +# define errno (*_errno()) +# endif +# endif +# endif +#endif + +/* + * Some compiler environments don't define some things. + */ +#if defined(__BORLANDC__) +# define _ftime ftime +# define _timeb timeb +#endif + +#if defined(__cplusplus) + +/* + * Internal exceptions + */ +class ptw32_exception {}; +class ptw32_exception_cancel : public ptw32_exception {}; +class ptw32_exception_exit : public ptw32_exception {}; + +#endif + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* FIXME: This is only required if the library was built using SEH */ +/* + * Get internal SEH tag + */ +PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#if !defined(PTW32_BUILD) + +#if defined(__CLEANUP_SEH) + +/* + * Redefine the SEH __except keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#define __except( E ) \ + __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \ + ? EXCEPTION_CONTINUE_SEARCH : ( E ) ) + +#endif /* __CLEANUP_SEH */ + +#if defined(__CLEANUP_CXX) + +/* + * Redefine the C++ catch keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#if defined(_MSC_VER) + /* + * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll' + * if you want Pthread-Win32 cancelation and pthread_exit to work. + */ + +#if !defined(PtW32NoCatchWarn) + +#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.") +#pragma message("------------------------------------------------------------------") +#pragma message("When compiling applications with MSVC++ and C++ exception handling:") +#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads") +#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread") +#pragma message(" cancelation and pthread_exit to work. For example:") +#pragma message("") +#pragma message(" #if defined(PtW32CatchAll)") +#pragma message(" PtW32CatchAll") +#pragma message(" #else") +#pragma message(" catch(...)") +#pragma message(" #endif") +#pragma message(" {") +#pragma message(" /* Catchall block processing */") +#pragma message(" }") +#pragma message("------------------------------------------------------------------") + +#endif + +#define PtW32CatchAll \ + catch( ptw32_exception & ) { throw; } \ + catch( ... ) + +#else /* _MSC_VER */ + +#define catch( E ) \ + catch( ptw32_exception & ) { throw; } \ + catch( E ) + +#endif /* _MSC_VER */ + +#endif /* __CLEANUP_CXX */ + +#endif /* ! PTW32_BUILD */ + +#if defined(__cplusplus) +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#if defined(PTW32__HANDLE_DEF) +# undef HANDLE +#endif +#if defined(PTW32__DWORD_DEF) +# undef DWORD +#endif + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* ! RC_INVOKED */ + +#endif /* PTHREAD_H */ diff --git a/Src/3rd_party/pthread/lib/x64/pthreadVC2.lib b/Src/3rd_party/pthread/lib/x64/pthreadVC2.lib new file mode 100644 index 0000000..1b07e0e Binary files /dev/null and b/Src/3rd_party/pthread/lib/x64/pthreadVC2.lib differ diff --git a/Src/3rd_party/pthread/lib/x86/pthreadVC2.lib b/Src/3rd_party/pthread/lib/x86/pthreadVC2.lib new file mode 100644 index 0000000..c20ee20 Binary files /dev/null and b/Src/3rd_party/pthread/lib/x86/pthreadVC2.lib differ diff --git a/Src/3rd_party/pthread/lib/x86/pthreadVCE2.lib b/Src/3rd_party/pthread/lib/x86/pthreadVCE2.lib new file mode 100644 index 0000000..7f05317 Binary files /dev/null and b/Src/3rd_party/pthread/lib/x86/pthreadVCE2.lib differ diff --git a/Src/3rd_party/pthread/lib/x86/pthreadVSE2.lib b/Src/3rd_party/pthread/lib/x86/pthreadVSE2.lib new file mode 100644 index 0000000..3f3335d Binary files /dev/null and b/Src/3rd_party/pthread/lib/x86/pthreadVSE2.lib differ diff --git a/Src/Char Server/charclient.cpp b/Src/Char Server/charclient.cpp new file mode 100644 index 0000000..5420141 --- /dev/null +++ b/Src/Char Server/charclient.cpp @@ -0,0 +1,46 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +// Constructor +CCharClient::CCharClient( ) +{ + userid = 0; + platinum = false; + memset( &username, '\0', 17 ); + memset( &password, '\0', 33 ); + memset( &charname, '\0', 17 ); + accesslevel = 0; + channel = 0; + clanid = 0; + clan_rank = 0; + level = 0; + job = 0; + charid = 0; + isLoggedIn = false; + logout = false; +} + +// Deconstructor +CCharClient::~CCharClient( ) +{ + +} diff --git a/Src/Char Server/charpackets.cpp b/Src/Char Server/charpackets.cpp new file mode 100644 index 0000000..d77108d --- /dev/null +++ b/Src/Char Server/charpackets.cpp @@ -0,0 +1,579 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +// Do player identification +bool CCharServer::pakDoIdentify( CCharClient* thisclient, CPacket* P ) +{ + if (thisclient->isLoggedIn) return false; + MYSQL_RES *result; + MYSQL_ROW row; + thisclient->userid = GETDWORD((*P), 0x00); + memcpy( thisclient->password, &P->Buffer[4], 32 ); + result = DB->QStore("SELECT username,lastsvr,accesslevel,platinum FROM accounts WHERE id=%i AND password='%s'", thisclient->userid, thisclient->password); + if(result==NULL) return false; + if (mysql_num_rows( result ) != 1) + { + Log( MSG_HACK, "Someone tried to connect to char server with an invalid account" ); + DB->QFree( ); + return false; + } + else + { + row = mysql_fetch_row(result); + strncpy_s (thisclient->username, row[0], 16); + thisclient->channel = atoi(row[1]); + thisclient->accesslevel = atoi(row[2]); + thisclient->platinum = atoi(row[3]); + DB->QFree( ); + } + + Log( MSG_INFO,"User '%s'(#%i) logged in", thisclient->username, thisclient->userid ); + + BEGINPACKET( pak, 0x070c ); + ADDBYTE ( pak, 0 ); + ADDDWORD ( pak, 0x87654321); + ADDDWORD ( pak, 0x00000000 ); + thisclient->SendPacket( &pak ); + result = DB->QStore( "SELECT online FROM accounts WHERE username='%s'", thisclient->username ); + if(result==NULL) return false; + row = mysql_fetch_row(result); + bool online = atoi(row[0]); + DB->QFree( ); + if(online) + return false; + if(!DB->QExecute( "UPDATE accounts SET online=1 WHERE username='%s'", thisclient->username )) + return false; + thisclient->isLoggedIn = true; + thisclient->returnedfromWS=false; + return true; +} + +// Get this clients character list +bool CCharServer::pakGetCharacters( CCharClient* thisclient, CPacket* P ) +{ + if (!thisclient->isLoggedIn) return false; + // Properly delete items, quests, friends when deleting a character + // Hooray for joins. + MYSQL_RES *result; + MYSQL_ROW row; + DB->QExecute( "DELETE FROM characters, items, list_friend, list_quest USING characters LEFT JOIN items ON items.owner = characters.id LEFT JOIN list_friend \ + ON (list_friend.id = characters.id OR list_friend.idfriend = characters.id) LEFT JOIN list_quest ON list_quest.owner = characters.id WHERE (characters.deletetime > 0 AND characters.deletetime <= %i)", GetServerTime( ) ); + + CItem items[10]; + unsigned int charnum=0; + CCharacter chars[5]; + // 0 1 2 3 4 5 6 7 + result = DB->QStore("SELECT char_name,level,face,hairStyle,sex,classid,id,deletetime FROM characters WHERE account_name='%s'", thisclient->username); + if(result==NULL) return false; + while (row = mysql_fetch_row(result)) + { + strcpy( chars[charnum].char_name , row[0] ); + chars[charnum].level = atoi(row[1]); + chars[charnum].face = atoi(row[2]); + chars[charnum].hairStyle = atoi(row[3]); + chars[charnum].sex = atoi(row[4]); + chars[charnum].classid = atoi(row[5]); + chars[charnum].id = atoi(row[6]); + chars[charnum].DeleteTime = atoi(row[7]); + if(chars[charnum].DeleteTime > 0) + { + chars[charnum].DeleteTime = chars[charnum].DeleteTime - GetServerTime( ); + } + charnum++; + } + DB->QFree( ); + BEGINPACKET( pak, 0x0712 ); + ADDBYTE ( pak, charnum ); + for (unsigned k=0;kQStore("SELECT itemnum,itemtype,refine,durability,lifespan,slotnum FROM items WHERE owner=%i AND slotnum<10", chars[k].id); + result = DB->QStore("SELECT itemid, itemtype, refine, durability, lifespan FROM charitems WHERE owner=%i", chars[k].id); + + if(result == NULL) return false; + char *tmpid; + char *itmp; + while( row = mysql_fetch_row(result) ) + { + tmpid = strtok( row[0] , "|"); + for(unsigned int i=1;i<10; i++) + { + if((tmpid = strtok( NULL , "|"))!= NULL) + { + items[i].itemnum = atoi(tmpid); + } + } + tmpid = strtok( row[1] , "|"); + for(unsigned int i=1;i<10; i++) + { + if((tmpid = strtok( NULL , "|"))!= NULL) + { + items[i].itemtype = atoi(tmpid); + } + } + tmpid = strtok( row[2] , "|"); + for(unsigned int i=1;i<10; i++) + { + if((tmpid = strtok( NULL , "|"))!= NULL) + { + items[i].refine = atoi(tmpid); + } + } + tmpid = strtok( row[3] , "|"); + for(unsigned int i=1;i<10; i++) + { + if((tmpid = strtok( NULL , "|"))!= NULL) + { + items[i].durability = atoi(tmpid); + } + } + tmpid = strtok( row[4] , "|"); + for(unsigned int i=1;i<10; i++) + { + if((tmpid = strtok( NULL , "|"))!= NULL) + { + items[i].lifespan = atoi(tmpid); + } + } + } + /* + while(row = mysql_fetch_row(result)) + { + unsigned itemnum = atoi(row[5]); + items[itemnum].itemnum = atoi(row[0]); + items[itemnum].itemtype = atoi(row[1]); + items[itemnum].refine = atoi(row[2]); + items[itemnum].durability = atoi(row[3]); + items[itemnum].lifespan = atoi(row[4]); + }*/ + DB->QFree( ); + + ADDSTRING ( pak, chars[k].char_name ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, chars[k].sex ); // SEX + ADDWORD ( pak, chars[k].level ); // LEVEL + ADDWORD ( pak, chars[k].classid ); // CLASSID + ADDDWORD ( pak, chars[k].DeleteTime ); // DELETE TIME + ADDBYTE ( pak, 0x00 );//thisclient->platinum?0x01:0x00 ); // IS PLATINUM? 00-NO;01-YES;02-YES BUT USER IS NOT + ADDDWORD ( pak, chars[k].face ); // FACE + ADDDWORD ( pak, chars[k].hairStyle ); // HAIR + ADDWORD ( pak, items[2].itemnum ); // CAP + ADDWORD ( pak, items[2].refine ); // CAP REFINE + ADDWORD ( pak, items[3].itemnum ); // BODY + ADDWORD ( pak, items[3].refine ); // BODY REFINE + ADDWORD ( pak, items[5].itemnum ); // GLOVES + ADDWORD ( pak, items[5].refine ); // GLOVES REFINE + ADDWORD ( pak, items[6].itemnum ); // BOOTS + ADDWORD ( pak, items[6].refine ); // BOOTS REFINE + ADDWORD ( pak, items[1].itemnum ); // FACE + ADDWORD ( pak, items[1].refine ); // FACE REFINE + ADDWORD ( pak, items[4].itemnum ); // BACK + ADDWORD ( pak, items[4].refine ); // BACK REFINE + ADDWORD ( pak, items[7].itemnum ); // WEAPON + ADDWORD ( pak, items[7].refine ); // WEAPON REFINE + ADDWORD ( pak, items[8].itemnum ); // SUBWEAPON + ADDWORD ( pak, items[8].refine ); // SUBWEAPON REFINE + } + thisclient->SendPacket( &pak ); + return true; +} + +// Send away the world IP +bool CCharServer::pakRequestWorld( CCharClient* thisclient, CPacket* P ) +{ + if (!thisclient->isLoggedIn) return false; + MYSQL_ROW row; + MYSQL_RES *result; + memcpy( thisclient->charname, &P->Buffer[3], P->Size-6-4 ); + thisclient->charname[P->Size-6-4] = 0; + Log( MSG_INFO,"User %s(%i) selected char '%s'", thisclient->username, thisclient->userid, thisclient->charname); + if(!DB->QExecute("UPDATE accounts SET lastchar='%s' WHERE id=%i", thisclient->charname, thisclient->userid)) + return false; + result = DB->QStore("SELECT host,port FROM channels WHERE id=%i", thisclient->channel); + if(result==NULL) return false; + if(mysql_num_rows( result )!=1 ) + { + Log( MSG_WARNING, "Invalid Server: %i", thisclient->channel ); + DB->QFree( ); + return false; + } + row = mysql_fetch_row( result ); + BEGINPACKET( pak, 0x711 ); + ADDWORD ( pak, atoi(row[1]) ); // PORT + ADDDWORD ( pak, thisclient->userid ); + ADDDWORD ( pak, 0x87654321 ); + ADDSTRING ( pak, row[0] );//IP + ADDBYTE ( pak, 0 ); + thisclient->SendPacket( &pak ); + DB->QFree( ); +//CHAR INFORMATION 0 1 2 3 4 + result = DB->QStore("SELECT clanid,clan_rank,id,level,classid FROM characters WHERE char_name='%s'", thisclient->charname); + if(result==NULL) return false; + if(mysql_num_rows(result)!=1) + { + Log( MSG_WARNING, "Number of user with charname '%s' is %i", thisclient->charname,mysql_num_rows(result)); + DB->QFree( ); + return false; + } + row = mysql_fetch_row( result ); + thisclient->clanid = atoi( row[0] ); + thisclient->clan_rank = atoi( row[1] ); + thisclient->charid = atoi( row[2] ); + thisclient->level = atoi( row[3] ); + thisclient->job = atoi( row[4] ); + DB->QFree( ); +//MESSENGER INFORMATION +// // 0 1 + result = DB->QStore("SELECT idfriend,namefriend FROM list_friend WHERE id=%i", thisclient->charid); + if(result==NULL) return false; + BYTE nfriends = mysql_num_rows(result) & 0xff; + + RESETPACKET( pak, 0x7e1 );// Friend list command + ADDBYTE ( pak, 0x06 );// Sending Friend List + ADDBYTE ( pak, nfriends );// # friends + while(row = mysql_fetch_row( result )) + { + CFriendList * newfriend = new CFriendList; + assert(newfriend); + newfriend->id = atoi(row[0]); //friendid + strcpy(newfriend->name, row[1]); //friend name + thisclient->FriendList.push_back( newfriend ); + ADDWORD (pak, newfriend->id);// friend id + ADDWORD (pak, 0x0000 ); + CCharClient* otherclient = (CCharClient*) GetClientByID( newfriend->id ); + if(otherclient!=NULL) //is online?? + { + ADDBYTE (pak, 0x07 ); //online + ChangeMessengerStatus ( thisclient, otherclient, 0x07); + } + else + { + ADDBYTE (pak, 0x08 );//offline + } + ADDSTRING (pak, newfriend->name); //friend name + ADDBYTE (pak, 0x00 ); + } + thisclient->SendPacket( &pak ); + DB->QFree( ); + thisclient->logout = false; + + //SEND CLAN INFORMATION + SendClanInfo (thisclient); + + return true; +} + +// Create a character +bool CCharServer::pakCreateChar( CCharClient* thisclient, CPacket* P ) +{ + if (!thisclient->isLoggedIn) return false; + + // Get info from packet + unsigned sex = GETBYTE((*P), 0 ) % 2; + unsigned hairstyle = GETBYTE((*P), 2 ); + unsigned face = GETBYTE((*P), 3 ); + string newname = (char*)&P->Buffer[7]; + MYSQL_RES *result; + result = DB->QStore("SELECT id FROM characters WHERE account_name='%s'", thisclient->username); + if(result==NULL) return false; + if ( mysql_num_rows( result ) == 5 ) + { + STARTPACKET( pak, 0x0713, 8 ); + SETWORD ( pak, 0x00, 4 ) + thisclient->SendPacket( &pak ); + DB->QFree( ); + return true; + } + DB->QFree( ); + result = DB->QStore("SELECT id FROM characters WHERE char_name='%s'", newname.c_str()); + if(result==NULL) return false; + if ( mysql_num_rows( result ) > 0 ) + { + STARTPACKET( pak, 0x0713, 8 ); + SETWORD ( pak, 0x00, 2 ); + thisclient->SendPacket( &pak ); + DB->QFree( ); + return true; + } + DB->QFree( ); + + if(!DB->QExecute("INSERT INTO characters (account_name, char_name, face, hairStyle, sex) VALUES('%s','%s',%i,%i,%i)", thisclient->username, newname.c_str(), face, hairstyle, sex)) + return false; + unsigned int mid = (unsigned)mysql_insert_id( DB->Mysql ); + MYSQL_ROW row; + + char tmp1[1024] = ""; //itemid + char tmp2[1024] = ""; //itemtype + char tmp3[1024] = ""; //refine + char tmp4[1024] = ""; //durability + char tmp5[1024] = ""; //lifespan + char tmp6[1024] = ""; //count + char tmp7[1024] = ""; //stats + char tmp8[1024] = ""; //socketed + char tmp9[1024] = ""; //appraised + + sprintf(&tmp2[strlen(tmp2)], "0|0|0|3|0|0|0|8|0|0|0|0|2|0"); + sprintf(&tmp3[strlen(tmp3)], "0|0|0|0|0|0|0|0|0|0|0|0|0|0"); + sprintf(&tmp4[strlen(tmp4)], "0|0|0|40|0|0|0|40|0|0|0|0|40|0"); + sprintf(&tmp5[strlen(tmp5)], "0|0|0|100|0|0|0|100|0|0|0|0|100|0"); + sprintf(&tmp6[strlen(tmp6)], "0|0|0|1|0|0|0|1|0|0|0|0|1|0"); + sprintf(&tmp7[strlen(tmp7)], "0|0|0|0|0|0|0|0|0|0|0|0|0|0"); + sprintf(&tmp8[strlen(tmp8)], "0|0|0|0|0|0|0|0|0|0|0|0|0|0"); + sprintf(&tmp9[strlen(tmp9)], "0|0|0|1|0|0|0|1|0|0|0|0|1|0"); + int tran = 1; + + //if(!DB->QExecute("INSERT INTO items VALUES(%i,30,3,0,40,100,3,1,0,0,0,0)", mid)) + // return false; + //if(!DB->QExecute("INSERT INTO items VALUES(%i,1,8,0,40,100,7,1,0,0,0,0)", mid)) + // return false; + if(sex==0) + { + //if(!DB->QExecute("INSERT INTO items VALUES(%i,222,2,0,40,100,12,1,0,0,0,0)", mid)) + // return false; + sprintf(&tmp1[strlen(tmp1)], "0|0|0|30|0|0|0|1|0|0|0|0|222|0"); + if(!DB->QExecute("INSERT INTO charitems (owner,itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,trans_action) VALUES('%i','%s','%s','%s','%s','%s','%s','%s','%s','%s','%i')", + mid,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,tran)) + return false; + } + else + { + //if(!DB->QExecute("INSERT INTO items VALUES(%i,221,2,0,40,100,12,1,0,0,0,0)", mid)) + // return false; + sprintf(&tmp1[strlen(tmp1)], "0|0|0|30|0|0|0|1|0|0|0|0|221|0"); + if(!DB->QExecute("INSERT INTO charitems (owner,itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,trans_action) VALUES('%i','%s','%s','%s','%s','%s','%s','%s','%s','%s','%i')", + mid,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,tran)) + return false; + } + STARTPACKET( pak, 0x0713, 8 ); + SETWORD ( pak, 0x00, 0 ); + thisclient->SendPacket( &pak ); + + return true; +} + +// Connect WorldServer +bool CCharServer::pakWSReady ( CCharClient* thisclient, CPacket* P ) +{ + + if(thisclient->isLoggedIn) return false; + if(GETDWORD((*P),0) != Config.CharPass ) + { + Log( MSG_HACK, "WorldServer Tried to connect CharServer with wrong password "); + return true; + } + thisclient->userid = GETDWORD((*P), 4); //our channelid + thisclient->accesslevel = 0xffff; //this will identify thisclient as channel + + // add channel + CChanels* newchannel = new (nothrow) CChanels; + if(newchannel==NULL) + return false; + + newchannel->id = GETDWORD((*P), 4); + newchannel->ip = inet_ntoa( thisclient->clientinfo.sin_addr ); + newchannel->port = GETWORD((*P),8 ); + Log( MSG_INFO, "Channel #%i connected from ip %s", newchannel->id, newchannel->ip ); + + // Connect to charserver + newchannel->sock = socket( AF_INET, SOCK_STREAM, 0 ); + if (newchannel->sock == INVALID_SOCKET) + { + Log( MSG_WARNING, "Could not access worldserver IP: %s , Port: %i", newchannel->ip, newchannel->port ); + delete newchannel; + return false; + } + struct sockaddr_in ain; + ain.sin_family = AF_INET; + ain.sin_addr.s_addr = inet_addr(newchannel->ip); + ain.sin_port = htons( newchannel->port ); + memset(&(ain.sin_zero), '\0', 8); + if ( connect( newchannel->sock , (SOCKADDR*) &ain, sizeof(ain) ) == SOCKET_ERROR) + { + Log( MSG_WARNING, "Could not access worldserver IP: %s , Port: %i", newchannel->ip , newchannel->port ); + delete newchannel; + return false; + } + ChannelList.push_back( newchannel ); + + thisclient->isLoggedIn = true; + BEGINPACKET( pak, 0x500 ); + ADDDWORD ( pak, Config.WorldPass ); + //cryptPacket( (char*)&pak, NULL ); + //EncryptBuffer( CryptTable,(unsigned char*)&pak ); + Log( MSG_INFO, "Accessing worldserver IP: %s , Port: %i", newchannel->ip , newchannel->port ); + send( newchannel->sock, (char*)&pak, pak.Size, 0 ); + return true; + +} + +// Login Connected +bool CCharServer::pakLoginConnected( CCharClient* thisclient, CPacket* P ) +{ + if(thisclient->isLoggedIn) return false; + if(GETDWORD((*P),0) != Config.CharPass ) + { + Log( MSG_HACK, "LoginServer Tried to connect CharServer with wrong password "); + return true; + } + thisclient->isLoggedIn = true; + return true; +} + +// Return to char select +bool CCharServer::pakWSCharSelect ( CCharClient* thisclient, CPacket* P ) +{ + //if(!thisclient->isLoggedIn) return false; + Log( MSG_INFO, "World server requested char select" ); + + unsigned int userid = GETDWORD( (*P), 0 ); + CCharClient* otherclient = GetClientByUserID( userid ); + if(otherclient==NULL) + { + Log(MSG_WARNING, "Invalid userid: %i", userid ); + return true; + } + BEGINPACKET( pak, 0x71c ); + //ADDBYTE ( pak, 0x00 ); + otherclient->SendPacket( &pak ); + Log( MSG_INFO, "Client returning to char select" ); + return true; +} + +// ??? +bool CCharServer::pak7e5 ( CCharClient* thisclient, CPacket* P ) +{ + BEGINPACKET( pak, 0 ); + if(!thisclient->isLoggedIn) return false; + unsigned int action = GETBYTE((*P),0); + switch(action) + { + case 0x03: + RESETPACKET( pak, 0x7e5 ); + ADDBYTE ( pak, 0x01 ); + ADDWORD ( pak, 0x0000 ); + thisclient->SendPacket( &pak ); + break; + default: + Log( MSG_WARNING,"Unknown 7e5 action %i",action); + } + return true; +} + +// Create a chatroom? +bool CCharServer::pakChatrooms ( CCharClient* thisclient, CPacket* P ) +{ + if(!thisclient->isLoggedIn) return false; + return true; +} + +// delete/resurect character +bool CCharServer::pakDeleteChar( CCharClient* thisclient, CPacket* P ) +{ + if(!thisclient->isLoggedIn) return false; + short int action = GETBYTE((*P), 1 ); + unsigned long int DeleteTime = 0; + switch(action) + { + case 0x00://Resurect + { + DeleteTime = 0; + if(!DB->QExecute(" UPDATE characters SET deletetime=0 WHERE char_name='%s'", + (char*)&P->Buffer[2] )) + return false; + } + break; + case 0x01://Delete + { + DeleteTime = GetServerTime( ) + Config.DeleteTime; + if(!DB->QExecute(" UPDATE characters SET deletetime=%i WHERE char_name='%s'", + DeleteTime, (char*)&P->Buffer[2] )) + return false; + + } + break; + } + BEGINPACKET( pak, 0x714 ); + if(DeleteTime > 0 ) + { + ADDDWORD ( pak, Config.DeleteTime ); + } + else + { + ADDDWORD ( pak, 0x00000000 ); + } + ADDSTRING ( pak, (char*)&P->Buffer[2] ); + ADDBYTE ( pak, 0x00 ); + thisclient->SendPacket( &pak ); + return true; +} + +bool CCharServer::pakUpdateLevel( CCharClient* thisclient, CPacket* P ) +{ + if(!thisclient->isLoggedIn) return false; + WORD charid = GETWORD((*P),0); + WORD level = GETWORD((*P),2); + for(UINT i=0;icharid==charid) + { + player->level = level; + break; + } + } + return true; +} + +// Disconnect client from char/world server +bool CCharServer::pakLoginDSClient( CCharClient* thisclient, CPacket* P ) +{ + + if(!thisclient->isLoggedIn) return false; + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 1: + { + unsigned int userid = GETDWORD ((*P), 1); + CCharClient* otherclient = GetClientByUserID( userid ); + if(otherclient==NULL) + { + Log( MSG_WARNING, "Userid '%u' is not online", userid ); + return true; + } + otherclient->isLoggedIn = false; + otherclient->isActive = false; + BEGINPACKET( pak, 0x502 ); + ADDBYTE ( pak, 1 ); + ADDDWORD ( pak, userid ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( otherclient->channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + } + break; + } + return true; +} diff --git a/Src/Char Server/charserver.cpp b/Src/Char Server/charserver.cpp new file mode 100644 index 0000000..5bd5ee2 --- /dev/null +++ b/Src/Char Server/charserver.cpp @@ -0,0 +1,204 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +// Constructor +CCharServer::CCharServer( string fn ) +{ + LoadConfigurations( (char*)fn.c_str() ); + GServer = this; +} + +// Deconstructor +CCharServer::~CCharServer( ) +{ +} + +// Return a new clientsocket structure +CCharClient* CCharServer::CreateClientSocket( ) +{ + CCharClient* thisclient = new (nothrow) CCharClient( ); + return thisclient; +} + +//loadserver encryption +void CCharServer::LoadEncryption( ) +{ + GenerateCryptTables( CryptTable, 0x87654321 ); +} + +// When Server is ready +bool CCharServer::OnServerReady( ) +{ + DB->QExecute( "DELETE FROM channels WHERE id=%u", Config.ServerID); + if(!DB->QExecute("INSERT INTO channels (id,name,host,port,connected,maxconnections,owner) VALUES (%i,'%s','%s',%i,0,%i,%i)", + Config.ServerID, Config.ServerName, Config.CharIP, Config.CharPort, Config.MaxConnections, Config.ParentID)) + { + Log(MSG_WARNING, "Error accessing to database, the other server will not connect to CharServer" ); + } + + MYSQL_RES *result; + MYSQL_ROW row; + // Load all our clans + result = DB->QStore("SELECT id,logo,back,name,cp,grade,slogan,news FROM list_clan"); + if(result==NULL) return false; + while(row = mysql_fetch_row(result)) + { + CClans* thisclan = new CClans; + assert(thisclan); // maybe remove + thisclan->id = atoi(row[0]); + thisclan->logo = atoi(row[1]); + thisclan->back = atoi(row[2]); + strcpy(thisclan->name,row[3]); + thisclan->cp = atoi(row[4]); + thisclan->grade = atoi(row[5]); + strcpy(thisclan->slogan,row[6]); + strcpy(thisclan->news,row[7]); + ClanList.push_back( thisclan ); + } + DB->QFree( ); + // Load Clans + for(UINT i=0;iQStore("SELECT id,char_name,clan_rank FROM characters WHERE clanid=%i order by clan_rank",Clan->id); + if(result==NULL) return false; + while(row = mysql_fetch_row(result)) + { + CClanMembers* newmember = new CClanMembers; + assert(newmember); + newmember->id = atoi(row[0]); + strcpy(newmember->name,row[1]); + newmember->clan_rank = atoi(row[2]); + Clan->ClanMembers.push_back( newmember ); + } + DB->QFree( ); + } + Log( MSG_INFO, "Clans Information Loaded" ); + return true; +} + +// When a a client is disconnected +void CCharServer::OnClientDisconnect( CClientSocket* thisclient ) +{ + CCharClient* thisclientwc = (CCharClient*)thisclient; + DB->QExecute( "UPDATE accounts SET online=0 WHERE username='%s'", thisclientwc->username ); + if(!thisclientwc->logout) + { + // Send logout message to friends + for(UINT i=0;iFriendList.size( );i++) + { + CFriendList* Friend = thisclientwc->FriendList.at( i );; + CCharClient* otherclient = GetClientByID( Friend->id ); + if(otherclient!=NULL) + { + ChangeMessengerStatus ( thisclientwc, otherclient, 0x08); + } + } + //set logout messga to clan + CClans* thisclan = GetClanByID(thisclientwc->clanid); + if(thisclan==NULL) + return; + for(UINT i=0;iClanMembers.size( );i++) + { + CClanMembers* ClanMember = thisclan->ClanMembers.at( i ); + CCharClient* otherclient = GetClientByID( ClanMember->id ); + if(otherclient!=NULL) + ChangeClanStatus (thisclientwc, otherclient, 0xff); + } + } +} + +// Deletes an old clientsocket structure +void CCharServer::DeleteClientSocket( CClientSocket* thisclient ) +{ + if (thisclient->isserver) + Log( MSG_INFO, "Server disconnected" ); + else + Log( MSG_INFO, "User disconnected" ); + delete (CCharClient*)thisclient; + +} + +// Load Server configuration +void CCharServer::LoadConfigurations( const char* file ) +{ + //Database + Config.SQLServer.pcServer = ConfigGetString ( file, "mysql_host", "localhost" ); + Config.SQLServer.pcDatabase = ConfigGetString ( file, "mysql_database", "roseon_beta" ); + Config.SQLServer.pcUserName = ConfigGetString ( file, "mysql_user", "root" ); + Config.SQLServer.pcPassword = ConfigGetString ( file, "mysql_pass", "" ); + Config.SQLServer.pcPort = ConfigGetInt ( file, "mysql_port", 3306 ); + //Server + Config.ServerID = ConfigGetInt ( file, "serverid", 1 ); + Config.ServerType = 1; // Char always = 1 + Config.CharIP = ConfigGetString ( file, "serverip", "127.0.0.1" ); + Config.CharPort = ConfigGetInt ( file, "serverport", 29100 ); + Config.ParentID = ConfigGetInt ( file, "parentid", 0 ); + Config.ServerName = ConfigGetString ( file, "servername", "Server" ); + Config.CharsPort = ConfigGetInt ( file, "CharsPort", 29110 ); + //Char + Config.usethreads = ConfigGetInt ( file, "usethreads", 0 )==0?false:true; + Config.DeleteTime = ConfigGetInt ( file, "deletetime", 36000 ); + Config.MinimumAccessLevel = ConfigGetInt ( file, "accesslevel", 100 ); + //Password + Config.LoginPass = ConfigGetInt ( file, "loginpass", 123456 ); + Config.CharPass = ConfigGetInt ( file, "charpass", 123456 ); + Config.WorldPass = ConfigGetInt ( file, "worldpass", 123456 ); +} + +// Incoming packet +bool CCharServer::OnReceivePacket( CClientSocket* thisclient, CPacket *P ) +{ + Log(MSG_INFO,"Packet received at OnReceivePacket"); + switch( P->Command ) + { + + case 0x0500: return pakWSReady ( (CCharClient*)thisclient, P ); + case 0x0501: return pakLoginConnected ( (CCharClient*)thisclient, P ); + case 0x0502: return pakLoginDSClient ( (CCharClient*)thisclient, P ); + case 0x0505: return pakWSCharSelect ( (CCharClient*)thisclient, P ); + case 0x070b: return pakDoIdentify ( (CCharClient*)thisclient, P ); + case 0x0712: return pakGetCharacters ( (CCharClient*)thisclient, P ); + case 0x0713: return pakCreateChar ( (CCharClient*)thisclient, P ); + case 0x0714: return pakDeleteChar ( (CCharClient*)thisclient, P ); + case 0x0715: return pakRequestWorld ( (CCharClient*)thisclient, P ); + case 0x0787: return pakClanChat ( (CCharClient*)thisclient, P ); + case 0x07e0: return pakClanManager ( (CCharClient*)thisclient, P ); + case 0x07e1: return pakMessengerManager ( (CCharClient*)thisclient, P ); + case 0x07e2: return pakMessengerChat ( (CCharClient*)thisclient, P ); + case 0x07e3: return pakChatrooms ( (CCharClient*)thisclient, P ); + case 0x07e5: return pak7e5 ( (CCharClient*)thisclient, P ); + case 0x07e6: return pakUploadCM ( (CCharClient*)thisclient, P ); + case 0x07e7: return pakDownloadCM ( (CCharClient*)thisclient, P ); + case 0x07e8: return pakClanIconTime ( (CCharClient*)thisclient, P ); + case 0x079e: return pakUpdateLevel ( (CCharClient*)thisclient, P ); + default: + Log( MSG_WARNING, "(SID:%i) Received unknown packet. Command:%04x Size:%04x", thisclient->sock, P->Command, P->Size ); + break; + } + return true; +} + +bool CCharServer::Ping( ) +{ + return DB->Ping( ); +} diff --git a/Src/Char Server/charserver.h b/Src/Char Server/charserver.h new file mode 100644 index 0000000..40726c3 --- /dev/null +++ b/Src/Char Server/charserver.h @@ -0,0 +1,115 @@ +/* + Open Source Rose Online Team - http://osroseon.to.md/ + note: the Server is develop with erose source server + eich source +*/ +#ifndef __ROSE_SERVERS__ +#define __ROSE_SERVERS__ + +#include +#include "../common/sockets.h" +#include "datatypes.h" +#define ClearItem(i) { i.durability=0; i.itemnum=0; i.itemtype=0; i.lifespan=0; i.refine=0; } +using namespace std; + +// Clent class +class CCharClient : public CClientSocket +{ + public: + CCharClient ( ); // constructor + ~CCharClient ( ); // destructor + + // User information + UINT userid; + bool platinum; + char username[17]; + char password[33]; + UINT accesslevel; + UINT channel; + + // Character information + char charname[17]; + UINT clanid; + int clan_rank; + UINT level; + UINT job; + UINT charid; + + bool isLoggedIn; + bool returnedfromWS; + bool logout; + vector FriendList; +}; + +// Server class +class CCharServer : public CServerSocket +{ + public: + CCharServer ( string ); + ~CCharServer( ); + CCharClient* CreateClientSocket( ); + void DeleteClientSocket( CClientSocket* thisclient ); + void LoadEncryption( ); + void OnClientDisconnect( CClientSocket* thisclient ); + bool OnServerReady( ); + void LoadConfigurations( const char* ); + bool OnReceivePacket( CClientSocket* thisclient, CPacket* P ); + bool Ping( ); + + // Extra functions + CChanels* GetChannelByID( UINT id ); + CCharClient* GetClientByUserID( UINT id ); + CCharClient* GetClientByID( UINT id ); + CCharClient* GetClientByName( char *name ); + CCharClient* GetClientByUserName( char *username ); + CClans* GetClanByID( int id ); + unsigned long int GetServerTime( ); + + // Community functions + bool ChangeMessengerStatus (CCharClient* thisclient, CCharClient* otherclient, int status); + bool pakMessengerManager ( CCharClient* thisclient, CPacket* P ); + bool pakMessengerChat ( CCharClient* thisclient, CPacket* P ); + + // Clan Functions + bool SendClanInfo (CCharClient* thisclient); + bool ChangeClanStatus (CCharClient* thisclient, CCharClient* otherclient, int channel); + bool SendToClanMembers( int clanid,CPacket* pak ); + bool UpdateClanWindow( int clanid ); + bool pakClanMembers ( CCharClient* thisclient ); + bool pakClanManager ( CCharClient* thisclient, CPacket* P ); + bool pakClanChat ( CCharClient* thisclient, CPacket* P ); + bool ClanLogout ( CCharClient* thisclient ); + bool pakUploadCM ( CCharClient* thisclient, CPacket* P ); + bool pakDownloadCM( CCharClient* thisclient, CPacket* P ); + bool pakClanIconTime( CCharClient* thisclient, CPacket* P ); + + // General Packets + bool pakUpdateLevel( CCharClient* thisclient, CPacket* P ); + bool pakDoIdentify( CCharClient* thisclient, CPacket* P ); + bool pakGetCharacters( CCharClient* thisclient, CPacket* P ); + bool pakRequestWorld( CCharClient* thisclient, CPacket* P ); + bool pakCreateChar( CCharClient* thisclient, CPacket* P ); + bool pakDeleteChar( CCharClient* thisclient, CPacket* P ); + bool pakWSReady ( CCharClient* thisclient, CPacket* P ); + bool pakWSCharSelect ( CCharClient* thisclient, CPacket* P ); + bool pakLoginConnected ( CCharClient* thisclient, CPacket* P ); + bool pakLoginDSClient( CCharClient* thisclient, CPacket* P ); + bool pak7e5 ( CCharClient* thisclient, CPacket* P ); + bool pakChatrooms ( CCharClient* thisclient, CPacket* P ); + + // Variables + string filename; + vector ClanList; + vector ChannelList; + + //Login + SOCKET lsock; // Socket to Login + char* lct; // Encryption table for login server + CDatabase* DB; +}; +void StartSignal( ); +void StopSignal( ); +void HandleSignal( int num ); +extern class CCharServer* GServer; +#endif + + diff --git a/Src/Char Server/clan.cpp b/Src/Char Server/clan.cpp new file mode 100644 index 0000000..51a3217 --- /dev/null +++ b/Src/Char Server/clan.cpp @@ -0,0 +1,1076 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "CharServer.h" + +// Send Clan information +bool CCharServer::SendClanInfo (CCharClient* thisclient) +{ + if( thisclient->clanid > 0 ) + { + CClans *thisclan = GetClanByID(thisclient->clanid); + if(thisclan!=NULL) + { + BEGINPACKET( pak, 0x7e0); + ADDBYTE ( pak, 0x33);//0x33 you have invited to clan + ADDWORD ( pak, thisclan->id);// clan id + ADDBYTE ( pak, 0x00);// + ADDBYTE ( pak, 0x00); + ADDWORD ( pak, thisclan->back);//Clan Background + ADDWORD ( pak, thisclan->logo);//Clan logo + ADDBYTE ( pak, thisclan->grade);//Clan grade + ADDBYTE ( pak, thisclient->clan_rank);// Clan rank (0 = red rokie / 6 = master) + ADDDWORD ( pak, thisclan->cp);//Clan Points + ADDDWORD ( pak, 0x00000064); + ADDDWORD ( pak, 0x00000000); //Clan found + ADDDWORD ( pak, 0x00000000); + ADDBYTE ( pak, 0x01); + for(int i=34;i<156;i++) + ADDBYTE ( pak, 0x00); + ADDWORD ( pak, 0x0000); + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->name);//Clan Name + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->slogan);//Clan slogan + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->news);//Clan news + ADDBYTE ( pak, 0x00); + thisclient->SendPacket(&pak); + + //Put the player online in clan + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* ClanMember = thisclan->ClanMembers.at( i );; + CCharClient* otherclient = GetClientByID( ClanMember->id ); + if(otherclient!=NULL) + ChangeClanStatus (thisclient, otherclient, thisclient->channel);//send channel here + } + } + } + return true; +} + +// Change player status in clan ((channel) = online/ 0xff = offline) +bool CCharServer::ChangeClanStatus (CCharClient* thisclient, CCharClient* otherclient, int channel) +{ + if (thisclient->charid !=otherclient->charid) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x73 );//Change player status + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, channel );//CANAL + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, thisclient->level ); + ADDWORD ( pak, thisclient->job ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + otherclient->SendPacket(&pak); + } + return true; +} + +// Clan chat +bool CCharServer::pakClanChat ( CCharClient* thisclient, CPacket* P ) +{ + if(!thisclient->isLoggedIn) + return false; + char* tmp; + if (P->Buffer[0]=='/') + { + char* command = strtok( (char*)&(*P).Buffer[1] , " "); + if(command==NULL) + return true; + if(strcmp(command, "pak")==0)//read cpacket.txt and send it (for test packets) + { + int comando = 0; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + comando=atoi(tmp); + char buffer; + FILE *packet1 = fopen("cpacket.txt","r"); + BEGINPACKET(pak,comando); + if(packet1==NULL) + { + printf("Error opening packet1.txt for 716!\n"); + return true; + } + while((fscanf(packet1,"%c",&buffer))!=EOF) + ADDBYTE(pak,buffer); + fclose(packet1); + thisclient->SendPacket( &pak ); + return true; + } + } + char* text = new (nothrow) char[P->Size]; + if(text==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( text, &P->Buffer, P->Size ); + CClans* thisclan = (CClans*) GetClanByID ( thisclient->clanid ); + if(thisclan!=NULL) + { + BEGINPACKET( pak, 0x787 ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + ADDSTRING ( pak, text ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclan->id,&pak); + } + delete []text; + return true; +} + +// Clan manager for all the clan functions +bool CCharServer::pakClanManager ( CCharClient* thisclient, CPacket* P ) +{ + if(!thisclient->isLoggedIn) return false; + MYSQL_RES *result; + MYSQL_ROW row; + BYTE action = GETBYTE ((*P),0); + switch (action) + { + case 0x01: + break; + case 0x02://invite clan + { + char* nick = new (nothrow) char[P->Size]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size ); + if(strcmp(nick,thisclient->charname)==0) + return true; + CCharClient* otherclient = (CCharClient*) GetClientByName( nick ); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x0b );//invite action + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x0 ); + otherclient->SendPacket( &pak ); + } + delete []nick; + } + break; + case 0x03://Kick from clan + { + char* nick = new (nothrow) char[P->Size]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size ); + CClans* thisclan = GetClanByID(thisclient->clanid); + if(thisclan==NULL) + { + delete []nick; + return true; + } + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x81 );//xxx have kicket to yyyy + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00); + SendToClanMembers(thisclient->clanid,&pak); + for(UINT i=0;iClanMembers.size( );i++) + { + CClanMembers* ClanMember = thisclan->ClanMembers.at( i ); + if(strcmp(ClanMember->name,nick)==0) + { + thisclan->ClanMembers.erase( thisclan->ClanMembers.begin() + i ); + break; + } + } + + CCharClient* otherclient = (CCharClient*) GetClientByName( nick ); + if( otherclient!=NULL ) + { + otherclient->clanid = 0; + otherclient->clan_rank=1; + RESETPACKET( pak, 0x7e1 );//Update world clan information + ADDBYTE ( pak, 0xfb );//action (kick) + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( otherclient->channel ); + if(thischannel!=NULL) + { + send( thischannel->sock, (char*)&pak, pak.Size, 0 ); + } + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + pakClanMembers( otherclient ); + } + if(!DB->QExecute("UPDATE characters SET clanid=0 AND clan_rank=1 WHERE char_name='%s'",nick)) + { + delete []nick; + return false; + } + delete []nick; + } + break; + case 0x04://up rank + { + bool online = true; + char* nick = new (nothrow) char[P->Size-7]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size-7 ); + int clan_rank=0; + int level=0; + int job=0; + int channel=0xff; + if(strcmp(nick,thisclient->charname)==0) + { + delete []nick; + return true; + } + CCharClient* otherclient = GetClientByName ( nick ); + if(otherclient!=NULL) + { + clan_rank = otherclient->clan_rank; + if(otherclient->clan_rank<5) + { + otherclient->clan_rank++; + } + else + { + delete []nick; + return true; + } + CClans* thisclan = GetClanByID(otherclient->clanid); + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at(i); + if(thismember!=0) + { + if (thismember->id==otherclient->charid) + { + thismember->clan_rank=otherclient->clan_rank; + } + } + } + job = otherclient->job; + level = otherclient->level; + channel=otherclient->channel; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x83 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, otherclient->clan_rank ); + otherclient->SendPacket(&pak); + } + else + { + online=false; + result = DB->QStore("SELECT clan_rank,classid,level FROM characters WHERE char_name='%s'",nick); + if(result==NULL) + { + delete []nick; + DB->QFree( ); + return false; + } + if(mysql_num_rows(result)!=1) + { + Log(MSG_WARNING, "Invalid charname: %s" , nick ); + delete []nick; + DB->QFree( ); + return false; + } + row = mysql_fetch_row( result ); + clan_rank=atoi(row[0]); + job = atoi(row[1]); + level = atoi(row[2]); + DB->QFree( ); + } + if(clan_rank<5) + { + clan_rank++; + } + if(!DB->QExecute("UPDATE characters SET clan_rank=%i WHERE char_name='%s'",clan_rank,nick)) + { + delete []nick; + return false; + } + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x75 ); + ADDBYTE ( pak, clan_rank ); + ADDBYTE ( pak, channel );//channel + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, level ); // level + ADDWORD ( pak, job ); // job + ADDSTRING ( pak, nick ); // Nick + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + if (online==true) + { + RESETPACKET( pak, 0x7e1 );//update world information + ADDBYTE ( pak, 0xfc ); + ADDBYTE ( pak, clan_rank ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + } + delete []nick; + } + break; + case 0x05://down rank + { + bool online = true; + char* nick = new (nothrow) char[P->Size-7]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size-7 ); + int clan_rank=0; + int level=0; + int job=0; + int channel=0xff; + if(strcmp(nick,thisclient->charname)==0) + { + delete []nick; + return true; + } + CCharClient* otherclient = GetClientByName ( nick ); + if(otherclient!=NULL) + { + clan_rank = otherclient->clan_rank; + if(otherclient->clan_rank>1) + { + otherclient->clan_rank--; + } + else + { + delete []nick; + return true; + } + CClans* thisclan = GetClanByID(otherclient->clanid); + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at(i); + if(thismember!=0) + { + if (thismember->id==otherclient->charid) + { + thismember->clan_rank=otherclient->clan_rank; + } + } + } + job = otherclient->job; + level = otherclient->level; + channel=otherclient->channel; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x83 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, otherclient->clan_rank ); + otherclient->SendPacket(&pak); + } + else + { + online=false; + result = DB->QStore("SELECT clan_rank,classid,level FROM characters WHERE char_name='%s'",nick); + if(result==NULL) + { + delete []nick; + DB->QFree( ); + return false; + } + if(mysql_num_rows(result)!=1) + { + Log(MSG_WARNING, "Invalid charname: %s" , nick ); + delete []nick; + DB->QFree( ); + return false; + } + row = mysql_fetch_row( result ); + clan_rank=atoi(row[0]); + job = atoi(row[1]); + level = atoi(row[2]); + DB->QFree( ); + } + if(clan_rank>1) + { + clan_rank--; + } + if(!DB->QExecute("UPDATE characters SET clan_rank=%i WHERE char_name='%s'",clan_rank,nick)) + { + delete []nick; + return false; + } + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x75 ); + ADDBYTE ( pak, clan_rank ); + ADDBYTE ( pak, channel );//channel + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, level ); // level + ADDWORD ( pak, job ); // job + ADDSTRING ( pak, nick ); // Nick + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + if (online==true) + { + RESETPACKET( pak, 0x7e1 );//update world information + ADDBYTE ( pak, 0xfc ); + ADDBYTE ( pak, clan_rank ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + } + delete []nick; + } + break; + case 0x06://Change news + { + if(thisclient->clan_rank<5) + return true; + char* news = new (nothrow) char[P->Size-7]; + if(news==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( news, &P->Buffer[1], P->Size-7 ); + CClans* thisclan = GetClanByID(thisclient->clanid); + if(thisclan!=NULL) + { + strcpy(thisclan->news,news); + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x34 ); + ADDSTRING ( pak, thisclan->news ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclan->id,&pak); + if(!DB->QExecute("UPDATE list_clan SET news='%s' WHERE id=%i", thisclan->news, thisclan->id)) + return false; + } + delete []news; + } + break; + case 0x07://Leave Clan + { + if( thisclient->clan_rank == 6 ) + return true; + CClans* thisclan = GetClanByID( thisclient->clanid ); + if(thisclan==NULL) + return true; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE( pak, 0x82 ); + ADDSTRING( pak, thisclient->charname ); + ADDBYTE( pak, 0x00); + SendToClanMembers(thisclient->clanid,&pak); + + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* ClanMember = thisclan->ClanMembers.at( i ); + if(strcmp(ClanMember->name,thisclient->charname)==0) + { + thisclan->ClanMembers.erase( thisclan->ClanMembers.begin()+i ); + delete ClanMember; + break; + } + } + thisclient->clanid = 0; + thisclient->clan_rank=1; + if(!DB->QExecute("UPDATE characters SET clanid=0 , clan_rank=0 WHERE char_name='%s'",thisclient->charname)) + return false; + RESETPACKET( pak, 0x7e1 );//Update world clan information + ADDBYTE ( pak, 0xfb );//action (leave) + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( thisclient->channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + } + break; + case 0x08: + { + pakClanMembers ( thisclient ); + } + break; + case 0x09://Give Master + { + int clan_rank=0; + int level=0; + int job=0; + int channel=0xff; + char* nick = new (nothrow) char[P->Size-7]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size-7 ); + if(strcmp(nick,thisclient->charname)==0) + { + delete []nick; + return true; + } + //Convert to master + CCharClient* otherclient = GetClientByName ( nick ); + if(otherclient!=NULL) + { + clan_rank = 6; + otherclient->clan_rank = clan_rank; + job = otherclient->job; + level = otherclient->level; + channel = otherclient->channel; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x83 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, otherclient->clan_rank ); + otherclient->SendPacket(&pak); + } + else + { // 0 1 + result = DB->QStore("SELECT classid,level FROM characters where char_name='%s'",nick); + if(result==NULL) + { + delete []nick; + return false; + } + if(mysql_num_rows(result)!=1) + { + DB->QFree( ); + Log(MSG_WARNING, "Invalid charname: %s" , nick ); + delete []nick; + return false; + } + row = mysql_fetch_row( result ); + job = atoi(row[0]); + level = atoi(row[1]); + DB->QFree( ); + } + if(!DB->QExecute("UPDATE characters SET clan_rank=%i WHERE char_name='%s'",clan_rank,nick)) + { + delete []nick; + return false; + } + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x75 ); + ADDBYTE ( pak, clan_rank ); + ADDBYTE ( pak, channel );//canal + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, level ); //level? + ADDWORD ( pak, job ); // job? + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + + RESETPACKET( pak, 0x7e1 );//update world information + ADDBYTE ( pak, 0xfc ); + ADDBYTE ( pak, clan_rank ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + CChanels* thischannel = GetChannelByID( channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + + //Convert to commander + clan_rank = 4; + thisclient->clan_rank=clan_rank; + job = thisclient->job; + level = thisclient->level; + channel = thisclient->channel; + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x83 ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, thisclient->clan_rank ); + thisclient->SendPacket(&pak); + + if(!DB->QExecute("UPDATE characters SET clan_rank=%i WHERE char_name='%s'",clan_rank,thisclient->charname)) + { + delete []nick; + return false; + } + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x75 ); + ADDBYTE ( pak, clan_rank ); + ADDBYTE ( pak, channel );//canal + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, level ); //level? + ADDWORD ( pak, job ); // job? + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + + RESETPACKET( pak, 0x7e1 );//update world information + ADDBYTE ( pak, 0xfc ); + ADDBYTE ( pak, clan_rank ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + thischannel = GetChannelByID( channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + delete []nick; + } + break; + case 0x0a://disorganize clan + { + if(thisclient->clan_rank<6) + return true; + unsigned int clanid = thisclient->clanid; + CClans* thisclan = GetClanByID(thisclient->clanid); + if(thisclan==NULL) + return true; + if(thisclan->ClanMembers.size()>1)//Can't be deleted if there are members + return true; + if(!DB->QExecute("UPDATE characters SET clanid=0 AND clan_rank=1 WHERE clanid=%i",thisclient->clanid)) + return false; + if(!DB->QExecute("DELETE FROM list_clan WHERE id=%i",thisclient->clanid)) + return false; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x51 ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid, &pak); + thisclan->ClanMembers.clear( ); + //delete from clan list + for(UINT i=0;icharid ); + CChanels* thischannel = GetChannelByID( thisclient->channel ); + if(thischannel!=NULL){ + send( thischannel->sock, (char*)&pak, pak.Size, 0 );} + else { Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel);} + thisclient->clanid = 0; + thisclient->clan_rank = 1; + //send( wct + } + break; + case 0x0c://invitation accepted + { + char* nick = new (nothrow) char[P->Size-7]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size-7 ); + CCharClient* otherclient = (CCharClient*) GetClientByName(nick ); + if(otherclient!=NULL) + { + thisclient->clanid = otherclient->clanid; + thisclient->clan_rank=1; + if(!DB->QExecute("UPDATE characters SET clanid=%i , clan_rank=1 WHERE char_name='%s'",thisclient->clanid,thisclient->charname)) + { + delete []nick; + return false; + } + CClans* thisclan = GetClanByID(thisclient->clanid); + if(thisclan==NULL) + { + delete []nick; + return true; + } + CClanMembers* newmember = new (nothrow) CClanMembers; + if(newmember==NULL) + { + Log( MSG_WARNING, "Error allocing memory" ); + delete []nick; + return false; + } + newmember->id = thisclient->charid; + strcpy(newmember->name,thisclient->charname); + newmember->clan_rank = 1; + thisclan->ClanMembers.push_back( newmember ); + } + else + { + delete []nick; + return true; + } + SendClanInfo(thisclient); + pakClanMembers(thisclient); + BEGINPACKET( pak, 0x7e0 );//xxx have invited to xxx + ADDBYTE( pak, 0x61 ); + ADDSTRING( pak, thisclient->charname ); + ADDBYTE( pak, 0x00); + ADDSTRING( pak, otherclient->charname ); + ADDBYTE( pak, 0x00); + SendToClanMembers(thisclient->clanid,&pak); + + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x84 ); + ADDWORD ( pak, thisclient->level ); + ADDWORD ( pak, thisclient->job ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + + RESETPACKET( pak, 0x7e1 );//update clan info in world + ADDBYTE( pak, 0xfa ); + ADDWORD( pak, thisclient->charid ); + ADDWORD( pak, thisclient->clanid ); + CChanels* thischannel = GetChannelByID( thisclient->channel ); + if(thischannel!=NULL) + { + send( thischannel->sock, (char*)&pak, pak.Size, 0 ); + } + else + { + Log(MSG_ERROR, "Check serverid in worldserver.conf it should be the same as ID in channels in the mysql database %i",thisclient->channel); + } + CChanels* otherchannel = GetChannelByID( otherclient->channel ); + if(otherchannel!=NULL && thischannel!=otherchannel) + { + send( thischannel->sock, (char*)&pak, pak.Size, 0 ); + } + delete []nick; + } + break; + case 0x0d://invitation no accepted + { + char* nick = new (nothrow) char[P->Size-7]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size-7 ); + CCharClient* otherclient = (CCharClient*) GetClientByName(nick ); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x0d ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + otherclient->SendPacket(&pak); + } + delete []nick; + } + break; + case 0x0f:// Update Clan window with Level and Job + { + CClans* thisclan = (CClans*) GetClanByID( thisclient->clanid ); + if(thisclan!=NULL) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x84 ); + ADDWORD ( pak, GETWORD ((*P),1) ); + ADDWORD ( pak, GETWORD ((*P),3) ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + } + } + break; + case 0x5C://message from worldserver to load the new clan information + { + UINT clanid = GETWORD((*P),1); + UINT clientrank = GETWORD((*P),3); + + CClans* thisclan = (CClans*) GetClanByID( clanid ); + if(thisclan==NULL) + return true; + thisclan->cp=GETDWORD ((*P),5); + + BEGINPACKET( pak, 0x7e0); + ADDBYTE ( pak, 0x71); + ADDWORD ( pak, clanid); + ADDWORD ( pak, 0x0000);// + ADDWORD ( pak, thisclan->back );//clan background + ADDWORD ( pak, thisclan->logo );//clanlogo + ADDBYTE ( pak, thisclan->grade);//clan grade + ADDBYTE ( pak, 0xCC); + ADDDWORD ( pak, thisclan->cp ); + ADDDWORD ( pak, 0x00000064 ); + ADDDWORD ( pak, 0x00000000 ); + ADDDWORD ( pak, 0x00000000 ); + ADDWORD ( pak, 0xCCCC ); + ADDWORD ( pak, 0xCCCC ); + ADDWORD ( pak, 0xCCCC ); + for(int i=0;i<119;i++) + ADDBYTE ( pak, 0x00); + SendToClanMembers(clanid,&pak); + UpdateClanWindow(clanid); + } + break; + case 0xfa://message from worldserver to load the new clan information + { + int id = GETWORD((*P),1); // clanid + int charid = GETWORD((*P),3); // charid + int clientid = GETWORD((*P),5); // clientid + CCharClient* otherclient = (CCharClient*) GetClientByID ( charid ); + if(otherclient==NULL) + return true; + // Load all our clan information + // 0 1 2 3 4 5 6 7 + result = DB->QStore("SELECT id,logo,back,name,cp,grade,slogan,news FROM list_clan where id=%i",id); + if(result == NULL) return false; + if(mysql_num_rows(result)!=1) + { + DB->QFree( ); + Log(MSG_WARNING, "Invalid clan: %i" , id ); + return false; + } + row = mysql_fetch_row(result); + //clan info + CClans* newclan = new CClans; + assert(newclan); + newclan->id = atoi(row[0]); + newclan->logo = atoi(row[1]); + newclan->back = atoi(row[2]); + strncpy_s(newclan->name,row[3],16); + newclan->cp = atoi(row[4]); + newclan->grade = atoi(row[5]); + strncpy_s(newclan->slogan,row[6],29); + strcpy(newclan->news,row[7]); + ClanList.push_back( newclan ); + DB->QFree( ); + //member info + CClanMembers* newmember = new CClanMembers; + newmember->id = otherclient->charid; + strcpy(newmember->name,otherclient->charname); + newmember->clan_rank = 6; + newclan->ClanMembers.push_back( newmember ); + + //DB->QFree( ); + otherclient->clanid = id; + otherclient->clan_rank = 6; + //Send new clan information + BEGINPACKET( pak, 0x7e0); + ADDBYTE ( pak, 0x30); + ADDWORD ( pak, clientid); + ADDWORD ( pak, 0x0100);// + ADDWORD ( pak, newclan->back );//clan background + ADDWORD ( pak, newclan->logo );//clanlogo + ADDBYTE ( pak, newclan->grade);//clan grade + ADDBYTE ( pak, otherclient->clan_rank);// clan rank + ADDDWORD ( pak, newclan->cp ); + ADDDWORD ( pak, 0x00000000 ); // Unk + ADDDWORD ( pak, 0x00000000 ); // Clan Funds + ADDDWORD ( pak, 0x00000000 ); // Unk + ADDWORD ( pak, 0x0000 ); // Unk + ADDDWORD ( pak, 0x00000000 ); // Contribution + for(int i=0;i<120;i++) + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, newclan->name);//clan name + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, newclan->slogan);//clan slogan + ADDBYTE ( pak, 0x00); + + otherclient->SendPacket(&pak); + } + break; + case 0xfb: + { + UINT clanid = GETWORD((*P),1); + UINT grade = GETWORD((*P),3); + CClans* thisclan = (CClans*) GetClanByID( clanid ); + if(thisclan!=NULL) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x84 ); + ADDWORD ( pak, GETWORD ((*P),1) ); + ADDWORD ( pak, GETWORD ((*P),3) ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + SendToClanMembers(thisclient->clanid,&pak); + } + } + default: + Log( MSG_INFO, "Clan action unknow %i ", action); + break; + } + return true; +} + +// Send clan members +bool CCharServer::pakClanMembers ( CCharClient* thisclient ) +{ + CClans* thisclan = (CClans*) GetClanByID( thisclient->clanid ); + if(thisclan!=NULL) + { + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x72 );//Send clan members + for(UINT i=0;iClanMembers.size( );i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at( i ); + if(thismember==NULL) + continue; + CCharClient* otherclient = (CCharClient*) GetClientByID (thismember->id); + if(otherclient!=NULL) + { + ADDBYTE ( pak, thismember->clan_rank ); //clan rank + ADDBYTE ( pak, otherclient->channel ); //channel (0x01 = channel 1) + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, otherclient->level );// Level + ADDWORD ( pak, otherclient->job );// Job + } + else + { + ADDBYTE ( pak, thismember->clan_rank); //clan rank + ADDBYTE ( pak, 0xff ); //channel (0xff = offline) + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 );// Level + ADDWORD ( pak, 0x0000 );// Job + } + ADDSTRING ( pak, thismember->name ); + ADDBYTE ( pak, 0x00 ); + } + thisclient->SendPacketCpy( &pak ); + } + return true; +} + +// Logout from clan +bool CCharServer::ClanLogout ( CCharClient* thisclient ) +{ + CClans* thisclan = (CClans*) GetClanByID(thisclient->clanid); + if(thisclan!=NULL) + { + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at( i ); + CCharClient* otherclient = (CCharClient*) GetClientByID ( thismember->id ); + if(otherclient!=NULL) + { + ChangeClanStatus (thisclient, otherclient, 0xff);//send channel here + } + } + } + return true; +} + +// upload clanmark +bool CCharServer::pakUploadCM ( CCharClient* thisclient, CPacket* P ) +{ + CClans* thisclan = GetClanByID( thisclient->clanid ); + if(thisclan == NULL) return true; + FILE* fh = fopen( "clanmark/clanmark.cnt", "r+b" ); + if(fh == NULL) + { + Log( MSG_WARNING, "Error opening clanmark counter file" ); + return true; + } + rewind(fh); + unsigned int cmid = 0;// this will be our clanmark id + unsigned int tcmid = 0;// this will be to update the id + fread( &cmid, 1, 4, fh ); + tcmid = cmid+1; + rewind(fh); + fwrite( &tcmid, 1, 4, fh ); + fclose(fh); + thisclan->logo = GETWORD((*P), 0 );//cmid; + thisclan->back = 0; + char filename[30]; + sprintf( filename, "clanmark/%u.cm", thisclan->logo ); + fh = fopen( filename, "w+b" ); + if(fh == NULL) + { + Log( MSG_WARNING, "Error creating clanmark file" ); + return true; + } + rewind(fh); + for(unsigned int i=0;iSize-6;i++) + fwrite( &P->Buffer[i], 1, 1, fh ); + fclose(fh); + DB->QExecute("UPDATE list_clan SET logo=%i,back=0 WHERE id=%i",thisclan->logo, thisclient->clanid ); + BEGINPACKET( pak, 0x7e1 );//update clan info in world + ADDBYTE ( pak, 0xff ); + ADDWORD ( pak, thisclient->clanid ); + ADDDWORD ( pak, cmid ); + for(int i=0;isock, (char*)&pak, pak.Size, 0 ); + return true; +} + +bool CCharServer::pakDownloadCM ( CCharClient* thisclient, CPacket* P ) +{ + unsigned int clanid = GETDWORD((*P), 0 ); + MYSQL_RES* result = DB->QStore("SELECT logo FROM list_clan WHERE id=%i",clanid ); + if(result == NULL) + return true; + if(mysql_num_rows(result)!=1) + return true; + MYSQL_ROW row = mysql_fetch_row(result); + unsigned int cmid = atoi(row[0]); + DB->QFree( ); + char filename[30]; + sprintf( filename, "clanmark/%u.cm", cmid ); + FILE* fh = fopen( filename, "rb" ); + if(fh == NULL) + { + Log( MSG_WARNING, "Invalid clanmark ID %i at filepath $s", cmid, filename ); + return true; + } + CClans* thisclan = GetClanByID(thisclient->clanid); + BEGINPACKET( pak, 0x7e7 ); + ADDDWORD ( pak, clanid ); + while(!feof(fh)) + { + unsigned char charvalue = '\0'; + fread( &charvalue, 1 , 1, fh ); + ADDBYTE( pak, charvalue ); + } + ADDWORD( pak,0); + thisclient->SendPacket( &pak ); + fclose(fh); + return true; +} +bool CCharServer::pakClanIconTime ( CCharClient* thisclient, CPacket* P ) //FORMAT UNKNOWN +{ + BEGINPACKET( pak, 0x7e8 ); + ADDWORD ( pak, 0x07d8); //clanmark change year + ADDBYTE ( pak, 0x01 ); //month + ADDBYTE ( pak, 0x0e ); //day + ADDBYTE ( pak, 0x12 ); //hour + ADDBYTE ( pak, 0x37 ); //minutes + ADDBYTE ( pak, 0x00 ); // ?? seconds? + thisclient->SendPacket( &pak ); + return true; +} diff --git a/Src/Char Server/community.cpp b/Src/Char Server/community.cpp new file mode 100644 index 0000000..5e07a10 --- /dev/null +++ b/Src/Char Server/community.cpp @@ -0,0 +1,215 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +// Change messenger status 07 - online | 08 - offline +bool CCharServer::ChangeMessengerStatus (CCharClient* thisclient, CCharClient* otherclient, int status) +{ + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x08 ); + ADDWORD ( pak, thisclient->charid ); + ADDWORD ( pak, 0x0000 ); + ADDBYTE ( pak, status ); + otherclient->SendPacket(&pak); + return true; +} + +// Messenger actions (add/remove/invite) +bool CCharServer::pakMessengerManager ( CCharClient* thisclient, CPacket* P ) +{ + BYTE action = GETBYTE((*P),0); + switch (action) + { + case 0x01://wanna be my friend? + { + char* nick = new (nothrow) char[P->Size]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory: pakMessengerManager 1" ); + return false; + } + memcpy( nick, &P->Buffer[1], P->Size ); + CCharClient* otherclient = (CCharClient*) GetClientByName (nick); + if(otherclient!=NULL) + {//Send friend invitation (check this one) + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x01 ); + ADDWORD ( pak, 0x0000 ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00 ); + otherclient->SendPacket(&pak); + } + else + {//This charname doesnt exist + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x04 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + thisclient->SendPacket(&pak); + } + delete []nick; + } + break; + case 0x02://yes i want + { + char* nick = new (nothrow) char[P->Size]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory: pakMessengerManager 2" ); + return false; + } + memcpy( nick, &P->Buffer[3], P->Size ); + CCharClient* otherclient = (CCharClient*) GetClientByName (nick); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x02 ); + ADDWORD ( pak, thisclient->charid ); + ADDBYTE ( pak, 0x00 ); + ADDWORD ( pak, 0x0000 ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00); + otherclient->SendPacket(&pak); + //Add friend to my friend list(sql) + if(!DB->QExecute("INSERT INTO list_friend (id,idfriend,namefriend) VALUES (%i,%i,'%s')", + otherclient->charid,thisclient->charid,thisclient->charname)) + return false; + CFriendList * newfriend1 = new (nothrow) CFriendList; + if(newfriend1==NULL) + return false; + newfriend1->id = otherclient->charid; //friendid + strcpy(newfriend1->name, otherclient->charname); //friend name + thisclient->FriendList.push_back( newfriend1 ); + RESETPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x02 ); + ADDWORD ( pak, otherclient->charid ); + ADDBYTE ( pak, 0x00 ); + ADDWORD ( pak, 0x0000 ); + ADDSTRING ( pak, otherclient->charname ); + ADDBYTE ( pak, 0x00); + thisclient->SendPacket(&pak); + //Add me to his friend list (sql) + if(!DB->QExecute("INSERT INTO list_friend (id,idfriend,namefriend) VALUES (%i,%i,'%s')", + thisclient->charid,otherclient->charid,otherclient->charname)) + return false; + CFriendList * newfriend2 = new (nothrow) CFriendList; + if(newfriend2==NULL) + return false; + newfriend2->id = thisclient->charid; //friendid + strcpy(newfriend2->name, thisclient->charname); //friend name + otherclient->FriendList.push_back( newfriend2 ); + } + else//not founded + { + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x04 ); + ADDWORD ( pak, 0x0000 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + thisclient->SendPacket(&pak); + } + delete []nick; + } + break; + case 0x03://no, i dont want + { + char* nick = new (nothrow) char[P->Size]; + if(nick==NULL) + { + Log(MSG_ERROR, "Error allocing memory: pakMessengerManager 3" ); + return false; + } + memcpy( nick, &P->Buffer[3], P->Size ); + CCharClient* otherclient = (CCharClient*) GetClientByName (nick); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x03 ); + ADDSTRING ( pak, thisclient->charname ); + ADDBYTE ( pak, 0x00); + otherclient->SendPacket(&pak); + } + else + { + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0x04 ); + ADDWORD ( pak, 0x0000 ); + ADDSTRING ( pak, nick ); + ADDBYTE ( pak, 0x00 ); + thisclient->SendPacket(&pak); + } + } + break; + case 0x05://freakin later + { + WORD id = GETWORD ((*P),1); + if(!DB->QExecute("DELETE FROM list_friend WHERE id=%i and idfriend=%i", + thisclient->charid,id)) + return false; + CCharClient* otherclient = (CCharClient*) GetClientByID(id); + if(otherclient!=NULL) + { + ChangeMessengerStatus ( thisclient, otherclient, 0x08); + } + } + break; + case 0xfa://messenger logout + { + WORD id = GETWORD ((*P),1); + CCharClient* ctherclient = (CCharClient*) GetClientByID(id); + if(ctherclient==NULL) + return true; + ctherclient->logout = true; + for(UINT i=0;iFriendList.size();i++) + { + CFriendList* thisfriend = ctherclient->FriendList.at( i ); + CCharClient* otherclient = (CCharClient*) GetClientByID( thisfriend->id ); + if(otherclient!=NULL) + { + ChangeMessengerStatus ( ctherclient, otherclient, 0x08); + } + } + //Logout in clan + ClanLogout ( ctherclient ); + } + break; + default: + Log( MSG_INFO, "Friend action unknown: %i", action ); + break; + } + return true; +} + +// Messenger Chat +bool CCharServer::pakMessengerChat ( CCharClient* thisclient, CPacket* P ) +{ + WORD id = GETWORD((*P),0); + CCharClient* otherclient = (CCharClient*) GetClientByID(id); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x7e2 ); + ADDWORD ( pak, thisclient->charid ); + for (int i=2;i<(P->Size-6);i++) + ADDBYTE ( pak, GETBYTE((*P),i )); + otherclient->SendPacket(&pak); + } + return true; +} diff --git a/Src/Char Server/datatypes.h b/Src/Char Server/datatypes.h new file mode 100644 index 0000000..61ed83e --- /dev/null +++ b/Src/Char Server/datatypes.h @@ -0,0 +1,68 @@ +/* + Open Source Rose Online Team - http://osroseon.to.md/ + note: the Server is develop with erose source server + eich source +*/ +#ifndef __ROSE_DATATYPES__ +#define __ROSE_DATATYPES__ +#include "../common/sockets.h" +// List of clan members +struct CClanMembers +{ + UINT id; + char name[50]; + int clan_rank; +}; + +// An item (for figuring out what a user is wearing) +struct CItem { + UINT owner; + UINT itemnum; + UINT itemtype; + UINT refine; + UINT durability; + UINT lifespan; +}; + +// A single client character +struct CCharacter { + char char_name[17]; + UINT level; + UINT face; + UINT hairStyle; + int sex; + UINT classid; + UINT id; + unsigned long int DeleteTime; +}; + +// List of friends +struct CFriendList +{ + int id; + char name[17]; +}; + +// List of clan +struct CClans +{ + int id; + int logo; + int back; + int grade; + int cp; + char name[17]; + char slogan[30]; + char news[260]; + vector ClanMembers; +}; + +// Channel list +struct CChanels +{ + UINT id; + char* ip; + UINT port; + SOCKET sock; +}; +#endif + diff --git a/Src/Char Server/extrafunctions.cpp b/Src/Char Server/extrafunctions.cpp new file mode 100644 index 0000000..a406d7b --- /dev/null +++ b/Src/Char Server/extrafunctions.cpp @@ -0,0 +1,184 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +// Search client by userid +CCharClient* CCharServer::GetClientByUserID( UINT userid ) +{ + for(UINT i=0;iuserid==userid) + { + return client; + } + } + return NULL; +} + +// Search the client by char id +CCharClient* CCharServer::GetClientByID( UINT charid ) +{ + for(UINT i=0;icharid==charid) + { + return client; + } + } + return NULL; +} + +// Search the client by char name +CCharClient* CCharServer::GetClientByName( char *name ) +{ + try + { + for(UINT i=0;icharname, name)==0) + { + return client; + } + } + } + catch(...) + { + return NULL; + } + return NULL; +} + +// Search the client by char name +CCharClient* CCharServer::GetClientByUserName( char *username ) +{ + for(UINT i=0;iusername, username)==0) + { + return client; + } + } + return NULL; +} + +// Search the clan by clan id +CClans* CCharServer::GetClanByID( int id ) +{ + for(UINT i=0;iid==id) + return thisclan; + } + return NULL; +} + +// Send packet to all the clan members +bool CCharServer::SendToClanMembers( int clanid,CPacket* pak ) +{ + CClans* thisclan = GetClanByID(clanid); + if(thisclan==0) + return false; + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at(i); + CCharClient* otherclient = (CCharClient*) GetClientByID (thismember->id); + if(otherclient!=0) + { + otherclient->SendPacketCpy(pak); + } + } + return true; +} + +// Update clan window all members 07 +bool CCharServer::UpdateClanWindow( int clanid ) +{ + CClans* thisclan = GetClanByID(clanid); + if(thisclan==0) + return false; + for(UINT i=0;iClanMembers.size();i++) + { + CClanMembers* thismember = thisclan->ClanMembers.at(i); + CCharClient* otherclient = (CCharClient*) GetClientByID (thismember->id); + if(otherclient!=0) + { + BEGINPACKET( pak, 0x7e0); + ADDBYTE ( pak, 0x33); + ADDWORD ( pak, clanid); + ADDWORD ( pak, 0x0000); + ADDWORD ( pak, thisclan->back );//clan background + ADDWORD ( pak, thisclan->logo );//clanlogo + ADDBYTE ( pak, thisclan->grade);//clan grade + ADDBYTE ( pak, otherclient->clan_rank); + ADDDWORD ( pak, thisclan->cp ); + ADDDWORD ( pak, 0x00000064 ); + ADDDWORD ( pak, 0x00000000 ); + ADDDWORD ( pak, 0x00000000 ); + ADDWORD ( pak, 0x0003 );//????????? + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + for(int i=0;i<119;i++) + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->name);//clan name + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->slogan);//Clan slogan + ADDBYTE ( pak, 0x00); + ADDSTRING ( pak, thisclan->news);//Clan news + ADDBYTE ( pak, 0x00); + otherclient->SendPacket(&pak); + } + } + return true; +} + +// Get channel by id +CChanels* CCharServer::GetChannelByID( unsigned int id ) +{ + for(unsigned int i=0;iid==id) + return thischannel; + } + return NULL; +} + +// Returns the current time/date +unsigned long int CCharServer::GetServerTime( ) +{ + // Get time/date and write it to the beginning of the row + time_t rawtime; // For time + struct tm* timeinfo; // " + time ( &rawtime ); + timeinfo = localtime( &rawtime ); + unsigned long int uCurTime = 0; + uCurTime += (timeinfo->tm_sec * 1 ); + uCurTime += (timeinfo->tm_min * 60 ); + uCurTime += (timeinfo->tm_hour * 3600 ); + uCurTime += (timeinfo->tm_yday * 86400 ); + uCurTime += ((timeinfo->tm_year-2000) * 86400*366 ); + return uCurTime; +} diff --git a/Src/Char Server/main.cpp b/Src/Char Server/main.cpp new file mode 100644 index 0000000..18169fe --- /dev/null +++ b/Src/Char Server/main.cpp @@ -0,0 +1,67 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" + +unsigned char LOG_THISSERVER = 0; +class CCharServer* GServer; + +// Main function +int main(int argc, char *argv[]) +{ + LOG_THISSERVER = LOG_CHARACTER_SERVER; + InitWinSocket( ); + string fileconf = "charserver.conf"; + if(argc>1) + { + fileconf = argv[1]; + } + CCharServer *server = new (nothrow) CCharServer( fileconf ); + if(server==NULL) + return -1; + MYSQL mysql; + server->DB = new CDatabase( server->Config.SQLServer.pcServer, + server->Config.SQLServer.pcUserName, + server->Config.SQLServer.pcPassword, + server->Config.SQLServer.pcDatabase, + server->Config.SQLServer.pcPort, &mysql ); + server->port = server->Config.CharPort; + if(server->Config.usethreads) + Log( MSG_INFO, "Using Threads" ); + if(server->DB->Connect( )==0) Log( MSG_INFO, "Connected to MySQL server" ); + else + { + delete server; + #ifdef _WIN32 + system("pause"); + #endif + return -1; + } + server->LoadEncryption (); + // Start server + server->StartServer(); + // Close server + server->DB->Disconnect( ); + delete server; + CloseWinSocket( ); + return EXIT_SUCCESS; +} + +// ----------------------------------------------------------------------------------------- diff --git a/Src/Char Server/signal.cpp b/Src/Char Server/signal.cpp new file mode 100644 index 0000000..cfb1532 --- /dev/null +++ b/Src/Char Server/signal.cpp @@ -0,0 +1,109 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "charserver.h" +/* +/ from signal.h + * NOTE: SIGINT is produced when the user presses Ctrl-C. + * SIGILL has not been tested. + * SIGFPE doesn't seem to work? + * SIGSEGV does not catch writing to a NULL pointer (that shuts down + * your app; can you say "segmentation violation core dump"?). + * SIGTERM comes from what kind of termination request exactly? + * SIGBREAK is indeed produced by pressing Ctrl-Break. + * SIGABRT is produced by calling abort. +*/ + +void StartSignal( ) +{ + signal( SIGINT, HandleSignal ); + signal( SIGILL, HandleSignal ); + signal( SIGFPE, HandleSignal ); + signal( SIGSEGV, HandleSignal ); + signal( SIGTERM, HandleSignal ); + signal( SIGBREAK, HandleSignal ); + signal( SIGABRT, HandleSignal ); +} + +void StopSignal( ) +{ + signal( SIGINT, 0 ); + signal( SIGILL, 0 ); + signal( SIGFPE, 0 ); + signal( SIGSEGV, 0 ); + signal( SIGTERM, 0 ); + signal( SIGBREAK, 0 ); + signal( SIGABRT, 0 ); +} + +void HandleSignal( int num ) +{ + switch(num) + { + case SIGINT:/* Interactive attention */ + Log( MSG_ERROR, "Signal received: SIGINT" ); + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + case SIGILL:/* Illegal instruction */ + Log( MSG_ERROR, "Signal received: SIGILL, Server will be closed, Trying to save..." ); + GServer->isActive = false; + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + case SIGFPE:/* Floating point error */ + Log( MSG_ERROR, "Signal received: SIGFPE, Server will be closed, Trying to save..." ); + GServer->isActive = false; + raise(num); + break; + case SIGSEGV:/* Segmentation violation */ + Log( MSG_ERROR, "Signal received: SIGSEGV, Server will be closed, Trying to save..." ); + GServer->isActive = false; + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + + case SIGTERM:/* Termination request */ + Log( MSG_INFO, "Signal received: SIGTERM, Server will be closed" ); + GServer->isActive = false; + break; + #ifdef _WIN32 + case SIGBREAK:/* Control-break */ + Log( MSG_INFO, "Signal received: SIGBREAK, Server will be closed" ); + GServer->isActive = false; + break; + #endif + default: + cout << "Unknown Signal Number: " << num << endl; + raise(num); + } +} diff --git a/Src/Common/SocketBase.cpp b/Src/Common/SocketBase.cpp new file mode 100644 index 0000000..177c712 --- /dev/null +++ b/Src/Common/SocketBase.cpp @@ -0,0 +1,66 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "sockets.h" + +// Constructor +CBaseSocket::CBaseSocket( ) +{ + sock = INVALID_SOCKET; + isActive = false; +} + +// Destructor +CBaseSocket::~CBaseSocket( ) +{ +} + +// Close all open sockets +void CBaseSocket::CloseSocket( void ) +{ + // Leave function, if socket isn't active + if ( !isActive ) return; + // Close socket + close( sock ); + sock = INVALID_SOCKET; + isActive = false; +} + +// Init winsocket, if we are using Windows +bool InitWinSocket ( void ) +{ +#ifdef _WIN32 + WSADATA wsa; + return (WSAStartup(MAKEWORD(2,0),&wsa)==0); +#else + return true; +#endif +} + +// Close winsocket, if we are using Windows +void CloseWinSocket( void ) +{ +#ifdef _WIN32 + WSACleanup( ); +#endif +} + + + diff --git a/Src/Common/SocketClient.cpp b/Src/Common/SocketClient.cpp new file mode 100644 index 0000000..19d823e --- /dev/null +++ b/Src/Common/SocketClient.cpp @@ -0,0 +1,216 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "sockets.h" +#include "seh.hpp" +#include "config.h" + +// Constructor +CClientSocket::CClientSocket( ) +{ + PacketSize = 6; // Starting size + PacketOffset = 0; // No bytes read, yet. +} + +// Destructor +CClientSocket::~CClientSocket( ) +{ + +} + +// Receive this client's socket +bool CClientSocket::ReceiveData( ) +{ + int ReceivedBytes; + short BytesToRead; + + // Calculate bytes to read to get the full packet + BytesToRead = PacketSize - PacketOffset; + // This should never happen, but it is integrated: + if ( BytesToRead > 0x400 - PacketOffset ) return false; + if ( BytesToRead == 0 ) return false; + + // Receive data from client + ReceivedBytes = recv( sock, (char*)&Buffer[ PacketOffset ], BytesToRead, 0 ); + if ( ReceivedBytes <= 0 ) return false; + + // Update pointer + PacketOffset += ReceivedBytes; + + // If the packet is not complete, leave the function + if ( ReceivedBytes != BytesToRead ) return true; + + if ( PacketSize == 6 ) + { + // We received the headerblock +#ifndef USE124 + PacketSize = DecryptBufferHeader( &CryptStatus, CryptTable, Buffer ); +#else + CPacket* header = (CPacket*)&Buffer; + PacketSize = header->Size; +#endif + + // Did we receive an incorrect buffer? + if ( PacketSize < 6 ) + { + Log(MSG_ERROR, "(SID:%i) Client sent incorrect blockheader.", sock ); + return false; + } + + // Is the packet larger than just the header, then continue receiving + if ( PacketSize > 6 ) return true; + } + +#ifndef USE124 + // We received the whole packet - Now we try to decrypt it + if ( !DecryptBufferData( CryptTable, Buffer ) ) + { + Log(MSG_ERROR, "(SID:%i) Client sent illegal block.", sock ); + return false; + } +#else + cryptPacket( (char*)Buffer, this->ct); +#endif + CPacket* pak = (CPacket*)&Buffer; + + //comment to prevent packet logging + FILE *fh = NULL; + fopen_s (&fh, "log/inoutpackets.log", "a+"); + if ( fh != NULL ) { + fprintf( fh, "(SID:%08u) IN %04x: ", sock, pak->Command ); + for ( int i=0; iSize-6; ++i ) fprintf( fh, "%02x ", (unsigned char)pak->Buffer[i] ); + fprintf( fh, "\n" ); + fclose( fh ); + } + + // Handle actions for this packet + if ( !GS->OnReceivePacket( this, pak ) ) + { + //Log(MSG_ERROR, "onrecieve packet returned false"); + return false; + } + + + // Reset values for the next packet + PacketSize = 6; + PacketOffset = 0; + //Log(MSG_DEBUG, "onrecieve packet returned true"); + return true; +} + +// Send a packet to this client +void CClientSocket::SendPacket( CPacket *P ) +{ + //first log it so we don't have to worry about encryption. Comment to prevent logging + bool LogFile = true; + FILE *fh = NULL; + fopen_s (&fh, "log/receivedpackets.log", "a+"); + + if ( fh != NULL && LogFile) + { + fprintf( fh, "(SID:%08u) OUT %04x: ", sock, P->Command ); + for ( int i=0; iSize-6; ++i ) fprintf( fh, "%02x ", (unsigned char)P->Buffer[i] ); + fprintf( fh, "\n" ); + fclose( fh ); + } + + + // :WARNING: IF WE ADD A CRYPT FUNCTION HERE WE MUST COPY THE + // PACKET AND NOT USE THE ORIGINAL, IT WILL FUCK UP + // THE SENDTOALL FUNCTIONS + + unsigned char* Buffer2 = (unsigned char*)P; + unsigned Size = P->Size; +#ifndef USE124 + if(this == NULL) + { + Log(MSG_WARNING,"tried to send a packet to a socket that doesn't exist any more"); + return; + } + //__seh_try + //{ + // EncryptBuffer( CryptTable, Buffer ); + //} + //__seh_except(info, context) + //{ + // Log(MSG_WARNING,"Caught a bad packet %s. Send cancelled", Buffer); + // return; + //} + //__seh_end + EncryptBuffer( CryptTable, Buffer2 ); +#endif + send( sock, (char*)Buffer2, Size, 0 ); + + //record teh packet that we just sent + //CPacket* pak = (CPacket*)&Buffer; + + +} + +//------------------------------------------------------------------------- +// Send a packet to a client without encrypting the source packet +//------------------------------------------------------------------------- +void CClientSocket::SendPacketCpy( CPacket *P ) +{ + CPacket NewPacket; + + memcpy( &NewPacket, P, sizeof( CPacket ) ); + SendPacket( &NewPacket ); +} + +// Handle client socket (threads) +PVOID ClientMainThread( PVOID ClientSocket ) +{ + CClientSocket* thisplayer = (CClientSocket*) ClientSocket; + fd_set fds; + while(thisplayer->isActive) + { + FD_ZERO(&fds); + FD_SET (thisplayer->sock, &fds); + int Select = select( thisplayer->sock+1, &fds, NULL, NULL, NULL ); + if(Select == SOCKET_ERROR) + { + Log( MSG_ERROR,NULL,"Error in Select"); + thisplayer->isActive = false; + } + else + { + if(FD_ISSET( thisplayer->sock, &fds )) + { + if (thisplayer->isserver == true) + { + //Log( MSG_INFO,"ISC PACKET in ClientMainThread"); + thisplayer->ISCThread(); + } + else + if(!thisplayer->ReceiveData( )) + { + thisplayer->isActive = false; + } + } + } + + } + thisplayer->GS->DisconnectClient( thisplayer ); + pthread_exit(NULL); + return 0 ; +} + +// ----------------------------------------------------------------------------------------- diff --git a/Src/Common/SocketServer.cpp b/Src/Common/SocketServer.cpp new file mode 100644 index 0000000..a2acb5c --- /dev/null +++ b/Src/Common/SocketServer.cpp @@ -0,0 +1,549 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "sockets.h" +#include + +// Constructor +CServerSocket::CServerSocket( ) +{ +#ifdef USE124 + csum = new char [0x200]; + csumlen = buildChecksum(csum, "TRose.exe"); + if(csumlen < 0) + std::cout << "Failed to generate checksum, error: " << csumlen << std::endl; + else + std::cout << "Generated checksum" << std::endl; +#endif + ConnectedClients = 0; +} + +// Destructor +CServerSocket::~CServerSocket( ) +{ + +} + +// Start up our server +bool CServerSocket::StartServer( ) +{ + //struct sockaddr_in ain; + int test; + sock = socket( AF_INET, SOCK_STREAM, 0 ); + if (sock == INVALID_SOCKET) + { + Log( MSG_FATALERROR, "Could not create a socket" ); + return false; + } + + int optval = 1; + if(setsockopt(sock, SOL_SOCKET,SO_KEEPALIVE,(const char*)&optval,sizeof(optval))==-1) + { + Log(MSG_ERROR, "setsockopt:SO_KEEPALIVE" ); + } + setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(const char*)&optval,sizeof(optval)); + ain.sin_family = AF_INET; + ain.sin_addr.s_addr = INADDR_ANY; + ain.sin_port = htons( port ); + memset(&(ain.sin_zero), '\0', 8); + if ( bind( sock, (const sockaddr*)&ain, sizeof( struct sockaddr ) ) ) + { + + Log( MSG_FATALERROR, "Could not bind socket" ); + std::cout <<"socket " << sock << std::endl; + std::cin >> test; + closesocket( sock ); + sock = INVALID_SOCKET; + return false; + } + if ( listen( sock, SOMAXCONN ) == -1 ) + { + Log( MSG_FATALERROR, "Could not listen on socket" ); + closesocket( sock ); + sock = INVALID_SOCKET; + return false; + } + if (LOG_THISSERVER == LOG_CHARACTER_SERVER) + { + //struct sockaddr_in ain; + sckISC = socket( AF_INET, SOCK_STREAM, 0 ); + if (sckISC == INVALID_SOCKET) { + Log( MSG_ERROR, "Could not create valid ISC socket (WSK2 ERROR: %i)", WSAGetLastError( ) ); + return false; + } + optval = 1; + if(setsockopt(sckISC, SOL_SOCKET,SO_KEEPALIVE,(const char*)&optval,sizeof(optval))==-1) + { + Log(MSG_ERROR, "setsockopt:SO_KEEPALIVE" ); + } + setsockopt(sckISC, IPPROTO_TCP,TCP_NODELAY,(const char*)&optval,sizeof(optval)); + sain.sin_family = AF_INET; + sain.sin_addr.s_addr = INADDR_ANY; + sain.sin_port = htons( Config.CharsPort);//29110 ); + memset(&(sain.sin_zero), '\0', 8); + if ( bind( sckISC, (const sockaddr*)&sain, sizeof( struct sockaddr ) ) ) + { + Log( MSG_FATALERROR, "Could not bind socket" ); + closesocket( sckISC ); + sckISC = INVALID_SOCKET; + return false; + } + if ( listen( sckISC, SOMAXCONN ) == -1 ) + { + Log( MSG_FATALERROR, "Could not listen on socket" ); + closesocket( sckISC ); + sckISC = INVALID_SOCKET; + return false; + } + Log( MSG_INFO, "opened ISC poort %i",29110 ); + + } + + if (LOG_THISSERVER == LOG_WORLD_SERVER) + { + struct sockaddr_in aina; //if this doesn't work. change back to ain + sckISC = socket( AF_INET, SOCK_STREAM, 0 ); + if (sckISC == INVALID_SOCKET) { + Log( MSG_ERROR, "Could not create valid ISC socket (WSK2 ERROR: %i)", WSAGetLastError( ) ); + return false; + } + aina.sin_family = AF_INET; + aina.sin_addr.s_addr = inet_addr(Config.CharIP);//( "127.0.0.1" ); + aina.sin_port = htons( Config.CharPort);//29110 ); + if ( connect( sckISC, (const sockaddr*)&aina, sizeof( aina ) ) == SOCKET_ERROR ) { + Log( MSG_ERROR, "Could not connect to ISC (WSK2 ERROR: %i)", WSAGetLastError( ) ); + closesocket( sckISC ); + sckISC = INVALID_SOCKET; + return false; + } + + + sckISCII = socket( AF_INET, SOCK_STREAM, 0 ); + if (sckISCII == INVALID_SOCKET) { + Log( MSG_ERROR, "Could not create valid ISC socket (WSK2 ERROR: %i)", WSAGetLastError( ) ); + return false; + } + optval = 1; + if(setsockopt(sckISCII, SOL_SOCKET,SO_KEEPALIVE,(const char*)&optval,sizeof(optval))==-1) + { + Log(MSG_ERROR, "setsockopt:SO_KEEPALIVE" ); + } + setsockopt(sckISCII, IPPROTO_TCP,TCP_NODELAY,(const char*)&optval,sizeof(optval)); + sain.sin_family = AF_INET; + sain.sin_addr.s_addr = INADDR_ANY; + sain.sin_port = htons( Config.WorldsPort );//29210 ); + memset(&(sain.sin_zero), '\0', 8); + if ( bind( sckISCII, (const sockaddr*)&sain, sizeof( struct sockaddr ) ) ) + { + Log( MSG_FATALERROR, "Could not bind socket" ); + closesocket( sckISCII ); + sckISCII = INVALID_SOCKET; + return false; + } + if ( listen( sckISCII, SOMAXCONN ) == -1 ) + { + Log( MSG_FATALERROR, "Could not listen on socket" ); + closesocket( sckISCII ); + sckISCII = INVALID_SOCKET; + return false; + } + Log( MSG_INFO, "opened ISC poort %i",Config.WorldsPort );//29210 ); + } + if (LOG_THISSERVER == LOG_LOGIN_SERVER) + { + struct sockaddr_in aina; //may need to change back to ain + sckISC = socket( AF_INET, SOCK_STREAM, 0 ); + if (sckISC == INVALID_SOCKET) { + Log( MSG_ERROR, "Could not create valid ISC socket (WSK2 ERROR: %i)", WSAGetLastError( ) ); + return false; + } + } + isActive = true; + + if ( !this->OnServerReady( ) ) + { + Log( MSG_FATALERROR, "Server could not start" ); + closesocket( sock ); + sock = INVALID_SOCKET; + isActive = false; + return false; + } + Log( MSG_INFO, "Server started on port %i and is listening.", port ); + //ISCThread( ); + ServerLoop( ); + // Nothing past here is ever really called + OnServerDie( ); + closesocket( sock ); + + return true; +} + +// Raven0123 +void CServerSocket::CryptISCPak( char* pak ) +{ + return; + unsigned paksize = *((unsigned short*)&pak[0]); + for( unsigned short i = 2; i < paksize; i ++ ) + pak[i] ^= 0x81 * i * paksize; +} + +// Raven0123 +//void CServerSocket::ISCThread() +bool CClientSocket::ISCThread() +{ + unsigned char buffer[0x400]; + unsigned recvd = 0; + unsigned bufsize = 0; + unsigned readlen = 6; + bool go = true; + + do { + + recvd = recv( sock, (char*)&buffer[bufsize], readlen-bufsize, 0 ); + if( recvd == 0){ + return false; + } + + if( recvd == SOCKET_ERROR ) { + return false; + } + + bufsize += recvd; + if( bufsize != readlen ) continue;//return;//continue; + if( bufsize == 6 ) { + readlen = *((unsigned short*)&buffer[0]); + if( readlen < 6 ) Log( MSG_ERROR, "Invalid server Packet Header" ); + if( readlen > 6 ) continue;//return;//continue; + } + + GS->OnReceivePacket( this, (CPacket*)&buffer[0] ); + go = false; + + bufsize = 0; + readlen = 6; + } while ( go==true ); + return true; +} + +// Server is started, lets run our loop :D +void CServerSocket::ServerLoop( ) +{ + fd_set fds; + int activity; + maxfd = 0; + sockaddr_in ClientInfo; + SOCKET NewSocket; + timeval timeout; + maxfd = sock; + OnServerStep(); + do + { + if (!Ping( )) + isActive = false; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + NewSocket = INVALID_SOCKET; + FD_ZERO( &fds ); + if(!Config.usethreads) + FillFDS( &fds ); + FD_SET( sock, &fds ); + activity = select( maxfd+1, &fds, NULL, NULL, &timeout ); + if ( activity == 0 ) + { + // continue; + FD_ZERO( &fds ); + if(!Config.usethreads) + FillFDS( &fds ); + FD_SET( sckISC, &fds ); + activity = select( maxfd+1, &fds, NULL, NULL, &timeout ); + if ( activity == 0 )continue; + if ( activity < 0 && errno != EINTR ) + { + #ifdef _WIN32 + Log( MSG_ERROR, "Select command failed. Error #%i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Select command failed. Error #%i", errno ); + #endif + isActive = false; + } + if ( FD_ISSET( sckISC, &fds ) ) + { + int clientinfolen = sizeof( sockaddr_in ); + #ifdef _WIN32 + NewSocket = accept( sckISC, (sockaddr*)&ClientInfo, (int*)&clientinfolen ); + #else + NewSocket = accept( sckISC, (sockaddr*)&ClientInfo, (socklen_t*)&clientinfolen ); + #endif + // TODO: check if server is full + if (NewSocket != INVALID_SOCKET) + { + AddUser( NewSocket, &ClientInfo, true ); + + } + else + { + #ifdef _WIN32 + Log( MSG_ERROR, "Error accepting socket: %i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Error accepting socket: %i", errno ); + #endif + } + } + } + + if ( activity < 0 && errno != EINTR ) + { + #ifdef _WIN32 + Log( MSG_ERROR, "Select command failed. Error #%i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Select command failed. Error #%i", errno ); + #endif + isActive = false; + } + if ( FD_ISSET( sock, &fds ) ) + { + int clientinfolen = sizeof( sockaddr_in ); + #ifdef _WIN32 + NewSocket = accept( sock, (sockaddr*)&ClientInfo, (int*)&clientinfolen ); + #else + NewSocket = accept( sock, (sockaddr*)&ClientInfo, (socklen_t*)&clientinfolen ); + #endif + // TODO: check if server is full + if (NewSocket != INVALID_SOCKET) + AddUser( NewSocket, &ClientInfo, false ); + else + { + #ifdef _WIN32 + Log( MSG_ERROR, "Error accepting socket: %i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Error accepting socket: %i", errno ); + #endif + } + } + if(!Config.usethreads) + HandleClients( &fds ); + } while( isActive ); +} + +// Fills out an FDS for the server +void CServerSocket::FillFDS( fd_set* fds ) +{ + for(UINT i=0;iisActive) + { + FD_SET( (unsigned)client->sock, fds ); + if(client->sock>maxfd) + maxfd=client->sock; + } + else + { + DisconnectClient( client ); + } + } +} + +// Handle all our clients +void CServerSocket::HandleClients( fd_set* fds ) +{ + for(UINT i=0;iisActive) + continue; + if(FD_ISSET( client->sock, fds )) + { + if (client->isserver == true) + { + //Log( MSG_INFO,"ISC PACKET in HandleClients"); + if(!client->ISCThread()){ + client->isActive = false; + DisconnectClient( client );} + } + else + if(!client->ReceiveData( ) ) + { + client->isActive = false; + DisconnectClient( client ); + } + } + } +} + +// Add a new user to our server +void CServerSocket::AddUser( SOCKET sock, sockaddr_in* ClientInfo, bool server ) +{ + ConnectedClients++; + CClientSocket* thisclient = this->CreateClientSocket( ); + if (thisclient==NULL) + { + closesocket( thisclient->sock ); + if (thisclient!=0) delete thisclient; + thisclient=0; + return; + } +#ifdef USE124 + thisclient->ct = new char[TABLE_SIZE]; + buildCryptTable(thisclient->ct, csum, csumlen); +#endif + thisclient->CryptTable = CryptTable; + thisclient->CryptStatus.CurAddValue = 0; + thisclient->CryptStatus.CurEncryptionValue = CryptTable->EncryptionStartValue; + thisclient->GS = this; + thisclient->sock = sock; + thisclient->isActive = true; + + if (!OnClientConnect( thisclient )) { + closesocket( thisclient->sock ); + if (thisclient!=0) delete thisclient; + thisclient=0; + return; + } + thisclient->ClientIP = ""; + thisclient->ClientIP = inet_ntoa( ClientInfo->sin_addr ); + char *tmp; + memset(&thisclient->ClientSubNet, '\0', 12 ); + sprintf_s(thisclient->ClientSubNet, "%i.%i.%i", + (ClientInfo->sin_addr.s_addr )&0xFF, (ClientInfo->sin_addr.s_addr>>8 )&0xFF, + (ClientInfo->sin_addr.s_addr>>16)&0xFF); + ClientList.push_back( thisclient ); + if(Config.usethreads) + { + pthread_create( &threads[sock], NULL, ClientMainThread, (PVOID)thisclient); + } + memcpy( &thisclient->clientinfo, ClientInfo, sizeof(struct sockaddr_in)); + if(server==true) + { + thisclient->isserver=true; + Log( MSG_INFO, "Server connected from %s", inet_ntoa( ClientInfo->sin_addr ) ); + } + else + { + thisclient->isserver=false; + Log( MSG_INFO, "User connected from %s", inet_ntoa( ClientInfo->sin_addr ) ); + } +} + +// Disconnect our user +void CServerSocket::DisconnectClient( CClientSocket* thisclient ) +{ + ConnectedClients--; + OnClientDisconnect( thisclient ); + closesocket( thisclient->sock ); + thisclient->isActive = false; + thisclient->sock = INVALID_SOCKET; + for(UINT i=0;iisserver) + Log( MSG_INFO, "Server disconnected" ); + else + Log( MSG_INFO, "User disconnected" ); + delete thisclient; +} + +//This function loads the encryption +void CServerSocket::LoadEncryption( ) +{ + GenerateCryptTables( CryptTable, 0x87654321 ); + //port = ConfigGetInt("server.conf", "loginport", 29000); +} + +// This function is called just before proccessing clients +void CServerSocket::OnServerStep( ) +{ +} + +// This function is called just before the server starts +bool CServerSocket::OnServerReady( ) +{ + return true; +} + +// This function is called just before the server dies +void CServerSocket::OnServerDie( ) +{ + // DOESNT WORK - DAMN CONSOLE APPS +} + +// This function is called, if a client receives data +bool CServerSocket::OnReceivePacket( CClientSocket* thisclient, CPacket *P ) +{ + return true; +} + +// This function is called, if a client connects +bool CServerSocket::OnClientConnect( CClientSocket* thisclient ) +{ + return true; +} + +// This function is called, if a client disconnects +void CServerSocket::OnClientDisconnect( CClientSocket* thisclient ) +{ +} + +// Raven0123 +void CServerSocket::SendISCPacket( CPacket* pak ) +{ + send( sckISC, (char*)pak, pak->Size, 0 ); +} + +void CServerSocket::ReceivedISCPacket( CPacket* pak ) +{ + Log( MSG_DEBUG, "GOT ISC PACKET (BASESERVER) - 0x%04x %04x", pak->Command, pak->Size ); +} + +bool CServerSocket::DoSQL(char *Format, ...) { + int retval; + char Buffer[0x1500]; + va_list ap; va_start( ap, Format ); + vsprintf_s( Buffer, Format, ap ); + va_end ( ap ); + retval = mysql_query( mysql, Buffer ); + if( retval != 0 ) Log( MSG_ERROR, "MySQL Query Error '%s'", mysql_error( mysql ) ); + return (retval==0); +} + +bool CServerSocket::Ping( ) +{ + return true; +} diff --git a/Src/Common/config.cpp b/Src/Common/config.cpp new file mode 100644 index 0000000..9b17f15 --- /dev/null +++ b/Src/Common/config.cpp @@ -0,0 +1,114 @@ +/******************************************************************************************* + ___ ___ ___ ___ + ___| _ \/ _ \/ __| __| + / -_) / (_) \__ \ _| + \___|_|_\\___/|___/___| + + eROSE Server Develped by Brett19. + hROSE Team: Brett19 and bdaan + Year: 2006 + Release Date: Summer-Winter 2006 +*******************************************************************************************/ +#include +#include +#include +#include + +// Find the entry +bool ConfigGetEntry( const char *pcFile, const char *pcNameMust, char* &pcResult ) +{ + char pcBuffer[ 512 ]; + char *pcName, *pcPar, *pcPoint; + unsigned len; + + pcResult=0; + + // Open file + FILE* fh = NULL; + fopen_s (&fh, pcFile, "r"); + if ( fh == NULL ) return false; + + do + { + fgets( pcBuffer, 510, fh ); + len = (unsigned)strlen( pcBuffer ); + if ( pcBuffer[ len-1 ] == '\n' ) --len; + pcBuffer[ len ] = 0; + + pcName = pcBuffer; + while ( *pcName==' ' || *pcName=='\t' ) ++pcName; + if ( *pcName==0 || *pcName=='#' ) continue; + pcPoint = pcName; + while ( *pcPoint!=' ' && *pcPoint!='\t' && *pcPoint!='#' && *pcPoint!='=' && *pcPoint!=0 ) ++pcPoint; + if ( *pcPoint==0 || *pcPoint=='#' ) continue; + pcPar = pcPoint; + while ( *pcPar==' ' || *pcPar=='\t' || *pcPar=='=' ) ++pcPar; + *pcPoint=0; + + #ifdef _WIN32 + if ( !_strcmpi( pcName, pcNameMust ) ) + { + pcResult = _strdup( pcPar ); + pcPoint = pcResult + strlen( pcPar ); + if ( *pcPoint==0 ) return true; + --pcPoint; + while( *pcPoint==' ' || *pcPoint=='\t' ) *pcPoint--=0; + return true; + } + + #else + if ( strcasecmp( pcName, pcNameMust )==0 ) + { + pcResult = _strdup( pcPar ); + pcPoint = pcResult + strlen( pcPar ); + if ( *pcPoint==0 ) return true; + --pcPoint; + while( *pcPoint==' ' || *pcPoint=='\t' ) *pcPoint--=0; + return true; + } + #endif + + } while ( !feof( fh ) ); + + // Close file + fclose( fh ); + + return true; +} + +// ----------------------------------------------------------------------------------------- +// Returns a text +// ----------------------------------------------------------------------------------------- +char *ConfigGetString( const char *pcFile, const char *pcName, const char *pcDefault ) +{ + char *pcRet, *pcRetReal, *pcRetOld; + if ( !ConfigGetEntry( pcFile, pcName, pcRet ) ) return _strdup( pcDefault ); + if (pcRet==0) return _strdup( pcDefault ); + pcRetOld = pcRet; + if ( *pcRet=='"' ) + { + ++pcRet; + pcRet[ strlen(pcRet) - 1 ] = 0; + } + + pcRetReal = _strdup( pcRet ); + free( pcRetOld ); + + return pcRetReal; +} + +// Returns a number +unsigned ConfigGetInt( const char *pcFile, const char *pcName, unsigned uDefault ) +{ + char *pcRet; + unsigned uRet; + if ( !ConfigGetEntry( pcFile, pcName, pcRet ) ) return uDefault; + if (pcRet==0) return uDefault; + + uRet = atoi( pcRet ); + free( pcRet ); + + return uRet; +} + +// ----------------------------------------------------------------------------------------- diff --git a/Src/Common/config.h b/Src/Common/config.h new file mode 100644 index 0000000..8b5a76c --- /dev/null +++ b/Src/Common/config.h @@ -0,0 +1,24 @@ +/******************************************************************************************* + ___ ___ ___ ___ + ___| _ \/ _ \/ __| __| + / -_) / (_) \__ \ _| + \___|_|_\\___/|___/___| + + eROSE Server Develped by Brett19. + hROSE Team: Brett19 and bdaan + Year: 2006 + Release Date: Summer-Winter 2006 +*******************************************************************************************/ +#ifndef __ROSE_CONFIG_FUNCTIONS__ +#define __ROSE_CONFIG_FUNCTIONS__ + +// ----------------------------------------------------------------------------------------- +// Config load functions +// ----------------------------------------------------------------------------------------- +char *ConfigGetString( const char *pcFile, const char *pcName, const char *pcDefault ); +unsigned ConfigGetInt( const char *pcFile, const char *pcName, unsigned uDefault ); + +// ----------------------------------------------------------------------------------------- + +#endif + diff --git a/Src/Common/database/database.cpp b/Src/Common/database/database.cpp new file mode 100644 index 0000000..831e131 --- /dev/null +++ b/Src/Common/database/database.cpp @@ -0,0 +1,188 @@ +#include "database.h" +#include "../log.h" + +// constructor +CDatabase::CDatabase( char* server , char* username, char* password, char* database, unsigned int port , MYSQL* mysql) +{ + Server = server; + Username = username; + Password = password; + Database = database; + Port = port; + Mysql = mysql; + LastPing = time( NULL ); + SQLMutex = PTHREAD_MUTEX_INITIALIZER; + mysql_init( Mysql ); +} + +// deconstructor +CDatabase::~CDatabase( ) +{ +} + +// disconnect from mysql +void CDatabase::Disconnect( ) +{ + mysql_close( Mysql ); +} + +int CDatabase::Connect( ) +{ + Log( MSG_INFO, "Connecting to MySQL" ); + if(!mysql_real_connect( Mysql, Server, Username, Password, Database,Port, NULL, 0 )) + { + Log( MSG_FATALERROR, "Error connecting to MySQL server: %s\n", mysql_error( Mysql ) ); + return -1; + } + // Get timeout so we know when to ping - Drakia + MYSQL_RES *myresult = QStore("SELECT @@wait_timeout"); + MYSQL_ROW row = mysql_fetch_row( myresult ); + Timeout = atoi(row[0]) - 60; // Set the timeout to 1 minute before the connection will timeout + QFree( ); + Log( MSG_INFO, "MySQL Ping Timeout: %d seconds", Timeout ); + return 0; +} + +// reconnect to mysql +int CDatabase::Reconnect( ) +{ + Log( MSG_INFO, "Reconnecting to Mysql" ); + if(!mysql_real_connect( Mysql, Server, Username, Password, Database,Port, NULL, 0 )) + { + Log( MSG_FATALERROR, "Error reconnecting to MySQL server: %s\n", mysql_error( Mysql ) ); + return -1; + } + return 0; +} + +// execute query +bool CDatabase::QExecute( const char *Format,... ) +{ + bool Qfail = true; + char query[80000]; + va_list ap; + va_start( ap, Format ); + vsprintf_s (query, Format, ap); + va_end ( ap ); + Log( MSG_QUERY, query ); + pthread_mutex_lock( &SQLMutex ); + while(Qfail) + { + if(mysql_query( Mysql, query )!=0) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + if(Reconnect( )==-1) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + pthread_mutex_unlock( &SQLMutex ); + return false; + } + else Qfail = false; + } + else Qfail = false; + } + pthread_mutex_unlock( &SQLMutex ); + return true; +} + +MYSQL_RES* CDatabase::QStore( const char *Format, ...) +{ + bool Qfail = true; + char query[80000]; + va_list ap; + va_start( ap, Format ); + vsprintf_s( query, Format, ap ); + va_end ( ap ); + result = NULL; + Log( MSG_QUERY, query ); + //Log( MSG_INFO, query ); + //Log( MSG_INFO, "Locking pthread mutex" ); + pthread_mutex_lock( &SQLMutex ); + //Log( MSG_INFO, "Locked pthread mutex" ); + while(Qfail) + { + if(mysql_query( Mysql, query )!=0) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + if(Reconnect( )==-1) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + pthread_mutex_unlock( &SQLMutex ); + return NULL; + } + else Qfail = false; + } + else Qfail = false; + // Log( MSG_INFO, "mysql answer: %s", mysql_error( Mysql ) ); + } + //Log( MSG_INFO, "reading mysql result" ); + result = mysql_store_result( Mysql ); + //Log( MSG_INFO, "returning mysql result" ); + return result; +} + +MYSQL_RES* CDatabase::QUse( const char *Format, ...) +{ + bool Qfail = true; + char query[1024]; + va_list ap; + va_start( ap, Format ); + vsprintf_s( query, Format, ap ); + va_end ( ap ); + result = NULL; + Log( MSG_QUERY, query ); + pthread_mutex_lock( &SQLMutex ); + while(Qfail) + { + if(mysql_query( Mysql, query )!=0) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + if(Reconnect( )==-1) + { + Log( MSG_FATALERROR, "Could not execute query: %s", mysql_error( Mysql ) ); + pthread_mutex_unlock( &SQLMutex ); + return NULL; + } + else Qfail = false; + } + else Qfail = false; + } + result = mysql_use_result( Mysql ); + return result; +} + +bool CDatabase::DoSQL(const char *Format, ...) { + int retval; + char Buffer[1024]; + va_list ap; va_start( ap, Format ); + vsprintf_s( Buffer, Format, ap ); + va_end ( ap ); + retval = mysql_query( Mysql, Buffer ); + if( retval!=0 ) Log( MSG_ERROR, "MySQL Query Error '%s'", mysql_error( Mysql ) ); + return (retval==0); +} + +void CDatabase::QFree( ) +{ + mysql_free_result( result ); + pthread_mutex_unlock( &SQLMutex ); +} + +bool CDatabase::Ping( ) +{ + if ( time( NULL ) > ( LastPing + Timeout ) ) { + Log(MSG_INFO, "MySql Ping"); + LastPing = time( NULL ); + MYSQL_RES *res = QStore("SELECT @@wait_timeout"); + if (res == NULL) { + Log( MSG_ERROR, "MySQL Ping Failed, Attempting reconnect" ); + QFree( ); + return !Reconnect( ); + } + MYSQL_ROW row = mysql_fetch_row(res); + Timeout = atoi(row[0]) - 60; + QFree( ); + Log( MSG_INFO, "MySql Pong, new Timeout: %d seconds", Timeout); + } + return true; +} diff --git a/Src/Common/database/database.h b/Src/Common/database/database.h new file mode 100644 index 0000000..ca0ec84 --- /dev/null +++ b/Src/Common/database/database.h @@ -0,0 +1,33 @@ +#ifndef _ROSE_DATABASE_ +#define _ROSE_DATABASE_ +#include "../sockets.h" + +class CDatabase +{ + public: + CDatabase( char* , char*, char*, char*, unsigned int, MYSQL* ); + ~CDatabase( ); + + char* Server; + char* Username; + char* Password; + char* Database; + unsigned int Port; + unsigned int Timeout; + time_t LastPing; + int Connect( ); + int Reconnect( ); + void Disconnect( ); + bool QExecute( const char *format,... ); + MYSQL_RES* QUse( const char *format,... ); + MYSQL_RES* QStore( const char *format,... ); + bool DoSQL(const char *Format, ...); + void QFree( ); + bool Ping( ); + + MYSQL* Mysql; + MYSQL_RES* result; + pthread_mutex_t SQLMutex; +}; + +#endif diff --git a/Src/Common/globals.cpp b/Src/Common/globals.cpp new file mode 100644 index 0000000..0f449cb --- /dev/null +++ b/Src/Common/globals.cpp @@ -0,0 +1,91 @@ +#include "sockets.h" + +CPacket::CPacket( unsigned short mycommand, unsigned short mysize, unsigned short myunused ) +{ + Command = mycommand; + Size = mysize; + Unused = myunused; +} +CPacket::~CPacket( ) { } + +void CPacket::StartPacket( unsigned short mycommand, unsigned short mysize, unsigned short myunused ) +{ + Command = mycommand; + Size = mysize; + Unused = myunused; +} +void CPacket::AddByte( unsigned char value ) +{ + Buffer[Size-6] = value; + Size += 1; +} +void CPacket::AddWord( unsigned short value ) +{ + *((unsigned short*)&Buffer[Size-6]) = value; + Size += 2; +} +void CPacket::AddDWord( unsigned value ) +{ + *((unsigned*)&Buffer[Size-6]) = value; + Size += 4; +} +void CPacket::AddQWord( unsigned long long value ) +{ + *((unsigned long long*)&Buffer[Size-6]) = value; + Size += 8; +} +void CPacket::AddFloat( float value ) +{ + *((float*)&Buffer[Size-6]) = value; + Size += 4; +} +void CPacket::AddString( char* value ) +{ + for(unsigned char c=0; c<(unsigned)strlen(value); c++) + AddByte( value[c] ); +} +void CPacket::SetByte( unsigned short pos, unsigned char value ) +{ + Buffer[pos] = value; +} +void CPacket::SetWord( unsigned short pos, unsigned short value ) +{ + *((unsigned short*)&Buffer[pos]) = value; +} +void CPacket::SetDWord( unsigned short pos, unsigned value ) +{ + *((unsigned*)&Buffer[pos]) = value; +} +void CPacket::SetQWord( unsigned short pos, unsigned long long value ) +{ + *((unsigned long long*)&Buffer[pos]) = value; +} +void CPacket::SetFloat( unsigned short pos, float value ) +{ + *((float*)&Buffer[pos]) = value; +} +unsigned char CPacket::GetByte( unsigned short pos ) +{ + return Buffer[pos]; +} +unsigned short CPacket::GetWord( unsigned short pos ) +{ + return *((unsigned short*)&Buffer[pos]); +} +unsigned CPacket::GetDWord( unsigned short pos ) +{ + return *((unsigned*)&Buffer[pos]); +} +unsigned long long CPacket::GetQWord( unsigned short pos ) +{ + return *((unsigned long long*)&Buffer[pos]); +} +float CPacket::GetFloat( unsigned short pos ) +{ + return *((float*)&Buffer[pos]); +} + +char* CPacket::GetString( unsigned short pos ) +{ + return (char*)&Buffer[pos]; +} diff --git a/Src/Common/log.cpp b/Src/Common/log.cpp new file mode 100644 index 0000000..184aea6 --- /dev/null +++ b/Src/Common/log.cpp @@ -0,0 +1,167 @@ +#ifdef _WIN32 + #include + #include + #include +#endif +#include +#include +#include +#include "log.h" + +// Basic colors +typedef enum +{ + BLACK, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + LIGHTGRAY, + DARKGRAY, + LIGHTBLUE, + LIGHTGREEN, + LIGHTCYAN, + LIGHTRED, + LIGHTMAGENTA, + YELLOW, + WHITE +} COLORS; + +// Our base colors +static int __BACKGROUND = BLACK; +static int __FOREGROUND = LIGHTGRAY; + +// Change console text color +void textcolor(int color) +{ + #ifdef _WIN32 + __FOREGROUND = color; + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color + (__BACKGROUND << 4)); + #else + + #endif +} + +// This function logs based on flags +void Log( enum msg_type flag, const char *Format, ... ) +{ + va_list ap; // For arguments + va_start( ap, Format ); + + // Timestamp + time_t rtime; + time (&rtime); + char timestamp[26]; + ctime_s (timestamp, sizeof (timestamp), &rtime); + timestamp[strlen (timestamp) - 1] = ' '; + + switch (flag) { + case MSG_NONE: // direct printf replacement + textcolor(WHITE); + vprintf( Format, ap ); + break; + case MSG_STATUS: + textcolor(GREEN); + printf("[STATUS]: "); + break; + case MSG_SQL: + textcolor(CYAN); + printf("[SQL]: "); + break; + case MSG_INFO: + textcolor(LIGHTGREEN); + printf("[INFO]: "); + break; + case MSG_NOTICE: + textcolor(LIGHTCYAN); + printf("[NOTICE]: "); + break; + case MSG_WARNING: + textcolor(YELLOW); + printf("[WARNING]: "); + break; + case MSG_DEBUG: + textcolor(LIGHTBLUE); + printf("[DEBUG]: "); + break; + case MSG_ERROR: + textcolor(RED); + printf("[ERROR]: "); + break; + case MSG_FATALERROR: + textcolor(LIGHTRED); + printf("[FATAL ERROR]: "); + break; + case MSG_HACK: + textcolor(LIGHTRED); + printf("[HACK]: "); + break; + case MSG_LOAD: + textcolor(BROWN); + printf("[LOADING]: "); + break; + case MSG_GMACTION: + textcolor(MAGENTA); + printf("[GM ACTION]: "); + break; + case MSG_START: + textcolor(MAGENTA); + vprintf( Format, ap ); + printf( "\r\n" ); + break; + //case MSG_SDEBUG: + // textcolor(MAGENTA); + // printf("[SDEBUG]: "); + // break; + //case MSG_QUERY: + // textcolor(LIGHTBLUE); + // printf("[QUERY]: "); + // break; + //default: + // textcolor(MAGENTA); + // printf("[UNKNOWN CASE]: "); + // break; + } + textcolor(LIGHTGRAY); + if(flag!=MSG_QUERY) + { + printf( "%s- ", timestamp ); + vprintf( Format, ap ); + printf( (flag==MSG_LOAD) ? "\r" : "\n" ); + } + FILE *fh = NULL; + switch(LOG_THISSERVER) + { + case LOG_LOGIN_SERVER: + fopen_s (&fh, LOG_DIRECTORY LOG_FILENAME_LOGINFILE, "a+"); + break; + case LOG_CHARACTER_SERVER: + fopen_s (&fh, LOG_DIRECTORY LOG_FILENAME_CHARFILE, "a+"); + break; + case LOG_WORLD_SERVER: + fopen_s (&fh, LOG_DIRECTORY LOG_FILENAME_WORLDFILE, "a+"); + break; + case LOG_SAME_FILE: + fopen_s (&fh, LOG_DIRECTORY LOG_DEFAULT_FILE, "a+"); + break; + default: + break; + } + if(flag == MSG_QUERY) + { + if(fh != NULL) + fclose(fh); + fopen_s (&fh, LOG_DIRECTORY "queries.txt", "a+"); + } + if ( fh != NULL ) + { + fprintf( fh, "%s- ", timestamp ); + vfprintf( fh, Format, ap ); + fputc( '\n', fh ); + fclose( fh ); + } + + va_end ( ap ); +} diff --git a/Src/Common/log.h b/Src/Common/log.h new file mode 100644 index 0000000..8d47887 --- /dev/null +++ b/Src/Common/log.h @@ -0,0 +1,58 @@ +/* + Open Source Rose Online Team - http://osroseon.to.md/ + note: the Server is develop with erose source server + eich source +*/ +#ifndef __ROSE_LOGGING_FUNCTIONS__ +#define __ROSE_LOGGING_FUNCTIONS__ + +// Log path +#ifdef _WIN32 +# define LOG_DIRECTORY "log/" +#endif + +#ifdef __unix__ +# define LOG_DIRECTORY "/var/log/roseserver/" +#endif + +#define LOG_LOGIN_SERVER 0x01 +#define LOG_CHARACTER_SERVER 0x02 +#define LOG_WORLD_SERVER 0x03 +#define LOG_SAME_FILE 0x04 + +// Log File names +extern unsigned char LOG_THISSERVER; +#define LOG_FILENAME_LOGINFILE "loginserver.log" +#define LOG_FILENAME_CHARFILE "charserver.log" +#define LOG_FILENAME_WORLDFILE "worldserver.log" +#define LOG_DEFAULT_FILE "server.log" +#define LOG_FILENAME_ERRORFILE "errors.log" + +#define LOG_LOGINPACKETS "loginpacket.log" +#define LOG_CHARPACKETS "charpackets.log" +#define LOG_WORLDPACKETS "worldpackets.log" +#define LOG_DEFAULTPACKETS "packets.log" + +// Error Types +enum msg_type { + MSG_NONE, + MSG_STATUS, + MSG_SQL, + MSG_INFO, + MSG_NOTICE, + MSG_WARNING, + MSG_DEBUG, + MSG_ERROR, + MSG_FATALERROR, + MSG_HACK, + MSG_LOAD, + MSG_SDEBUG, + MSG_GMACTION, + MSG_START, + MSG_QUERY, + MSG_QUESTDEBUG +}; + +// Logging function +void Log( msg_type flag, const char* Format, ... ); + +#endif diff --git a/Src/Common/mysql/mysql.h b/Src/Common/mysql/mysql.h new file mode 100644 index 0000000..ab61fe6 --- /dev/null +++ b/Src/Common/mysql/mysql.h @@ -0,0 +1,739 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _mysql_h +#define _mysql_h + +#ifdef __CYGWIN__ /* CYGWIN implements a UNIX API */ +#undef WIN +#undef _WIN +#undef _WIN32 +#undef _WIN64 +#undef __WIN__ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _global_h /* If not standard header */ +#include +#ifdef __LCC__ +#include /* For windows */ +#endif +typedef char my_bool; +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__WIN__) +#define __WIN__ +#endif +#if !defined(__WIN__) +#define STDCALL +#else +#define STDCALL __stdcall +#endif +typedef char * gptr; + +#ifndef my_socket_defined +#ifdef __WIN__ +#define my_socket SOCKET +#else +typedef int my_socket; +#endif /* __WIN__ */ +#endif /* my_socket_defined */ +#endif /* _global_h */ + +#include "mysql_com.h" +#include "mysql_time.h" +#include "mysql_version.h" +#include "typelib.h" + +#include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ + +extern unsigned int mysql_port; +extern char *mysql_unix_port; + +#define CLIENT_NET_READ_TIMEOUT 365*24*3600 /* Timeout on read */ +#define CLIENT_NET_WRITE_TIMEOUT 365*24*3600 /* Timeout on write */ + +#ifdef __NETWARE__ +#pragma pack(push, 8) /* 8 byte alignment */ +#endif + +#define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG) +#define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG) +#define IS_BLOB(n) ((n) & BLOB_FLAG) +#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR) +#define IS_NUM_FIELD(f) ((f)->flags & NUM_FLAG) +#define INTERNAL_NUM_FIELD(f) (((f)->type <= FIELD_TYPE_INT24 && ((f)->type != FIELD_TYPE_TIMESTAMP || (f)->length == 14 || (f)->length == 8)) || (f)->type == FIELD_TYPE_YEAR) + + +typedef struct st_mysql_field { + char *name; /* Name of column */ + char *org_name; /* Original column name, if an alias */ + char *table; /* Table of column if column was a field */ + char *org_table; /* Org table name, if table was an alias */ + char *db; /* Database for table */ + char *catalog; /* Catalog for table */ + char *def; /* Default value (set by mysql_list_fields) */ + unsigned long length; /* Width of column (create length) */ + unsigned long max_length; /* Max width for selected set */ + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; /* Div flags */ + unsigned int decimals; /* Number of decimals in field */ + unsigned int charsetnr; /* Character set */ + enum enum_field_types type; /* Type of field. See mysql_com.h for types */ +} MYSQL_FIELD; + +typedef char **MYSQL_ROW; /* return data as array of strings */ +typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */ + +#ifndef _global_h +#if defined(NO_CLIENT_LONG_LONG) +typedef unsigned long my_ulonglong; +#elif defined (__WIN__) +typedef unsigned __int64 my_ulonglong; +#else +typedef unsigned long long my_ulonglong; +#endif +#endif + +#define MYSQL_COUNT_ERROR (~(my_ulonglong) 0) + +typedef struct st_mysql_rows { + struct st_mysql_rows *next; /* list of rows */ + MYSQL_ROW data; + unsigned long length; +} MYSQL_ROWS; + +typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */ + +#include "my_alloc.h" + +typedef struct st_mysql_data { + my_ulonglong rows; + unsigned int fields; + MYSQL_ROWS *data; + MEM_ROOT alloc; +#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY) + MYSQL_ROWS **prev_ptr; +#endif +} MYSQL_DATA; + +enum mysql_option +{ + MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE, + MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, + MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE, + MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT, + MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, + MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, + MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH +}; + +struct st_mysql_options { + unsigned int connect_timeout, read_timeout, write_timeout; + unsigned int port, protocol; + unsigned long client_flag; + char *host,*user,*password,*unix_socket,*db; + struct st_dynamic_array *init_commands; + char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name; + char *ssl_key; /* PEM key file */ + char *ssl_cert; /* PEM cert file */ + char *ssl_ca; /* PEM CA file */ + char *ssl_capath; /* PEM directory of CA-s? */ + char *ssl_cipher; /* cipher to use */ + char *shared_memory_base_name; + unsigned long max_allowed_packet; + my_bool use_ssl; /* if to use SSL or not */ + my_bool compress,named_pipe; + /* + On connect, find out the replication role of the server, and + establish connections to all the peers + */ + my_bool rpl_probe; + /* + Each call to mysql_real_query() will parse it to tell if it is a read + or a write, and direct it to the slave or the master + */ + my_bool rpl_parse; + /* + If set, never read from a master, only from slave, when doing + a read that is replication-aware + */ + my_bool no_master_reads; +#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY) + my_bool separate_thread; +#endif + enum mysql_option methods_to_use; + char *client_ip; + /* Refuse client connecting to server if it uses old (pre-4.1.1) protocol */ + my_bool secure_auth; + + /* function pointers for local infile support */ + int (*local_infile_init)(void **, const char *, void *); + int (*local_infile_read)(void *, char *, unsigned int); + void (*local_infile_end)(void *); + int (*local_infile_error)(void *, char *, unsigned int); + void *local_infile_userdata; +}; + +enum mysql_status +{ + MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,MYSQL_STATUS_USE_RESULT +}; + +enum mysql_protocol_type +{ + MYSQL_PROTOCOL_DEFAULT, MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET, + MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY +}; +/* + There are three types of queries - the ones that have to go to + the master, the ones that go to a slave, and the adminstrative + type which must happen on the pivot connectioin +*/ +enum mysql_rpl_type +{ + MYSQL_RPL_MASTER, MYSQL_RPL_SLAVE, MYSQL_RPL_ADMIN +}; + +struct st_mysql_methods; + +typedef struct st_mysql +{ + NET net; /* Communication parameters */ + gptr connector_fd; /* ConnectorFd for SSL */ + char *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info; + char *db; + struct charset_info_st *charset; + MYSQL_FIELD *fields; + MEM_ROOT field_alloc; + my_ulonglong affected_rows; + my_ulonglong insert_id; /* id if insert on table with NEXTNR */ + my_ulonglong extra_info; /* Used by mysqlshow */ + unsigned long thread_id; /* Id for connection in server */ + unsigned long packet_length; + unsigned int port; + unsigned long client_flag,server_capabilities; + unsigned int protocol_version; + unsigned int field_count; + unsigned int server_status; + unsigned int server_language; + unsigned int warning_count; + struct st_mysql_options options; + enum mysql_status status; + my_bool free_me; /* If free in mysql_close */ + my_bool reconnect; /* set to 1 if automatic reconnect */ + + /* session-wide random string */ + char scramble[SCRAMBLE_LENGTH+1]; + + /* + Set if this is the original connection, not a master or a slave we have + added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + */ + my_bool rpl_pivot; + /* + Pointers to the master, and the next slave connections, points to + itself if lone connection. + */ + struct st_mysql* master, *next_slave; + + struct st_mysql* last_used_slave; /* needed for round-robin slave pick */ + /* needed for send/read/store/use result to work correctly with replication */ + struct st_mysql* last_used_con; + + LIST *stmts; /* list of all statements */ + const struct st_mysql_methods *methods; + void *thd; + /* + Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + from mysql_stmt_close if close had to cancel result set of this object. + */ + my_bool *unbuffered_fetch_owner; +} MYSQL; + +typedef struct st_mysql_res { + my_ulonglong row_count; + MYSQL_FIELD *fields; + MYSQL_DATA *data; + MYSQL_ROWS *data_cursor; + unsigned long *lengths; /* column lengths of current row */ + MYSQL *handle; /* for unbuffered reads */ + MEM_ROOT field_alloc; + unsigned int field_count, current_field; + MYSQL_ROW row; /* If unbuffered read */ + MYSQL_ROW current_row; /* buffer to current row */ + my_bool eof; /* Used by mysql_fetch_row */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; + const struct st_mysql_methods *methods; +} MYSQL_RES; + +#define MAX_MYSQL_MANAGER_ERR 256 +#define MAX_MYSQL_MANAGER_MSG 256 + +#define MANAGER_OK 200 +#define MANAGER_INFO 250 +#define MANAGER_ACCESS 401 +#define MANAGER_CLIENT_ERR 450 +#define MANAGER_INTERNAL_ERR 500 + +#if !defined(MYSQL_SERVER) && !defined(MYSQL_CLIENT) +#define MYSQL_CLIENT +#endif + + +typedef struct st_mysql_manager +{ + NET net; + char *host,*user,*passwd; + unsigned int port; + my_bool free_me; + my_bool eof; + int cmd_status; + int last_errno; + char* net_buf,*net_buf_pos,*net_data_end; + int net_buf_size; + char last_error[MAX_MYSQL_MANAGER_ERR]; +} MYSQL_MANAGER; + +typedef struct st_mysql_parameters +{ + unsigned long *p_max_allowed_packet; + unsigned long *p_net_buffer_length; +} MYSQL_PARAMETERS; + +#if !defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY) +#define max_allowed_packet (*mysql_get_parameters()->p_max_allowed_packet) +#define net_buffer_length (*mysql_get_parameters()->p_net_buffer_length) +#endif + +/* + Set up and bring down the server; to ensure that applications will + work when linked against either the standard client library or the + embedded server library, these functions should be called. +*/ +int STDCALL mysql_server_init(int argc, char **argv, char **groups); +void STDCALL mysql_server_end(void); +/* + mysql_server_init/end need to be called when using libmysqld or + libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so + you don't need to call it explicitely; but you need to call + mysql_server_end() to free memory). The names are a bit misleading + (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general + names which suit well whether you're using libmysqld or libmysqlclient. We + intend to promote these aliases over the mysql_server* ones. +*/ +#define mysql_library_init mysql_server_init +#define mysql_library_end mysql_server_end + +MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void); + +/* + Set up and bring down a thread; these function should be called + for each thread in an application which opens at least one MySQL + connection. All uses of the connection(s) should be between these + function calls. +*/ +my_bool STDCALL mysql_thread_init(void); +void STDCALL mysql_thread_end(void); + +/* + Functions to get information from the MYSQL and MYSQL_RES structures + Should definitely be used if one uses shared libraries. +*/ + +my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res); +unsigned int STDCALL mysql_num_fields(MYSQL_RES *res); +my_bool STDCALL mysql_eof(MYSQL_RES *res); +MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res, + unsigned int fieldnr); +MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res); +MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res); +MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res); + +unsigned int STDCALL mysql_field_count(MYSQL *mysql); +my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql); +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql); +unsigned int STDCALL mysql_errno(MYSQL *mysql); +const char * STDCALL mysql_error(MYSQL *mysql); +const char *STDCALL mysql_sqlstate(MYSQL *mysql); +unsigned int STDCALL mysql_warning_count(MYSQL *mysql); +const char * STDCALL mysql_info(MYSQL *mysql); +unsigned long STDCALL mysql_thread_id(MYSQL *mysql); +const char * STDCALL mysql_character_set_name(MYSQL *mysql); +int STDCALL mysql_set_character_set(MYSQL *mysql, char *csname); + +MYSQL * STDCALL mysql_init(MYSQL *mysql); +my_bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, + const char *cert, const char *ca, + const char *capath, const char *cipher); +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db); +MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, + const char *user, + const char *passwd, + const char *db, + unsigned int port, + const char *unix_socket, + unsigned long clientflag); +int STDCALL mysql_select_db(MYSQL *mysql, const char *db); +int STDCALL mysql_query(MYSQL *mysql, const char *q); +int STDCALL mysql_send_query(MYSQL *mysql, const char *q, + unsigned long length); +int STDCALL mysql_real_query(MYSQL *mysql, const char *q, + unsigned long length); +MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql); +MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql); + +/* perform query on master */ +my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q, + unsigned long length); +my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, + unsigned long length); +/* perform query on slave */ +my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q, + unsigned long length); +my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, + unsigned long length); + +/* local infile support */ + +#define LOCAL_INFILE_ERROR_LEN 512 + +void +mysql_set_local_infile_handler(MYSQL *mysql, + int (*local_infile_init)(void **, const char *, + void *), + int (*local_infile_read)(void *, char *, + unsigned int), + void (*local_infile_end)(void *), + int (*local_infile_error)(void *, char*, + unsigned int), + void *); + +void +mysql_set_local_infile_default(MYSQL *mysql); + + +/* + enable/disable parsing of all queries to decide if they go on master or + slave +*/ +void STDCALL mysql_enable_rpl_parse(MYSQL* mysql); +void STDCALL mysql_disable_rpl_parse(MYSQL* mysql); +/* get the value of the parse flag */ +int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql); + +/* enable/disable reads from master */ +void STDCALL mysql_enable_reads_from_master(MYSQL* mysql); +void STDCALL mysql_disable_reads_from_master(MYSQL* mysql); +/* get the value of the master read flag */ +my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql); + +enum mysql_rpl_type STDCALL mysql_rpl_query_type(const char* q, int len); + +/* discover the master and its slaves */ +my_bool STDCALL mysql_rpl_probe(MYSQL* mysql); + +/* set the master, close/free the old one, if it is not a pivot */ +int STDCALL mysql_set_master(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); +int STDCALL mysql_add_slave(MYSQL* mysql, const char* host, + unsigned int port, + const char* user, + const char* passwd); + +int STDCALL mysql_shutdown(MYSQL *mysql, + enum mysql_enum_shutdown_level + shutdown_level); +int STDCALL mysql_dump_debug_info(MYSQL *mysql); +int STDCALL mysql_refresh(MYSQL *mysql, + unsigned int refresh_options); +int STDCALL mysql_kill(MYSQL *mysql,unsigned long pid); +int STDCALL mysql_set_server_option(MYSQL *mysql, + enum enum_mysql_set_option + option); +int STDCALL mysql_ping(MYSQL *mysql); +const char * STDCALL mysql_stat(MYSQL *mysql); +const char * STDCALL mysql_get_server_info(MYSQL *mysql); +const char * STDCALL mysql_get_client_info(void); +unsigned long STDCALL mysql_get_client_version(void); +const char * STDCALL mysql_get_host_info(MYSQL *mysql); +unsigned long STDCALL mysql_get_server_version(MYSQL *mysql); +unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql); +MYSQL_RES * STDCALL mysql_list_dbs(MYSQL *mysql,const char *wild); +MYSQL_RES * STDCALL mysql_list_tables(MYSQL *mysql,const char *wild); +MYSQL_RES * STDCALL mysql_list_processes(MYSQL *mysql); +int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, + const char *arg); +void STDCALL mysql_free_result(MYSQL_RES *result); +void STDCALL mysql_data_seek(MYSQL_RES *result, + my_ulonglong offset); +MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result, + MYSQL_ROW_OFFSET offset); +MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result, + MYSQL_FIELD_OFFSET offset); +MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result); +unsigned long * STDCALL mysql_fetch_lengths(MYSQL_RES *result); +MYSQL_FIELD * STDCALL mysql_fetch_field(MYSQL_RES *result); +MYSQL_RES * STDCALL mysql_list_fields(MYSQL *mysql, const char *table, + const char *wild); +unsigned long STDCALL mysql_escape_string(char *to,const char *from, + unsigned long from_length); +unsigned long STDCALL mysql_hex_string(char *to,const char *from, + unsigned long from_length); +unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql, + char *to,const char *from, + unsigned long length); +void STDCALL mysql_debug(const char *debug); +char * STDCALL mysql_odbc_escape_string(MYSQL *mysql, + char *to, + unsigned long to_length, + const char *from, + unsigned long from_length, + void *param, + char * + (*extend_buffer) + (void *, char *to, + unsigned long *length)); +void STDCALL myodbc_remove_escape(MYSQL *mysql,char *name); +unsigned int STDCALL mysql_thread_safe(void); +my_bool STDCALL mysql_embedded(void); +MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con); +MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con, + const char* host, + const char* user, + const char* passwd, + unsigned int port); +void STDCALL mysql_manager_close(MYSQL_MANAGER* con); +int STDCALL mysql_manager_command(MYSQL_MANAGER* con, + const char* cmd, int cmd_len); +int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con, + char* res_buf, + int res_buf_size); +my_bool STDCALL mysql_read_query_result(MYSQL *mysql); + + +/* + The following definitions are added for the enhanced + client-server protocol +*/ + +/* statement state */ +enum enum_mysql_stmt_state +{ + MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE, + MYSQL_STMT_FETCH_DONE +}; + + +/* bind structure */ +typedef struct st_mysql_bind +{ + unsigned long *length; /* output length pointer */ + my_bool *is_null; /* Pointer to null indicator */ + void *buffer; /* buffer to get/put data */ + enum enum_field_types buffer_type; /* buffer type */ + unsigned long buffer_length; /* buffer length, must be set for str/binary */ + + /* Following are for internal use. Set by mysql_stmt_bind_param */ + unsigned char *inter_buffer; /* for the current data position */ + unsigned long offset; /* offset position for char/binary fetch */ + unsigned long internal_length; /* Used if length is 0 */ + unsigned int param_number; /* For null count and error messages */ + unsigned int pack_length; /* Internal length for packed data */ + my_bool is_unsigned; /* set if integer type is unsigned */ + my_bool long_data_used; /* If used with mysql_send_long_data */ + my_bool internal_is_null; /* Used if is_null is 0 */ + void (*store_param_func)(NET *net, struct st_mysql_bind *param); + void (*fetch_result)(struct st_mysql_bind *, unsigned char **row); + void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *, + unsigned char **row); +} MYSQL_BIND; + + +/* statement handler */ +typedef struct st_mysql_stmt +{ + MEM_ROOT mem_root; /* root allocations */ + LIST list; /* list to keep track of all stmts */ + MYSQL *mysql; /* connection handle */ + MYSQL_BIND *params; /* input parameters */ + MYSQL_BIND *bind; /* output parameters */ + MYSQL_FIELD *fields; /* result set metadata */ + MYSQL_DATA result; /* cached result set */ + MYSQL_ROWS *data_cursor; /* current row in cached result */ + /* copy of mysql->affected_rows after statement execution */ + my_ulonglong affected_rows; + my_ulonglong insert_id; /* copy of mysql->insert_id */ + /* + mysql_stmt_fetch() calls this function to fetch one row (it's different + for buffered, unbuffered and cursor fetch). + */ + int (*read_row_func)(struct st_mysql_stmt *stmt, + unsigned char **row); + unsigned long stmt_id; /* Id for prepared statement */ + unsigned int last_errno; /* error code */ + unsigned int param_count; /* input parameter count */ + unsigned int field_count; /* number of columns in result set */ + enum enum_mysql_stmt_state state; /* statement state */ + char last_error[MYSQL_ERRMSG_SIZE]; /* error message */ + char sqlstate[SQLSTATE_LENGTH+1]; + /* Types of input parameters should be sent to server */ + my_bool send_types_to_server; + my_bool bind_param_done; /* input buffers were supplied */ + my_bool bind_result_done; /* output buffers were supplied */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; + /* + Is set to true if we need to calculate field->max_length for + metadata fields when doing mysql_stmt_store_result. + */ + my_bool update_max_length; +} MYSQL_STMT; + +enum enum_stmt_attr_type +{ + /* + When doing mysql_stmt_store_result calculate max_length attribute + of statement metadata. This is to be consistent with the old API, + where this was done automatically. + In the new API we do that only by request because it slows down + mysql_stmt_store_result sufficiently. + */ + STMT_ATTR_UPDATE_MAX_LENGTH +}; + + +typedef struct st_mysql_methods +{ + my_bool (*read_query_result)(MYSQL *mysql); + my_bool (*advanced_command)(MYSQL *mysql, + enum enum_server_command command, + const char *header, + unsigned long header_length, + const char *arg, + unsigned long arg_length, + my_bool skip_check); + MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + unsigned int fields); + MYSQL_RES * (*use_result)(MYSQL *mysql); + void (*fetch_lengths)(unsigned long *to, + MYSQL_ROW column, unsigned int field_count); + void (*flush_use_result)(MYSQL *mysql); +#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) + MYSQL_FIELD * (*list_fields)(MYSQL *mysql); + my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); + int (*stmt_execute)(MYSQL_STMT *stmt); + int (*read_binary_rows)(MYSQL_STMT *stmt); + int (*unbuffered_fetch)(MYSQL *mysql, char **row); + void (*free_embedded_thd)(MYSQL *mysql); + const char *(*read_statistics)(MYSQL *mysql); + my_bool (*next_result)(MYSQL *mysql); + int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd); +#endif +} MYSQL_METHODS; + + +MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql); +int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, + unsigned long length); +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, + unsigned int column, + unsigned long offset); +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt); +unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, + enum enum_stmt_attr_type attr_type, + const void *attr); +my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, + enum enum_stmt_attr_type attr_type, + void *attr); +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +my_bool STDCALL mysql_stmt_close(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt); +my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, + unsigned int param_number, + const char *data, + unsigned long length); +MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt); +MYSQL_RES *STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt); +unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT * stmt); +const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt); +const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT * stmt); +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt, + MYSQL_ROW_OFFSET offset); +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt); +void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset); +my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt); +my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt); +my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt); +unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt); + +my_bool STDCALL mysql_commit(MYSQL * mysql); +my_bool STDCALL mysql_rollback(MYSQL * mysql); +my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode); +my_bool STDCALL mysql_more_results(MYSQL *mysql); +int STDCALL mysql_next_result(MYSQL *mysql); +void STDCALL mysql_close(MYSQL *sock); + + +/* status return codes */ +#define MYSQL_NO_DATA 100 + +#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT) + +#ifdef USE_OLD_FUNCTIONS +MYSQL * STDCALL mysql_connect(MYSQL *mysql, const char *host, + const char *user, const char *passwd); +int STDCALL mysql_create_db(MYSQL *mysql, const char *DB); +int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); +#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT) +#endif +#define HAVE_MYSQL_REAL_CONNECT + +/* + The following functions are mainly exported because of mysqlbinlog; + They are not for general usage +*/ + +#define simple_command(mysql, command, arg, length, skip_check) \ + (*(mysql)->methods->advanced_command)(mysql, command, \ + NullS, 0, arg, length, skip_check) +unsigned long net_safe_read(MYSQL* mysql); + +#ifdef __NETWARE__ +#pragma pack(pop) /* restore alignment */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _mysql_h */ diff --git a/Src/Common/rosecrypt.cpp b/Src/Common/rosecrypt.cpp new file mode 100644 index 0000000..46e42d6 --- /dev/null +++ b/Src/Common/rosecrypt.cpp @@ -0,0 +1,530 @@ +#include +#include +#include "rosecrypt.hpp" +#include + +#define LOGINSERVER_CRYPTTABLE_START_VALUE 0x0042D266 + +//------------------------------------------------------------------------- +// This structure saves values while the tables are generated +//------------------------------------------------------------------------- +struct CCryptTableControlStruct +{ + unsigned CountTableMod[4]; + unsigned char CurTableMod ; + unsigned ValueTableMod[4]; +}; + +//------------------------------------------------------------------------ +// This structure is used by the encryption routines +//------------------------------------------------------------------------- +typedef unsigned long long int u64; + +struct CCryptBuffer +{ + u64 AddBufferLen : 11; + u64 Command : 11; + u64 AddTableValue : 11; + u64 EncryptAddValue : 4; + u64 EncryptValue : 3; +}; + +//------------------------------------------------------------------------- +// This table is used to calculate a checksum (size of 256 bytes) +//------------------------------------------------------------------------- +const unsigned char CheckSumTable[] = + "\x00\x5E\xBC\xE2\x61\x3F\xDD\x83\xC2\x9C\x7E\x20\xA3\xFD\x1F\x41" + "\x9D\xC3\x21\x7F\xFC\xA2\x40\x1E\x5F\x01\xE3\xBD\x3E\x60\x82\xDC" + "\x23\x7D\x9F\xC1\x42\x1C\xFE\xA0\xE1\xBF\x5D\x03\x80\xDE\x3C\x62" + "\xBE\xE0\x02\x5C\xDF\x81\x63\x3D\x7C\x22\xC0\x9E\x1D\x43\xA1\xFF" + "\x46\x18\xFA\xA4\x27\x79\x9B\xC5\x84\xDA\x38\x66\xE5\xBB\x59\x07" + "\xDB\x85\x67\x39\xBA\xE4\x06\x58\x19\x47\xA5\xFB\x78\x26\xC4\x9A" + "\x65\x3B\xD9\x87\x04\x5A\xB8\xE6\xA7\xF9\x1B\x45\xC6\x98\x7A\x24" + "\xF8\xA6\x44\x1A\x99\xC7\x25\x7B\x3A\x64\x86\xD8\x5B\x05\xE7\xB9" + "\x8C\xD2\x30\x6E\xED\xB3\x51\x0F\x4E\x10\xF2\xAC\x2F\x71\x93\xCD" + "\x11\x4F\xAD\xF3\x70\x2E\xCC\x92\xD3\x8D\x6F\x31\xB2\xEC\x0E\x50" + "\xAF\xF1\x13\x4D\xCE\x90\x72\x2C\x6D\x33\xD1\x8F\x0C\x52\xB0\xEE" + "\x32\x6C\x8E\xD0\x53\x0D\xEF\xB1\xF0\xAE\x4C\x12\x91\xCF\x2D\x73" + "\xCA\x94\x76\x28\xAB\xF5\x17\x49\x08\x56\xB4\xEA\x69\x37\xD5\x8B" + "\x57\x09\xEB\xB5\x36\x68\x8A\xD4\x95\xCB\x29\x77\xF4\xAA\x48\x16" + "\xE9\xB7\x55\x0B\x88\xD6\x34\x6A\x2B\x75\x97\xC9\x4A\x14\xF6\xA8" + "\x74\x2A\xC8\x96\x15\x4B\xA9\xF7\xB6\xE8\x0A\x54\xD7\x89\x6B\x35"; + +//---------------------------------------------------------------------------- +// Private function, that generates the next value for the tables +//---------------------------------------------------------------------------- +unsigned ctGenerateNextValue( CCryptTable *CryptTables, CCryptTableControlStruct *ControlStruct ) +{ + // Get the last value depending on the current TableMod. + int Value = ControlStruct->ValueTableMod[ ControlStruct->CurTableMod ]; + + // Update generatecount, depending on the current TableMod + ControlStruct->CountTableMod [ ControlStruct->CurTableMod ]; + + // Calculate and return the next value + switch ( ControlStruct->CurTableMod ) + { + case 0: + Value = (int(Value * 0x8088405)) + 1; + ControlStruct->ValueTableMod[ 0 ] = Value; + return int(Value + (Value<0?0xFFFF:0)) >> 0x10; + case 1: + Value = (0x3039 - (int(Value * 0x3E39B193))) & 0x7FFFFFFF; + ControlStruct->ValueTableMod[ 1 ] = Value; + return Value; + case 2: + Value = (int(Value * 0x41C64E6D) + 0x3039); + ControlStruct->ValueTableMod[ 2 ] = Value; + return int(Value + (Value<0?0xFFFF:0)) >> 0x10; + case 3: + Value = (int(Value * 0x343FD) + 0x269EC3) & 0x7FFFFFFF; + ControlStruct->ValueTableMod[ 3 ] = Value; + return int(Value + (Value<0?0xFFFF:0)) >> 0x10; + default: + return 0; + } + + return 0; +} + +//------------------------------------------------------------------------- +// Private functions, that searches a value in an existing table to +// check if this value already exists. +//------------------------------------------------------------------------- +bool ctValueExistsInTable( CCryptTable *CryptTables, unsigned char Table, unsigned Value, unsigned short EntryCount ) +{ + for ( unsigned short i=0; iTables[ Table ][ i ] == Value ) return true; + return false; +} + +//------------------------------------------------------------------------- +// This function generates a new crypt table depending on the ModValue +// parameter. +//------------------------------------------------------------------------- +void GenerateCryptTables( CCryptTable* &CryptTables, unsigned ModValue ) +{ + CCryptTableControlStruct ControlStruct; + unsigned char TableFuncList[ 17 ]; + unsigned char table; + unsigned short entry; + unsigned value; + int ivalue; + + // Allocate memory for structure + CryptTables = (CCryptTable*)malloc( sizeof( CCryptTable ) ); + CryptTables->Tables = (unsigned **)calloc( 16, sizeof( unsigned ) ); + + // Prepare values + ModValue = (int(ModValue * 0x41C64E6D) + 0x3039); + ModValue = int(ModValue + ((int)ModValue<0?0xFFFF:0)) >> 0x10; + + // Startvalue for checksum variable for each user + CryptTables->EncryptionStartValue = ModValue; + + // set startvalues + for ( table=0; table<4; ++table ) + { + ControlStruct.CountTableMod[ table ] = 0; + ControlStruct.ValueTableMod[ table ] = ModValue; + } + + // Generate TableMod list for creation + ControlStruct.CurTableMod = 0; + for ( table=0; table<0x11; ++table ) + { + ivalue = ctGenerateNextValue( CryptTables, &ControlStruct ) & 0x80000003; + if ( ivalue<0 ) ivalue = ( ( ivalue - 1 ) | 0xFFFFFFFC ) + 1; + TableFuncList[ table ] = ( unsigned char )ivalue; + } + + // Create tables + for ( table=0; table<16; ++table ) + { + // Prepare creation loop for current table + CryptTables->Tables[ table ] = (unsigned*)calloc( 0x800, sizeof( unsigned ) ); + ControlStruct.CurTableMod = TableFuncList[ table ] & 3; + + // Generate current table + entry = 0; + do + { + // Generate next value + value = ctGenerateNextValue( CryptTables, &ControlStruct ); + + // Check, if value already exist in table 0 + if ( ctValueExistsInTable( CryptTables, 0, value, entry ) ) continue; + + // Save value to memory + CryptTables->Tables[ table ][ entry ] = value; + + ++entry; + } while ( entry<0x800 ); + } + + // Generate additional table + CryptTables->AddTable = (unsigned short*)calloc( 0x200, sizeof( unsigned short ) ); + ControlStruct.CurTableMod = table = (unsigned char)(TableFuncList[ 0x10 ]) & 3; + entry = 0; + do + { + // Generate next value + value = ctGenerateNextValue( CryptTables, &ControlStruct ) & 0x7FF; + + // Check, if value already exist + for ( ivalue=0; ivalue<=(int)entry; ++ivalue ) + if ( (unsigned)CryptTables->AddTable[ivalue] == value ) { value=0xFFFFFFFF; break; } + + // If value exist, continue + if ( value==0xFFFFFFFF ) continue; + + // Save value + CryptTables->AddTable[ entry++ ] = value; + } while ( entry<0x200 ); +} + +//------------------------------------------------------------------------- +// This function generates a new crypt table used by the Login Server +//------------------------------------------------------------------------- +void GenerateLoginServerCryptTables( CCryptTable* &CryptTables ) +{ + CCryptTableControlStruct ControlStruct; + unsigned char table; + unsigned short entry; + unsigned value; + int ivalue; + + // Allocate memory for structure + CryptTables = (CCryptTable*)malloc( sizeof( CCryptTable ) ); + CryptTables->Tables = (unsigned **)calloc( 16, sizeof( unsigned ) ); + + // Set default startvalues + for ( table=0; table<4; ++table ) + { + ControlStruct.CountTableMod[ table ] = 0; + ControlStruct.ValueTableMod[ table ] = LOGINSERVER_CRYPTTABLE_START_VALUE; + } + + // Another startvalue + CryptTables->EncryptionStartValue = LOGINSERVER_CRYPTTABLE_START_VALUE; + + // Generate default tables + for ( table=0; table<16; ++table ) + { + // Prepare creation loop for current table + ControlStruct.CurTableMod = table & 3; + CryptTables->Tables[ table ] = (unsigned*)calloc( 0x800, sizeof( unsigned ) ); + + entry = 0; + do + { + // Generate next value depend on CurTableMod + value = ctGenerateNextValue( CryptTables, &ControlStruct ); + + // Only take value, if it doesn't exist in table 0 already + if ( ctValueExistsInTable( CryptTables, 0, value, entry ) ) continue; + + // Save value to structure + CryptTables->Tables[ table ][ entry ] = value; + + ++entry; + } while ( entry<0x800 ); + } + + // Create additional table + CryptTables->AddTable = (unsigned short*)calloc( 0x200, sizeof( unsigned short ) ); + entry = 0; + do + { + // Generate next value for table + ControlStruct.CountTableMod[1]; + ivalue = ControlStruct.ValueTableMod[1]; + ivalue *= 0x3E39B193; + value = (0x3039 - (unsigned)ivalue) & 0x7FFFFFFF; + ControlStruct.ValueTableMod[1] = value; + + // Don't save, if value already exist + value &= 0x7FF; + for ( ivalue=0; ivalue<=(int)entry; ++ivalue ) + if ( (unsigned)CryptTables->AddTable[ivalue] == value ) { value=0xFFFFFFFF; break; } + + // If value already exist, continue + if ( value==0xFFFFFFFF ) continue; + + // Save value to structure + CryptTables->AddTable[ entry ] = value; + + ++entry; + } while ( entry<0x200 ); +} + +//------------------------------------------------------------------------- +// This function frees the memory of a crypt table +//------------------------------------------------------------------------- +void FreeCryptTables( CCryptTable* &CryptTables ) +{ + unsigned char c; + + // Free every table + for ( c=0; c<16; ++c ) + free( CryptTables->Tables[ c ] ); + + // Free memory + free( CryptTables->AddTable ); + free( CryptTables->Tables ); + free( CryptTables ); + + CryptTables = NULL; +} + +//------------------------------------------------------------------------- +// This function decrypts a received 6-byte buffer and returns the full +// size of the incoming packet (including this 6-byte header). +// If the buffer is incorrect, this function returns a zero. +//------------------------------------------------------------------------- +int DecryptBufferHeader( CCryptStatus *ri, CCryptTable *CryptTables, unsigned char *Buffer ) +{ + CCryptBuffer TempBuffer; + unsigned BigBuffer; + unsigned char c; + + // Read "AddTableValue" from buffer + BigBuffer = *((unsigned*)Buffer); + TempBuffer.AddTableValue = ( ( Buffer[4] >> 0x03 ) & 0x0007 ) | ( ( BigBuffer ) & 0x0038 ) | \ + ( ( BigBuffer >> 0x03 ) & 0x01C0 ) | ( ( BigBuffer >> 0x11 ) & 0x0600 ); + + // Decrypt header in the buffer + for ( c=0; c<5; ++c ) + Buffer[c] ^= CryptTables->Tables[ c ][ TempBuffer.AddTableValue ]; + + BigBuffer = *((unsigned*)Buffer); + TempBuffer.EncryptValue = ( BigBuffer >> 0x0F ) & 0x0007 ; + TempBuffer.Command = ( ( BigBuffer >> 0x0C ) & 0x0007 ) | ( ( BigBuffer << 0x03 ) & 0x0038 ) | \ + ( ( Buffer[4] << 0x06 ) & 0x01C0 ) | ( ( BigBuffer >> 0x13 ) & 0x0600 ); + TempBuffer.AddBufferLen = ( ( BigBuffer >> 0x06 ) & 0x0007 ) | ( ( BigBuffer >> 0x0F ) & 0x0038 ) | \ + ( ( BigBuffer >> 0x11 ) & 0x01C0 ) | ( ( Buffer[4] << 0x03 ) & 0x0600 ); + TempBuffer.EncryptAddValue = ( ( BigBuffer >> 0x1E ) & 0x0003 ) | ( ( BigBuffer >> 0x13 ) & 0x000C ); + + // Size correct? + if ( TempBuffer.AddBufferLen < 0x06 || TempBuffer.AddBufferLen > 0x400 ) + return 0; + + // Header Checksum correct? ~ not implented yet ~ +/* + if ( ((unsigned)( TempBuffer.AddTableValue + TempBuffer.EncryptAddValue ) & 7 ) != \ + ((unsigned)( TempBuffer.EncryptValue ) & 7 ) ) + return 0; +*/ + + // Save CryptBuffer temporary to buffer for later use + *((unsigned*)Buffer) = *((unsigned *)&TempBuffer) ; + Buffer[4] = ((unsigned char*)&TempBuffer)[4]; + + // Update encryption value + ri->CurEncryptionValue = ( ri->CurEncryptionValue * 0x343FD ) + 0x269EC3; + + // Everything went ok + return TempBuffer.AddBufferLen; +} + +//------------------------------------------------------------------------- +// This function will decrypt the full buffer after it is received. +// If it fails, it will return FALSE. +//------------------------------------------------------------------------- +bool DecryptBufferData( CCryptTable *CryptTables, unsigned char *Buffer ) +{ + CCryptBuffer TempBuffer; + unsigned short c, BufferLen; + unsigned char CheckSum; + + // Read CryptBuffer from received buffer + *((unsigned *)&TempBuffer) = *((unsigned*)Buffer); + ((unsigned char*)&TempBuffer)[4] = Buffer[4]; + + // Calculate CheckSum for bufferheader + for ( CheckSum=0, c=0; c<5; ++c ) + CheckSum = CheckSumTable[ (unsigned char)Buffer[ c ] ^ CheckSum ]; + + // Calculate real buffer length and check it + BufferLen = (unsigned short)(TempBuffer.AddBufferLen - TempBuffer.EncryptValue); + if ( BufferLen < 0x06 ) return false; + + // Decrypt buffer data + if ( BufferLen > 0x06 ) + { + for ( c=6; cTables[ (TempBuffer.EncryptAddValue + c) & 0x000F ] \ + [ (TempBuffer.AddTableValue + c) & 0x07FF ]; + CheckSum = CheckSumTable [ (unsigned char)Buffer[ c ] ^ CheckSum ]; + } + } + + // Check CheckSum + if ( Buffer[5] != CheckSum ) + return false; + + // Complete buffer + *((unsigned short*)(Buffer )) = BufferLen ; + *((unsigned short*)(Buffer+2)) = TempBuffer.Command; + + return true; +} + +//------------------------------------------------------------------------- +// This functions will encrypt a buffer. The buffer must have at least a +// size of 6 bytes. The first two bytes must be the size of the buffer. +//------------------------------------------------------------------------- +void EncryptBuffer( CCryptTable *CryptTables, unsigned char *Buffer ) +{ + CCryptBuffer TempBuffer; + unsigned short c; + unsigned char CheckSum; + unsigned EncryptValue; + + // Generate some random values for encrypting + TempBuffer.AddTableValue = (rand( ) % 0x800); + TempBuffer.EncryptAddValue = (rand( ) % 0x10 ); + TempBuffer.EncryptValue = TempBuffer.AddTableValue + TempBuffer.EncryptAddValue; + + // Fill other fields with values from Buffer + TempBuffer.AddBufferLen = *((unsigned short*)(Buffer )); + TempBuffer.Command = *((unsigned short*)(Buffer+2)); + + // Build new bufferheader + + Buffer[4] = (((unsigned)TempBuffer.AddBufferLen ) & 0x00000007 ) ^ \ + (((unsigned)TempBuffer.Command >> 0x03 ) & 0x000000C0 ); + EncryptValue = (((unsigned)TempBuffer.AddBufferLen >> 0x03 ) & 0x00000007 ); + EncryptValue |= (((unsigned)TempBuffer.AddBufferLen << 0x06 ) & 0x00007000 ); + EncryptValue |= (((unsigned)TempBuffer.AddBufferLen << 0x13 ) & 0x30000000 ); + EncryptValue |= (((unsigned)TempBuffer.Command << 0x17 ) & 0x03800000 ); + EncryptValue |= (((unsigned)TempBuffer.Command << 0x0F ) & 0x001C0000 ); + EncryptValue |= (((unsigned)TempBuffer.Command ) & 0x000001C0 ); + EncryptValue |= (((unsigned)TempBuffer.EncryptValue << 0x09 ) & 0x00000E00 ); + EncryptValue |= (((unsigned)TempBuffer.EncryptAddValue << 0x1A ) & 0x0C000000 ); + EncryptValue |= (((unsigned)TempBuffer.EncryptAddValue << 0x1C ) & 0xC0000000 ); + + *((unsigned*)Buffer) = EncryptValue; + + // Encrypt bufferheader + for ( CheckSum=0, c=0; c<5; ++c ) + { + CheckSum = CheckSumTable [ ((unsigned char*)&TempBuffer)[ c ] ^ CheckSum ]; + Buffer[c] ^= CryptTables->Tables[ c ][ TempBuffer.AddTableValue ]; + } + + + // Encrypt bufferdata if exists + if ( TempBuffer.AddBufferLen > 0x06 ) + { + for ( c=6; cTables[ (TempBuffer.EncryptAddValue + c) & 0x000F ] \ + [ (TempBuffer.AddTableValue + c) & 0x07FF ]; + } + } + Buffer[ 5 ] = CheckSum; + + // Complete bufferheader + EncryptValue = *((unsigned*)Buffer) & 0xFF9C7FC7; + EncryptValue |= (((unsigned)TempBuffer.AddTableValue << 0x03 ) & 0x00000038 ); + EncryptValue |= (((unsigned)TempBuffer.AddTableValue << 0x09 ) & 0x00038000 ); + EncryptValue |= (((unsigned)TempBuffer.AddTableValue << 0x0C ) & 0x00600000 ); + Buffer[ 4 ] = (Buffer[4]&0xC7) | (((unsigned)TempBuffer.AddTableValue ) & 0x38 ); + *((unsigned*)Buffer) = EncryptValue; +} + +//------------------------------------------------------------------------- +bool CryptISCPak( unsigned char* pak ) +{ + unsigned paksize = *((unsigned short*)&pak[0]); + for( unsigned short i = 2; i < paksize; i ++ ) + pak[i] ^= 0x81 * i * paksize; + return true; +} + +#ifdef USE124 +// This encryption code was taken from the dRose source. Not sure who to credit other than them. +// To use 124 you will need the 124 trose.exe in your server directory. +int buildChecksum(char* csum, char* path) +{ + char buf[0x200]; + int bytesread=0; + FILE* fh; + + if (path==0) path = "TRose.exe"; + fh = fopen( path, "rb" ); + if ( fh == NULL ) return -1; + + fseek(fh, -512, SEEK_END); + bytesread = fread(buf, 1, 0x200, fh); + fclose(fh); + + if (bytesread!=0x200) return -2; + + short keylen = buf[0x0f]; + char* key = new char[keylen+1]; + unsigned short key_ct[] = {0x94, 0x20, 0x5F, 0x53, 0x14, 0xF2, 0x61, 0x11, 0x54, 0xFF, 0x72, 0x99, 0x44, 0xF4, 0xA1, 0xAA, 0xBA, 0xC2, 0x54, 0x52, 0x6F, 0x73, 0x65, 0x2E, 0x65, 0x78, 0x65}; + + for(int i=0; i0; i--) { + int tmp = (key[i-1]-0x30)*multiplier; + multiplier *= 0x0a; + csumlen += tmp; + } + + delete [] key; + + unsigned short csumtable[] = {0x34, 0x5C, 0x3D, 0x52, 0xCE, 0xF3, 0x12, 0xD4, 0x05, 0x91, 0xEE, 0xFF, 0x49, 0xC2, 0xD2}; + + int tmp=0; + for(int i=0; i +#include "seh.hpp" + +#ifdef __WIN32__ + +EXCEPTION_DISPOSITION __SEH_HANDLER::ExceptionRouter(PEXCEPTION_RECORD pRecord, + __SEH_EXCEPTION_REGISTRATION* pReg, + PCONTEXT pContext, + PEXCEPTION_RECORD pRecord2) +{ + return pReg->exthandler->ExceptionHandler(pRecord, pReg, pContext, pRecord2); +} + +EXCEPTION_DISPOSITION __SEH_HANDLER::ExceptionHandler(PEXCEPTION_RECORD pRecord, + __SEH_EXCEPTION_REGISTRATION* pReg, + PCONTEXT pContext, + PEXCEPTION_RECORD pRecord2) +{ + /*std::memcpy(&excContext, pContext, sizeof(_CONTEXT)); + std::memcpy(&excRecord, pRecord, sizeof(_EXCEPTION_RECORD));*/ + memcpy(&excContext, pContext, sizeof(_CONTEXT)); + memcpy(&excRecord, pRecord, sizeof(_EXCEPTION_RECORD)); + longjmp(context, 1); +} + +#endif + + diff --git a/Src/Common/seh.hpp b/Src/Common/seh.hpp new file mode 100644 index 0000000..2f9362a --- /dev/null +++ b/Src/Common/seh.hpp @@ -0,0 +1,96 @@ + +#ifndef __FV_SEH_H__ +#define __FV_SEH_H__ + +#ifdef __WIN32__ +#include +#include +#endif + +#include +//#include + +//NAMESPACE_BEGIN(PU) +//NAMESPACE_BEGIN(SEH) + +#ifdef __WIN32__ + +class __SEH_HANDLER; + +typedef struct tag__SEH_EXCEPTION_REGISTRATION +{ + tag__SEH_EXCEPTION_REGISTRATION* prev; + PEXCEPTION_HANDLER handler; + __SEH_HANDLER* exthandler; +} __SEH_EXCEPTION_REGISTRATION; + + +class __SEH_HANDLER +{ +public: + __SEH_HANDLER() { } + static EXCEPTION_DISPOSITION ExceptionRouter(PEXCEPTION_RECORD pRecord, + __SEH_EXCEPTION_REGISTRATION* pReg, + PCONTEXT pContext, + PEXCEPTION_RECORD pRecord2); + + virtual EXCEPTION_DISPOSITION ExceptionHandler(PEXCEPTION_RECORD pRecord, + __SEH_EXCEPTION_REGISTRATION* pReg, + PCONTEXT pContext, + PEXCEPTION_RECORD pRecord2); + + jmp_buf context; + EXCEPTION_RECORD excRecord; + CONTEXT excContext; +}; + +#define __seh_try \ +{ \ + __SEH_EXCEPTION_REGISTRATION _lseh_er; \ + __SEH_HANDLER _lseh_handler; \ + \ + _lseh_er.handler = \ + reinterpret_cast(__SEH_HANDLER::ExceptionRouter); \ + _lseh_er.exthandler = &_lseh_handler; \ + asm volatile ("movl %%fs:0, %0" : "=r" (_lseh_er.prev)); \ + asm volatile ("movl %0, %%fs:0" : : "r" (&_lseh_er)); \ + int _lseh_setjmp_res = setjmp(_lseh_handler.context); \ + while(true) { \ + if(_lseh_setjmp_res != 0) { \ + break; \ + } \ + +#define __seh_finally \ + break; \ + } \ + while(true) { + + +#define __seh_except(rec, ctx) \ + break; \ + } \ + PEXCEPTION_RECORD rec = &_lseh_handler.excRecord; \ + PCONTEXT ctx = &_lseh_handler.excContext; \ + \ + asm volatile ("movl %0, %%fs:0" : : "r" (_lseh_er.prev)); \ + if(_lseh_setjmp_res != 0) + +#define __seh_end } + +#else + +#define __seh_try + +#define __seh_except(rec, ctx) if(false) { + +#define __seh_end } + +#define __seh_finally + +#endif + +//NAMESPACE_END(SEH) +//NAMESPACE_END(PU) + +#endif + diff --git a/Src/Common/sockets.h b/Src/Common/sockets.h new file mode 100644 index 0000000..68f578d --- /dev/null +++ b/Src/Common/sockets.h @@ -0,0 +1,438 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +//#define _CRT_NO_TIME_T +#ifndef __ROSE_SOCKETS__ +#define __ROSE_SOCKETS__ +#define USEIFO +#ifdef _WIN32 + + #include + #define close closesocket + #ifdef FD_SETSIZE + #undef FD_SETSIZE + #endif + #define FD_SETSIZE 1024 +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #define SOCKET int + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #define PVOID void * + #define ioctlsocket ioctl + #define SOCKADDR struct sockaddr + #define closesocket close +#endif + +#include "mysql.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pthread.h" +#include +#include +#include "log.h" +#include "rosecrypt.hpp" +#include "config.h" +#include "database/database.h" +#pragma comment(lib, "Ws2_32.lib") +// Just some defs for easier use +typedef char SBYTE; +typedef unsigned char BYTE; +typedef short SWORD; +typedef unsigned short WORD; +typedef long SDWORD; +typedef unsigned long DWORD; +typedef long long SQWORD; +typedef unsigned long long QWORD; +typedef unsigned __int8 byte; +typedef unsigned __int16 word; +typedef unsigned __int32 dword; +typedef unsigned __int64 qword; +typedef char* strings; +using std::cout; +using std::endl; +using std::system; +using std::vector; +using std::map; +using std::nothrow; +using std::string; +#define STARTPACKET(p,c,s) CPacket p; p.Command = c; p.Size = s; +#define SETBYTE(p,o,v) p.Buffer[o]=v; +#define SETWORD(p,o,v) *((unsigned short*)&p.Buffer[o])=v; +#define SETDWORD(p,o,v) *((unsigned*)&p.Buffer[o])=v; +#define SETQWORD(p,o,v) *((long long*)&p.Buffer[o])=v; +#define SETFLOAT(p,o,v) *((float*)&p.Buffer[o])=v; + +#define BEGINPACKET(p,c) CPacket p; p.Command = c; p.Size = 6; +#define RESETPACKET(p,c) p.Command = c; p.Size = 6; +#define ADDBYTE(p,v) { p.Buffer[p.Size-6]=v; p.Size+=1; } +#define ADDWORD(p,v) { *((WORD*)&p.Buffer[p.Size-6])=v; p.Size+=2; } +#define ADDDWORD(p,v) { *((unsigned*)&p.Buffer[p.Size-6])=v; p.Size+=4; } +#define ADDQWORD(p,v) { *((QWORD*)&p.Buffer[p.Size-6])=v; p.Size+=8; } +#define ADDFLOAT(p,v) { *((float*)&p.Buffer[p.Size-6])=v; p.Size+=4; } +#define ADDSTRING(p,v) { for(int q=0; q<(int)strlen((char*)v); q++) ADDBYTE(p,(v)[q]); } +#define GETBYTE(p,o) (p.Buffer[o]) +#define GETWORD(p,o) *((WORD*)&p.Buffer[o]) +#define GETDWORD(p,o) *((DWORD*)&p.Buffer[o]) +#define GETQWORD(p,o) *((QWORD*)&p.Buffer[o]) +#define GETFLOAT(p,o) *((float*)&p.Buffer[o]) +#define pi 3.1415926535897932384626433832795 +#define r2d pi/180 +#define ZONE_SIZE 6400 +#define ENTITY_NO_TYPE 0 +#define ENTITY_PLAYER 1 +#define ENTITY_MONSTER 2 +#define ENTITY_NPC 3 +#define ENTITY_SUMMON 4 +#define ENTITY_DROP 5 + +// Structures +// Packet information +struct CPacket +{ + unsigned short Size; // Packet size + unsigned short Command; // Packet command + unsigned short Unused; // unused + unsigned char Buffer[0x400]; // Packet data //0x600 + + CPacket( unsigned short mycommand=0, unsigned short mysize=6, unsigned short myunused=0 ); + ~CPacket( ); + void StartPacket( unsigned short mycommand, unsigned short mysize=6, unsigned short myunused=0 ); + void AddByte( unsigned char value ); + void AddWord( unsigned short value ); + void AddDWord( unsigned value ); + void AddQWord( unsigned long long value ); + void AddFloat( float value ); + void AddString( char* value ); + void SetByte( unsigned short pos, unsigned char value ); + void SetWord( unsigned short pos, unsigned short value ); + void SetDWord( unsigned short pos, unsigned value ); + void SetQWord( unsigned short pos, unsigned long long value ); + void SetFloat( unsigned short pos, float value ); + unsigned char GetByte( unsigned short pos ); + unsigned short GetWord( unsigned short pos ); + unsigned GetDWord( unsigned short pos ); + unsigned long long GetQWord( unsigned short pos ); + float GetFloat( unsigned short pos ); + char* GetString( unsigned short pos ); + + // Functions added by Drakia + template void Add( T value ) { + *((T*)&Buffer[Size]) = value; + Size += sizeof(T); + } + void AddString( char* value, bool NullTerminate ) { + for (dword i = 0; i < strlen((const char*)value); i++) { + Add(value[i]); + } + if (NullTerminate) Add(0); + } + template void AddString( char* value ) { + Add(strlen((const char*)value)); + AddString(value, false); + } + void AddBytes( byte* value, dword len ) { + for (dword i = 0; i < len; i++) + Add((byte)value[i]); + } +}; + +// SQL Configuration +struct CROSEServerConfigSQL +{ + char* pcServer; // IP + char* pcDatabase; // Database + char* pcUserName; // UserName + char* pcPassword; // Password + int pcPort; // port +}; + +// Server configuration +struct CROSEServerConfig +{ + // GLOBAL + CROSEServerConfigSQL SQLServer; // SQL Information + bool usethreads; // Use Threads (in sockets) + bool Verbose; // print server information? + unsigned MaxConnections; // Max connections + unsigned UserPerThread; // User per thread + bool AllowMultipleConnections; // Allow multiple connection from same ip? + bool StayInServerLoop; // Server actived? + UINT ServerID; //id from this server + UINT ParentID; //id from the server to connect + char* ServerName; //Server name + UINT ServerType; //0 login - 1 server(char) - 2 channel(world) + + // LOGINSERVER + unsigned int MinimumAccessLevel; // Minimum access level + char* LoginIP; // LoginIP [external] + unsigned int LoginPort; // Login port + unsigned int LoginPass; + bool CreateLoginAccount; // Create account in login if not exist? + bool Testserver; // channel for test ? + + // CHARSEVER + unsigned long int DeleteTime; // Delete time + char* CharIP; // CharIP + unsigned int CharPort; // Char port + unsigned int CharPass; + unsigned int CharsPort; + + // WORLDSERVER + char* WorldIP; + unsigned int WorldPort; + unsigned int WorldsPort; + unsigned int WorldPass; + UINT EXP_RATE; + UINT DROP_RATE; + UINT BASE_DROP_CHANCE; + UINT ZULY_RATE; + UINT AUTOSAVE; + UINT SAVETIME; + UINT MapDelay; + UINT VisualDelay; + UINT WorldDelay; + UINT DeathDelay; //Set a timer delay so that we don't have a problem with monster death + UINT AIWatch; //Set an AI script number to watch for. Debug only + UINT SpawnType; + + char* WELCOME_MSG; + UINT DROP_TYPE; + int MaxStat; + int Partygap; + int FairyMode; + int FairyStay; + int FairyWait; + int FairyMax; + int ScrollFairy; + int FairyTestMode; + int PlayerDmg; + int MonsterDmg; + int Cfmode; + int BlueChance; + int StatChance; + int SlotChance; + int RefineChance; + int Rare_Refine; + int KillOnFail; + int MaxLevel; + int DropMultiple; + int KTPointRate; + UINT AttkMult; + UINT SkillMult; + UINT DropDelay; + + + // COMMAND LEVELS + int Command_Go; + int Command_Who; + int Command_Who2; + int Command_Broadcast; + int Command_Mute; + int Command_Tele; + int Command_Level; + int Command_LevelUp; + int Command_TeleToMe; + int Command_Save; + int Command_Reload; + int Command_Ann; + int Command_SSpawn; + int Command_Set; + int Command_ESpawn; + int Command_DSpawn; + int Command_DelSpawn; + int Command_Pak; + int Command_Pak2; + int Command_Pakm; + int Command_Info; + int Command_Exp; + int Command_Mon; + int Command_Kick; + int Command_Job; + int Command_Cha; + int Command_Item; + int Command_Drop; + int Command_GiveZuly; + int Command_Npc; + int Command_GiveFairy; + int Command_ManageFairy; + int Command_ChangeFairyWait; + int Command_ChangeFairyStay; + int Command_ChangeFairyTestMode; + int Command_Move; + int Command_Goto; + int Command_PlayerInfo; + int Command_Give2; + int Command_Ban; + int Command_Ani; + int Command_Summon; + int Command_ReloadQuest; + int Command_Shutdown; + int Command_DQuest; + int Command_IQuest; + int Command_ShopType; + int Command_Stat; + int Command_KillInRange; + int Command_GoToMap; + int Command_Heal; + int Command_ServerInfo; + int Command_TargetInfo; + int Command_Hide; + int Command_Class; + int Command_Monster; + int Command_GlobalTime; + int Command_Here; + int Command_Face; + int Command_Hair; + int Command_Pvp; + int Command_go; + int Command_Convert; + int Command_Rate; + int Command_Moveto; + int Command_Settime; + int Command_Transx; + int Command_Partylvl; + int Command_Pdmg; + int Command_Mdmg; + int Command_Rules; + int Command_Cfmode; + + char* Command_GlobalPrefix; + +}; + + +// Classes +// Base class +class CBaseSocket +{ + public: + CBaseSocket( ); // Constructor + virtual ~CBaseSocket( ); // Destructor + void CloseSocket( void ); // Close Socket + SOCKET sock; // This is our socket + SOCKET sckISC; // Raven0123 + SOCKET sckISCII; + bool isActive; // Is this socket connected? + bool isserver; + CCryptTable* CryptTable; // This is for decrypting incomming packets +#ifdef USE124 + char* csum; + int csumlen; +#endif + string ClientIP; // Client IP + char ClientSubNet[12]; // Lan Subnet +}; +// Client class +class CClientSocket : public CBaseSocket +{ + public: + CClientSocket( ); // Client Constructor + ~CClientSocket( ); // Client Destructor + bool ReceiveData( ); // Receive packet + void SendPacket( CPacket *P ); // Send Packet + void SendPacketCpy( CPacket *P ); // Send a packet without encrypting the orginal packet + CCryptStatus CryptStatus; // Status of the encryption + class CServerSocket* GS; // Pointer to Server class + bool ISCThread(); // Raven0123 + struct sockaddr_in clientinfo; + void* player; +#ifdef USE124 + char* ct; +#endif + private: + unsigned short PacketSize; // Size of the current packet + unsigned short PacketOffset; // Current offset of the data + unsigned char Buffer[ 0xFFF ]; // Buffer to receive data into + unsigned char tempBuffer[0xFFF]; +}; + +// Server class +class CServerSocket : public CBaseSocket +{ +private: + //void CryptISCPak( char* pak ); // Raven0123 + //void ISCThread(); // Raven0123 + //SOCKET sckISC; // Raven0123 + +public: + void CryptISCPak( char* pak ); // Raven0123 + CServerSocket( ); // Constructor + ~CServerSocket( ); // Destructor + + bool StartServer( ); // Start Server + void FillFDS( fd_set* fds ); // Create FDS table + void HandleClients( fd_set* fds ); // Handle Clients + void AddUser( SOCKET sock, sockaddr_in* ClientInfo, bool server ); // Add User + void DisconnectClient( CClientSocket* thisclient ); // Disconnect Client + bool DoSQL(char *Format, ...); + + // Virtual Functions + virtual void LoadEncryption( ); //load encryption + virtual void ServerLoop( ); // Main Loop + virtual CClientSocket* CreateClientSocket( ); // Create a client + virtual void DeleteClientSocket( CClientSocket* thisclient ); // Delete client + virtual bool OnServerReady( ); // Executed with server is ready + virtual void OnServerStep( ); // Executed + virtual void OnServerDie( ); + virtual bool OnReceivePacket( CClientSocket* thisclient, CPacket* P ); + virtual void ReceivedISCPacket( CPacket* pak ); // Raven0123 + virtual bool OnClientConnect( CClientSocket* thisclient ); + virtual void OnClientDisconnect( CClientSocket* thisclient ); + + // Variables + CROSEServerConfig Config; // Hold server configuration + int maxfd; + MYSQL *mysql; + virtual bool Ping( ); // Used to ping the mysql server + vector ClientList; + UINT ConnectedClients; + unsigned short port; + struct sockaddr_in ain; + struct sockaddr_in sain; + + pthread_t threads[65535]; + + void SendISCPacket( CPacket* pak ); // Raven0123 +}; +PVOID ClientMainThread( PVOID ); // Handle clients +bool InitWinSocket ( void ); // Init windows sockets (wsa) +void CloseWinSocket( void ); // Close Windows sockets (wsa) +// ----------------------------------------------------------------------------------------- + +#endif diff --git a/Src/Login Server/datatype.h b/Src/Login Server/datatype.h new file mode 100644 index 0000000..6b462a2 --- /dev/null +++ b/Src/Login Server/datatype.h @@ -0,0 +1,28 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +// Server list +struct CServers +{ + UINT id; + char* ip; + UINT port; + SOCKET sock; +}; diff --git a/Src/Login Server/extrafunction.cpp b/Src/Login Server/extrafunction.cpp new file mode 100644 index 0000000..1e57503 --- /dev/null +++ b/Src/Login Server/extrafunction.cpp @@ -0,0 +1,21 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" diff --git a/Src/Login Server/loginclient.cpp b/Src/Login Server/loginclient.cpp new file mode 100644 index 0000000..cf68f53 --- /dev/null +++ b/Src/Login Server/loginclient.cpp @@ -0,0 +1,37 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" + +// Constructor +CLoginClient::CLoginClient( ) +{ + userid = 0; + isLoggedIn = false; + accesslevel = 0; + username.reserve(17); + password.reserve(33); +} + +// Deconstructor +CLoginClient::~CLoginClient( ) +{ + +} diff --git a/Src/Login Server/loginpackets.cpp b/Src/Login Server/loginpackets.cpp new file mode 100644 index 0000000..1dd7bf9 --- /dev/null +++ b/Src/Login Server/loginpackets.cpp @@ -0,0 +1,240 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" + +// Send Server encryption +bool CLoginServer::pakEncryptionRequest( CLoginClient* thisclient, CPacket* P ) +{ + try + { + thisclient->CryptStatus.CurAddValue = rand( ) | (rand( )<<16); + STARTPACKET( pak, 0x7FF, 11 ); + SETBYTE ( pak, 0x00, 0x02 ); + SETDWORD ( pak, 0x01, thisclient->CryptStatus.CurAddValue ); + thisclient->SendPacket( &pak ); + return true; + } + catch(...) + { + Log(MSG_ERROR, "pakEncryptionRequest"); + } +} + +// Packet when user login (chck user and pass) +bool CLoginServer::pakUserLogin( CLoginClient* thisclient, CPacket* P ) +{ + Log(MSG_DEBUG,"User Login request received"); + try + { + if ( thisclient->isLoggedIn ) return false; + MYSQL_ROW row; + thisclient->username.assign( (const char*)&P->Buffer, 32, (P->Size-6-32)>16?16:P->Size-6-32 ); + thisclient->password.assign( (const char*)&P->Buffer, 0, 32 ); + BEGINPACKET( pak, 0x708 ); + MYSQL_RES *result = DB->QStore( "SELECT id,password,accesslevel,online FROM accounts WHERE username='%s'", thisclient->username.c_str() ); + if(result == NULL) + { + Log(MSG_WARNING,"DB Query returned a NULL result"); + return false; + } + if( mysql_num_rows( result ) == 1 ) + { + row = mysql_fetch_row(result); + int res = 0; + #ifdef _WIN32 + res = _stricmp( row[1], thisclient->password.c_str() ); + #else + res = strcasecmp( row[1], thisclient->password.c_str() ); + #endif + if ( res == 0 ) + { + if(atoi(row[3])==1) + { // characters is already logged + Log(MSG_WARNING, "Account %s try re-login", thisclient->username.c_str() ); + ADDBYTE( pak, 4 ); + ADDDWORD( pak, 0 ); + thisclient->SendPacket( &pak ); + DB->QFree( ); + DB->QExecute( "update accounts set login_count=1 WHERE username='%s'", thisclient->username.c_str()); + return true; + } + thisclient->accesslevel = atoi(row[2]); + if( thisclient->accesslevel < Config.MinimumAccessLevel ) + { //The server are under inspection + Log(MSG_WARNING,"User accesslevel %i is lower than minimum %i",thisclient->accesslevel,Config.MinimumAccessLevel); + ADDBYTE( pak, 0 ); + ADDDWORD( pak, 0 ); + thisclient->SendPacket( &pak ); + DB->QFree( ); + return true; + } + if ( thisclient->accesslevel > 0 ) + { + thisclient->userid = atoi(row[0]); + thisclient->isLoggedIn = true; + DB->QFree( ); + //OK! + Log(MSG_WARNING,"User %s passed to charserver",thisclient->username.c_str()); + ADDDWORD( pak, 0x0c000000 );//ADDDWORD( pak, 0x6f000000 ); + ADDBYTE( pak, 0 ); + result = DB->QStore( "SELECT id,name FROM channels WHERE owner=0" ); + if(result==NULL) return false; + while( row = mysql_fetch_row(result) ) + { + if (Config.Testserver){ADDBYTE( pak, 63 + atoi( row[0] ) );} + else{ + ADDBYTE( pak, 48 + atoi( row[0] ) );} + ADDSTRING( pak, row[1] ); + ADDBYTE( pak, 0 ); + ADDDWORD(pak, atoi( row[0] ) ); + } + DB->QFree( ); + } + else + { + //BANNED + Log(MSG_WARNING,"User %s banned",thisclient->username.c_str()); + ADDBYTE( pak, 5 ); + ADDDWORD( pak, 0 ); + DB->QFree( ); + } + } + else + { + //BAD PASSWORD + Log(MSG_WARNING,"User %s attempted to log in with invalid password",thisclient->username.c_str()); + ADDBYTE( pak, 3 ); + ADDDWORD( pak, 0 ); + DB->QFree( ); + } + } + else + { + DB->QFree( ); + if(Config.CreateLoginAccount) + { + if(!DB->QExecute("INSERT into accounts (username,password,accesslevel) values ('%s','%s',100)",thisclient->username.c_str(),thisclient->password.c_str())) + return true; + Log( MSG_INFO, "New Account created '%s'", thisclient->username.c_str() ); + } + //BAD USERNAME + ADDBYTE( pak, 2 ); + ADDDWORD( pak, 0 ); + } + /* + 1 - general error | 4 - your account is already logged + 6 - topup account | 7 - cannot connect to server please try again + 8 - server exceeded | 9 - account have not been verified + 10 - login failed | 11 - ip capacity is full + */ + thisclient->SendPacket ( &pak ); + return true; + } + catch(...) + { + Log(MSG_ERROR, "Error in pakUserLogin"); + } + +} + +// Send server list +bool CLoginServer::pakGetServers( CLoginClient* thisclient, CPacket* P ) +{ + try + { + if( !thisclient->isLoggedIn ) return false; + MYSQL_ROW row; + unsigned servernum = GETDWORD( (*P), 0 ); + MYSQL_RES *result = DB->QStore( "SELECT id,name,connected,maxconnections FROM channels WHERE owner=%i", servernum ); + if(result==NULL) return false; + BEGINPACKET( pak, 0x704 ); + ADDDWORD ( pak, servernum ); + ADDBYTE ( pak, (BYTE)mysql_num_rows( result ) ); //old function + while(row = mysql_fetch_row(result)) + { + UINT connected = atoi(row[2]); + UINT maxconnections = atoi(row[3]); + BYTE id = atoi( row[0] ); + BYTE status = ((100 * connected) / (maxconnections == 0 ? 1 : maxconnections)) & 0xff; + + ADDWORD( pak, id ); + ADDBYTE( pak, 0 ); + ADDWORD( pak, status ); + ADDSTRING( pak, row[1] ); // Name + ADDBYTE( pak, 0 ); + } + DB->QFree( ); + thisclient->SendPacket ( &pak ); + return true; + } + catch(...) + { + Log(MSG_ERROR, "Error in pakGetServers"); + } +} + +// Send server IP +bool CLoginServer::pakGetIP( CLoginClient* thisclient, CPacket* P ) +{ + try + { + if (!thisclient->isLoggedIn) return false; + + MYSQL_ROW row; + + DWORD servernum = GETDWORD( (*P), 0 ); + BYTE channelnum = GETBYTE( (*P), 4 ); + + + BEGINPACKET( pak, 0x70a ); + + if(!DB->QExecute( "UPDATE accounts SET lastsvr=%i,lastip='%s' WHERE id=%i", channelnum, inet_ntoa( thisclient->clientinfo.sin_addr ), thisclient->userid)) + return false; + MYSQL_RES *result = DB->QStore( "SELECT host,port,connected,maxconnections FROM channels WHERE id=%i", servernum ); + if(result==NULL) return false; + if(mysql_num_rows(result)!=1) + { + Log(MSG_WARNING, "Player selected a invalid channel or channel offline" ); + DB->QFree( ); + return true; + } + row = mysql_fetch_row(result); + UINT connected = atoi(row[2]); + UINT maxconnections = atoi(row[3]); + + Log(MSG_DEBUG, "IP from database. Sent to client: %s ",row[0] ); + + ADDBYTE( pak, 0 ); //atoi(row[0]) ); // What is status? It's NULL in tables - Drakia + ADDDWORD( pak, thisclient->userid ); + ADDDWORD( pak, 0x87654321 ); + ADDSTRING( pak, row[0] ); + ADDBYTE( pak, 0 ); + ADDWORD( pak, atoi(row[1]) ); + DB->QFree( ); + thisclient->SendPacket ( &pak ); + return true; + } + catch(...) + { + Log(MSG_ERROR, "Error in pakGetIP"); + } +} + diff --git a/Src/Login Server/loginserver.cpp b/Src/Login Server/loginserver.cpp new file mode 100644 index 0000000..45749af --- /dev/null +++ b/Src/Login Server/loginserver.cpp @@ -0,0 +1,142 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" + +// Constructor +CLoginServer::CLoginServer( string fn ) +{ + filename = fn; + LoadConfigurations( (const char*)filename.c_str() ); + GServer = this; +} + +// Destructor +CLoginServer::~CLoginServer( ) +{ + +} + +// Create client socket (alloc memory) +CLoginClient* CLoginServer::CreateClientSocket( ) +{ + try + { + CLoginClient* thisclient = new (nothrow) CLoginClient( ); + if(thisclient==NULL) + return NULL; + thisclient->GS = this; + return thisclient; + } + catch(...) + { + Log(MSG_ERROR, "Error in CreateClientSocket"); + } +} + +//loadserver encryption +void CLoginServer::LoadEncryption( ) +{ + GenerateLoginServerCryptTables( CryptTable ); +} + +// Delete client socket (free memory) +void CLoginServer::DeleteClientSocket( CClientSocket* thisclient ) +{ + try + { + Log( MSG_INFO, "User disconnected" ); + delete (CLoginClient*)thisclient; + } + catch(...) + { + Log(MSG_ERROR, "Error in DeleteClientSocket"); + } +} + +// Load information when server is ready +bool CLoginServer::OnServerReady( ) +{ + return true; +} + +// Load Server configuration +void CLoginServer::LoadConfigurations( const char* file ) +{ + try + { + //Database + Config.SQLServer.pcServer = ConfigGetString ( file, "mysql_host", "localhost" ); + Config.SQLServer.pcDatabase = ConfigGetString ( file, "mysql_database", "irose2" ); + Config.SQLServer.pcUserName = ConfigGetString ( file, "mysql_user", "root" ); + Config.SQLServer.pcPassword = ConfigGetString ( file, "mysql_pass", "root" ); + Config.SQLServer.pcPort = ConfigGetInt ( file, "mysql_port", 3306 ); + //Server + Config.ServerID = ConfigGetInt ( file, "serverid", 0 ); + Config.ServerType = 0; // Login is always 0 + Config.LoginIP = ConfigGetString ( file, "serverip", "127.0.0.1" ); + Config.LoginPort = ConfigGetInt ( file, "serverport", 29000 ); + Config.ServerName = ConfigGetString ( file, "servername", "Loginserver" ); + //Passwords + Config.LoginPass = ConfigGetInt ( file, "loginpass", 123456 ); + Config.CharPass = ConfigGetInt ( file, "charpass", 123456 ); + Config.WorldPass = ConfigGetInt ( file, "worldpass", 123456 ); + //Login + Config.MinimumAccessLevel = ConfigGetInt ( file, "accesslevel", 100 ); + Config.usethreads = ConfigGetInt ( file, "usethreads", 0 )==0?false:true; + Config.CreateLoginAccount = ConfigGetInt ( file, "CreateLoginAccount", 0 )==0?false:true; + Config.Testserver = ConfigGetInt ( file, "Testserver", 1 )==0?false:true; + } + catch(...) + { + Log(MSG_FATALERROR, "Error parsing configuration file"); + } + +} + +// Handle packets +bool CLoginServer::OnReceivePacket( CClientSocket* thisclient, CPacket *P ) +{ + //Log(MSG_ERROR, "Packet received"); + switch( P->Command ) + { + + case 0x703: return pakEncryptionRequest( (CLoginClient*)thisclient, P ); + case 0x708: return pakUserLogin ( (CLoginClient*)thisclient, P ); + case 0x704: return pakGetServers ( (CLoginClient*)thisclient, P ); + case 0x70a: return pakGetIP ( (CLoginClient*)thisclient, P ); + case 0x808: return true; + default: + Log( MSG_WARNING, "[%i]Login Server Received unknown packet. Command:%04x Size:%04x", thisclient->sock, P->Command, P->Size ); + break; + } + return true; +} +//<<<<<<< .mine + +//bool CLoginServer::Ping( ) { +// return DB->Ping( ); +//} +//======= + +bool CLoginServer::Ping( ) { + return DB->Ping( ); +} +//>>>>>>> .r81 diff --git a/Src/Login Server/loginserver.h b/Src/Login Server/loginserver.h new file mode 100644 index 0000000..c250d0b --- /dev/null +++ b/Src/Login Server/loginserver.h @@ -0,0 +1,77 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef __ROSE_SERVER__ +#define __ROSE_SERVER__ + +#include "../common/sockets.h" +#include "datatype.h" + +// Player class +class CLoginClient : public CClientSocket +{ + public: + CLoginClient ( ); // Constructor + ~CLoginClient ( ); // destructor + // Variables + bool isLoggedIn; + UINT userid; + string username; + string password; + WORD accesslevel; +}; + +// Server class +class CLoginServer : public CServerSocket +{ + public: + CLoginServer ( string ); // Constructor + ~CLoginServer( ); // Destructor + + //Functions + CLoginClient* CreateClientSocket( ); // Create a client + void DeleteClientSocket( CClientSocket* thisclient ); // Delete a client + void LoadEncryption( ); + bool OnReceivePacket( CClientSocket* thisclient, CPacket* P ); + bool OnServerReady(void); + void LoadConfigurations( const char* ); + bool Ping( ); + // Packets + bool pakEncryptionRequest( CLoginClient* thisclient, CPacket* P ); + bool pakConnectToChar( CLoginClient* thisclient, CPacket *P ); + bool pakUserLogin( CLoginClient* thisclient, CPacket* P ); + bool pakGetServers( CLoginClient* thisclient, CPacket* P ); + bool pakGetIP( CLoginClient* thisclient, CPacket* P ); + + // Variables + string filename; + vector ServerList; // list of channels; + + CDatabase *DB; +}; + +void StartSignal( ); +void StopSignal( ); +void HandleSignal( int num ); +extern class CLoginServer* GServer; + +#endif + + diff --git a/Src/Login Server/main.cpp b/Src/Login Server/main.cpp new file mode 100644 index 0000000..d44ec13 --- /dev/null +++ b/Src/Login Server/main.cpp @@ -0,0 +1,65 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" + +unsigned char LOG_THISSERVER = 0; +class CLoginServer* GServer; + +// Main Funtion +int main(int argc, char *argv[]) +{ + LOG_THISSERVER = LOG_LOGIN_SERVER; + InitWinSocket( ); + string fileconf = "loginserver.conf"; + if(argc>1) + { + fileconf = argv[1]; + } + + CLoginServer *server = new (nothrow) CLoginServer( fileconf ); + + MYSQL mysql; + server->DB = new CDatabase( server->Config.SQLServer.pcServer, + server->Config.SQLServer.pcUserName, + server->Config.SQLServer.pcPassword, + server->Config.SQLServer.pcDatabase, + server->Config.SQLServer.pcPort, &mysql ); + server->port = server->Config.LoginPort; + if(server->Config.usethreads) + Log( MSG_INFO, "Using Threads" ); + // Start server + if(server->DB->Connect( )==0) Log( MSG_INFO, "Connected to MySQL server" ); + else + { + delete server; + #ifdef _WIN32 + system("pause"); + #endif + return -1; + } + server->LoadEncryption (); + server->StartServer( ); + server->DB->Disconnect( ); + // Close server + delete server; + CloseWinSocket( ); + return EXIT_SUCCESS; +} diff --git a/Src/Login Server/signal.cpp b/Src/Login Server/signal.cpp new file mode 100644 index 0000000..37bb9db --- /dev/null +++ b/Src/Login Server/signal.cpp @@ -0,0 +1,109 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://osroseon.to.md + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "loginserver.h" +/* +/ from signal.h + * NOTE: SIGINT is produced when the user presses Ctrl-C. + * SIGILL has not been tested. + * SIGFPE doesn't seem to work? + * SIGSEGV does not catch writing to a NULL pointer (that shuts down + * your app; can you say "segmentation violation core dump"?). + * SIGTERM comes from what kind of termination request exactly? + * SIGBREAK is indeed produced by pressing Ctrl-Break. + * SIGABRT is produced by calling abort. +*/ + +void StartSignal( ) +{ + signal( SIGINT, HandleSignal ); + signal( SIGILL, HandleSignal ); + signal( SIGFPE, HandleSignal ); + signal( SIGSEGV, HandleSignal ); + signal( SIGTERM, HandleSignal ); + signal( SIGBREAK, HandleSignal ); + signal( SIGABRT, HandleSignal ); +} + +void StopSignal( ) +{ + signal( SIGINT, 0 ); + signal( SIGILL, 0 ); + signal( SIGFPE, 0 ); + signal( SIGSEGV, 0 ); + signal( SIGTERM, 0 ); + signal( SIGBREAK, 0 ); + signal( SIGABRT, 0 ); +} + +void HandleSignal( int num ) +{ + switch(num) + { + case SIGINT:/* Interactive attention */ + Log( MSG_ERROR, "Signal received: SIGINT" ); + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + case SIGILL:/* Illegal instruction */ + Log( MSG_ERROR, "Signal received: SIGILL, Server will be closed, Trying to save..." ); + GServer->isActive = false; + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + case SIGFPE:/* Floating point error */ + Log( MSG_ERROR, "Signal received: SIGFPE, Server will be closed, Trying to save..." ); + GServer->isActive = false; + raise(num); + break; + case SIGSEGV:/* Segmentation violation */ + Log( MSG_ERROR, "Signal received: SIGSEGV, Server will be closed, Trying to save..." ); + GServer->isActive = false; + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + raise(num); + break; + + case SIGTERM:/* Termination request */ + Log( MSG_INFO, "Signal received: SIGTERM, Server will be closed" ); + GServer->isActive = false; + break; + #ifdef _WIN32 + case SIGBREAK:/* Control-break */ + Log( MSG_INFO, "Signal received: SIGBREAK, Server will be closed" ); + GServer->isActive = false; + break; + #endif + default: + cout << "Unknown Signal Number: " << num << endl; + raise(num); + } +} diff --git a/Src/World Server/Buff.cpp b/Src/World Server/Buff.cpp new file mode 100644 index 0000000..f7ed9c2 --- /dev/null +++ b/Src/World Server/Buff.cpp @@ -0,0 +1,946 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Add Player buffs +bool CWorldServer::AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue ) +{ + //Log(MSG_INFO, "called addbuffs." ); + bool bflag = false; + for(unsigned int i=0;i<2;i++) + { + if(thisskill->status[i] != 0) // sets status flags in magicstatus array + { + bflag = CheckABuffs( thisskill, character, Evalue, i ); + } + else // some skills are instant effects without status changes e.g. healing. + { + if(thisskill->buff[i] != 0) + { + bflag = InstantBuff( thisskill, character, Evalue, i ); + } + } + } + return bflag; +} + +// handle instant effect skills without status changes +bool CWorldServer::InstantBuff( CSkills* thisskill, CCharacter* character, int Evalue, int i ) +{ + //Log( MSG_INFO, "Instant skill cast: Status (should be zero):%i buff: %i", thisskill->status[i], thisskill->buff[i] ); + switch( thisskill->buff[i]) + { + case 16: // HP + { + if(thisskill->duration <= 0 )//is heal + { + if(character->Stats->HP <= 0) //character is dead + return true; + character->Stats->HP += (long int)thisskill->value1[i]; //apparently there is no buff here. // + Evalue - 8; + if(character->Stats->HP > character->Stats->MaxHP ) + character->Stats->HP = character->Stats->MaxHP; + return true; + } + } + break; + case 17: // MP + { + if(thisskill->duration <= 0 )// recover MP + { + character->Stats->MP += (long int)thisskill->value1[i]; + if(character->Stats->MP > character->Stats->MaxMP) + character->Stats->MP = character->Stats->MaxMP; + return true; + } + } + break; + case 76: // Stamina + { + if(character->IsPlayer()) //monsters do not have stamina it would seem + { + if(thisskill->duration <= 0 )// recover Stamina + { + CPlayer* thisclient = GServer->GetClientByID(character->clientid); + if(thisclient == NULL)return false; + thisclient->CharInfo->stamina += (long int)thisskill->value1[i]; + if(thisclient->CharInfo->stamina > 5000) + thisclient->CharInfo->stamina = 5000; + //Log(MSG_INFO,"Character %i Stamina is now %i", character->clientid, thisclient->CharInfo->stamina); + return true; + } + } + else + return false; + } + break; + default: + return false; + break; + } + return false; +} + +// Add DeBuffs +bool CWorldServer::CheckABuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i) +{ + //Log( MSG_INFO, "checkabuffs status: %i buff: %i",( thisskill->status[i], thisskill->buff[i] )); + bool bflag = false; + if(thisskill->status[i] != 0) + { + switch( thisskill->status[i]) + { + case 7: case 8: case 9: case 10: case 11: //poisoned + { + //Log( MSG_INFO, "checkabuffs: Poisoned Status detected %i", thisskill->status[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Poisoned, character->Status->Poisoned,true, true ); + if(BuffValue.Position != 0xff) + { + UINT j = BuffValue.Position; + if(j>14) + { + character->Status->Poisoned = j; + character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1 + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = 0; // this might cause headaches later + character->MagicStatus[j].Status = thisskill->status[i]; + //Log( MSG_INFO, "Poisoned Status applied"); + bflag = true; + } + } + } + break; + case 30: // muted + { + //Log( MSG_INFO, "checkabuffs: Mute Status detected %i", thisskill->status[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Muted, character->Status->Muted, true, true ); + if(BuffValue.Position != 0xff) + { + UINT j = BuffValue.Position; + if(j>14) + { + character->Status->Muted = j; + character->Status->CanCastSkill = false; + character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1 + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = 0; // this might cause headaches later + character->MagicStatus[j].Status = thisskill->status[i]; + //Log( MSG_INFO, "Muted Status applied"); + bflag = true; + } + } + } + break; + case 31: // sleep + { + //Log( MSG_INFO, "checkabuffs: Sleep Status detected %i", thisskill->status[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Sleep, character->Status->Sleep, true, true ); + Log( MSG_INFO, "Sleep Status attempted. position = %i", BuffValue.Position); + if(BuffValue.Position != 0xff) + { + UINT j = BuffValue.Position; + if(j>14) + { + character->Status->Sleep = j; + character->Status->CanAttack = false; + character->Status->CanMove = false; + character->Status->CanCastSkill = false; + character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1 + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = 0; // this might cause headaches later + character->MagicStatus[j].Status = thisskill->status[i]; + //Log( MSG_INFO, "Sleep Status applied"); + bflag = true; + } + } + } + break; + case 32: // faint + { + //Log( MSG_INFO, "checkabuffs: Fainted Status detected %i", thisskill->status[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Faint, character->Status->Faint, true, true ); + //Log( MSG_INFO, "Stun Status attempted. position = %i", BuffValue.Position); + if(BuffValue.Position != 0xff) + { + UINT j = BuffValue.Position; + if(j>14) + { + character->Status->Faint = j; + character->Status->CanAttack = false; + character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1 + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = 0; // this might cause headaches later + character->MagicStatus[j].Status = thisskill->status[i]; + //Log( MSG_INFO, "Fainted Status applied"); + bflag = true; + } + } + } + break; + case 39: // Dispell. removes all buff and debuff effects + { + Log( MSG_INFO, "checkabuffs: Dispell Status detected %i", thisskill->status[i] ); + // some dispell skills have duration of 2 seconds. Others have zero. + // for now we will treat them all as instants so we don't need a buff slot + float randv = GServer->RandNumber(0, 100); + if(randv <= thisskill->successrate) + { + for(int j=0;j<30;j++) + { // Clean Buffs + character->MagicStatus[j].Duration = 0; + } + Log( MSG_INFO, "Dispell Status applied successfully"); + // the rest will be taken care of in RefreshBuffs() + } + bflag = true; + } + break; + + case 54: //A_GMExtra_Damage: + case 36: //A_Extra_Damage: + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->ExtraDamage_up, + character->Status->ExtraDamage_down, + character->Stats->ExtraDamage, true ); + if(BuffValue.Position != 0xff) + { + UINT j = BuffValue.Position; + character->Stats->ExtraDamage = thisskill->atkpower; + if(j<15) + character->Status->ExtraDamage_up = j; + else + character->Status->ExtraDamage_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = thisskill->atkpower; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + Log( MSG_INFO, "Extra Damage Buff. Value %i Status %i",character->MagicStatus[j].Value,character->MagicStatus[j].Status); + } + } + case 35: // shield damage + { + // uses skill power not buff value. Add later + } + break; + case 18: // attack power up + case 19: // attack power down + case 48: // attack power up + { + //Log( MSG_INFO, "checkabuffs: Attack buff detected %i", thisskill->buff[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Attack_up, + character->Status->Attack_down, + character->Stats->Attack_Power, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Attack_Power = BuffValue.NewValue; + if(j<15) + character->Status->Attack_up = j; + else + character->Status->Attack_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 20: // def up + case 21: // def down + case 49: // def up + { + //Log( MSG_INFO, "checkabuffs: Defense buff detected %i", thisskill->buff[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Defense_up, + character->Status->Defense_down, + character->Stats->Defense, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Defense = BuffValue.NewValue; + if(j<15) + character->Status->Defense_up = j; + else + character->Status->Defense_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 24: //accuracy up + case 25: //accuracy down + case 51: //attack accuracy up. + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Accury_up, + character->Status->Accury_down, + character->Stats->Accury, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Accury = BuffValue.NewValue; + if(j<15) + character->Status->Accury_up = j; + else + character->Status->Accury_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 22: // macic resistance up + case 23: // magic resistance down + case 50: // magic resistance up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Magic_Defense_up, + character->Status->Magic_Defense_down, + character->Stats->Magic_Defense, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Magic_Defense = BuffValue.NewValue; + if(j<15) + character->Status->Magic_Defense_up = j; + else + character->Status->Magic_Defense_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 28: //dodge up + case 29: //dodge down + case 53: //dodge rate up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Dodge_up, + character->Status->Dodge_down, + character->Stats->Dodge, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Dodge = BuffValue.NewValue; + if(j<15) + character->Status->Dodge_up = j; + else + character->Status->Dodge_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 14: //dash + case 15: //slow + case 46: //movement speed increased + { + //Log( MSG_INFO, "checkabuffs: move speed buff detected %i", thisskill->buff[i] ); + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Dash_up, + character->Status->Dash_down, + character->Stats->Move_Speed, true ); + if(BuffValue.NewValue != 0) + { + UINT j = BuffValue.Position; + character->Stats->Move_Speed = BuffValue.NewValue; + if(j<15) + character->Status->Dash_up = j; + else + character->Status->Dash_down = j; + //Log( MSG_INFO, "Move speed buff position %i. new move speed %i. buff value %i", j,BuffValue.NewValue,BuffValue.Value ); + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 16: // haste attack + case 17: // slow attack + case 47: // attack speed up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Haste_up, + character->Status->Haste_down, + (UINT)character->Stats->Attack_Speed, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->Attack_Speed = BuffValue.NewValue; + if(j<15) + character->Status->Haste_up = j; + else + character->Status->Haste_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 26: // crit up + case 27: // crit down + case 52: // crit up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->Critical_up, + character->Status->Critical_down, + character->Stats->Critical, true ); + if(BuffValue.NewValue != 0) + { + UINT j = BuffValue.Position; + character->Stats->Critical = BuffValue.NewValue; + if(j<15) + character->Status->Critical_up = j; + else + character->Status->Critical_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 12: // max HP up + case 44: // max HP up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->HP_up, + character->Status->HP_down, + character->Stats->MaxHP, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->MaxHP = BuffValue.NewValue; + if(j<15) + character->Status->HP_up = j; + else + character->Status->HP_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + case 13: // max MP up + case 45: // max MP up + { + CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, + character->Status->MP_up, + character->Status->MP_down, + character->Stats->MaxMP, true ); + if(BuffValue.NewValue!=0) + { + UINT j = BuffValue.Position; + character->Stats->MaxMP = BuffValue.NewValue; + if(j<15) + character->Status->MP_up = j; + else + character->Status->MP_down = j; + character->MagicStatus[j].Buff = thisskill->buff[i]; + character->MagicStatus[j].BuffTime = clock(); + character->MagicStatus[j].Duration = thisskill->duration; + character->MagicStatus[j].Value = BuffValue.Value; + character->MagicStatus[j].Status = thisskill->status[i]; + bflag = true; + } + } + break; + default: + Log(MSG_WARNING,"Status %i unknown. Possibly not coded yet",thisskill->status[i]); + break; + } + } + return bflag; +} + +// Get the new buffs values (character) +CBValue CWorldServer::GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff, bool Status ) +{ + CBValue NewValue; + NewValue.Position = 0xff; + NewValue.NewValue = 0; + NewValue.Value = 0; + INT Value = 0; + UINT NormalValue = 0; + UINT UpValue = 0; + UINT DownValue = 0; + bool BuffFlag = false; + // not currently detecting debuffs properly so let's bring in some new code :) + //PY buff mod based on thiskill->status[i] + switch(thisskill->status[i]) + { + case 12: case 13: case 14: case 16: case 18: case 20: case 22: case 24: case 26: case 28: case 35: case 36: case 53: + case 54: + Buff = true; + break; + case 7: case 8: case 9: case 10: case 15: case 17: case 19: case 21: case 23: case 25: case 27: case 29: + case 30: case 31: case 32: case 59: case 60: + Buff = false; + break; + default: + Buff = true; + break; + } + Log(MSG_INFO, "Current Buff boolean status %i Skill status = %i up: %i down: %i", Buff,thisskill->status[i],up,down); + //PY end + if(Buff) //skill is a buff + { + //find whether character already has buffs for this stat + for(UINT z=0;z<15;z++) // 0 to 14 are upward + { + if(character->MagicStatus[z].Status == thisskill->status[i]) + { + NewValue.Position = z; + //Log(MSG_INFO, "Existing Buff position = %i",z); + NormalValue = CurrentValue - character->MagicStatus[z].Value; + UpValue = character->MagicStatus[z].Value; + BuffFlag = true; + break; + } + } + if(BuffFlag == false) // no active buffs so find a new slot + { + for(UINT z=0;z<15;z++) // 0 to 14 are upward + { + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; + //Log(MSG_INFO, "New Buff position = %i",z); + NormalValue = CurrentValue; + BuffFlag = true; + break; + } + } + } + } + else + { + //find whether character already has debuffs for this stat + for(UINT z=15;z<32;z++) // 15 to 32 are downward + { + if(character->MagicStatus[z].Status == thisskill->status[i]) + { + NewValue.Position = z; + //Log(MSG_INFO, "Existing De-Buff position = %i",z); + NormalValue = CurrentValue + character->MagicStatus[z].Value; + DownValue = character->MagicStatus[z].Value; + BuffFlag = true; + break; + } + } + if(BuffFlag == false) // no active debuffs so find a new slot + { + for(UINT z=15;z<32;z++) // 15 to 32 are downward + { + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; + //Log(MSG_INFO, "New De-Buff position = %i",z); + NormalValue = CurrentValue; + BuffFlag = true; + break; + } + } + } + } + if(BuffFlag == false) // no available spaces found + return NewValue; + + + + + //if(up != 0xff) //currently have a buff. up is its location + //{ + // UpValue = character->MagicStatus[up].Value; + //Log(MSG_INFO, "Current active buff detected in position %i with value %i", NewValue.Position,UpValue); + // NormalValue = CurrentValue - character->MagicStatus[up].Value; + // if(Buff) + // NewValue.Position = up; + //} + + //else // don't currently have a buff + if(NewValue.Position == 0xff) + { + //Log(MSG_INFO, "No buff currently active"); + if(Buff) //is this skill a buff? + { + for(UINT z=0;z<15;z++) // 0 to 14 are upward + { + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; //z is the new index in the magicstatus array where the buff will be placed + //Log(MSG_INFO, "Buff position = %i",z); + break; + } + } + if(NewValue.Position == 0xff) //no spaces found ?? + return NewValue; + NormalValue = CurrentValue; // no buffs currently active so set normalvalue to currentvalue + } + else //debuff + { + for(UINT z=15;z<30;z++) // 15 to 29 are downward + { + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; //z is the new index in the magicstatus array where the buff will be placed + Log(MSG_INFO, "DeBuff position = %i",z); + break; + } + } + if(NewValue.Position == 0xff) //no spaces found ?? + return NewValue; + NormalValue = CurrentValue; // no buffs currently active so set normalvalue to currentvalue + } + } + + /* + if (!Status) // status boolean set false as default so it will run this code in pretty much all cases + { + Log(MSG_INFO, "Status set to false so checking for debuffs"); + if( down != 0xff) //do we have a debuff in place already? down is its location + { + DownValue = character->MagicStatus[down].Value; + Log(MSG_INFO, "Current active de-buff detected in position %i with value %i", i,DownValue); + if( NormalValue!=0) + NormalValue += character->MagicStatus[down].Value; + else + NormalValue = CurrentValue + character->MagicStatus[down].Value; + if(!Buff) + NewValue.Position = down; + } + else + { + Log(MSG_INFO, "No de-buff currently active"); + if(!Buff) //is this a debuff? + { + Log(MSG_INFO, "Buff is false so adding a new de-buff"); + for(UINT z=15;z<30;z++) // 15 to 29 are downward + { + if(character->MagicStatus[z].Status == thisskill->status[i]) + { + NewValue.Position = z; //z is the index in the magicstatus array where the de-buff will be placed + Log(MSG_INFO, "#1 found status De-Buff in position = %i. refreshing it.",z); + break; + } + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; //z is the new index in the magicstatus array where the de-buff will be placed + Log(MSG_INFO, "#1 De-Buff position = %i",z); + break; + } + } + if(NewValue.Position == 0xff) + return NewValue; + NormalValue = CurrentValue; + } + } + } + else if ( down != 0xff ) //Status = true and we currently have a debuff in effect?? + { + Log(MSG_INFO, "Current active debuff detected in position %i with value %i", DownValue, character->MagicStatus[down].Value); + NormalValue = CurrentValue + character->MagicStatus[down].Value; + if(!Buff) + NewValue.Position = down; + } + else //status set true and no current debuff. Used for skills with only a Status effect but no buff/debuff value + { + for(UINT z=15;z<30;z++) //check magicstatus array debuff area for spaces + { + if(character->MagicStatus[z].Status == 0) + { + NewValue.Position = z; //z is the new index in the magicstatus array where the de-buff will be placed + Log(MSG_INFO, "#2 De-Buff position = %i",z); + break; + } + } + if(NewValue.Position == 0xff) + return NewValue; + NormalValue = CurrentValue; + } + */ + + //Log(MSG_INFO, "Newvalue.position = %i",NewValue.Position); + if( thisskill->value2[i] > 0 ) + { + Value = NormalValue * thisskill->value2[i] / 100; + //Log(MSG_INFO, "Percentage buff = %i",Value); + } + if( thisskill->value1[i] > 0 ) + { + Value += thisskill->value1[i]; + if(thisskill->buff[i] == A_DASH || thisskill->buff[i] == MOV_SPEED) + { + //Log(MSG_DEBUG,"Made it into the DASH calculations. Evalue = %i",Evalue); + if( Evalue <= 60 ) + { + int temp1 = Evalue - 60; + //Log(MSG_DEBUG,"temp1 = %i",temp1); + float temp2 = (20.0 / 45.0); + //Log(MSG_DEBUG,"temp2 = %f",temp2); + int temp = (int)(temp1 * temp2); + //Log(MSG_DEBUG,"Result of calculation for INT < 60 = %i",temp); + //int temp = (int)((Evalue - 60) * (20.0 / 45.0)); + //Log(MSG_DEBUG,"Result of calculation for INT < 60 = %i",temp); + Value += temp; + } + if( Evalue > 60 && Evalue <= 150) + { + int temp1 = Evalue - 60; + float temp2 = (17.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += temp; + } + if( Evalue > 150 && Evalue <= 195) + { + int temp1 = Evalue - 150; + float temp2 = (18.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 34 + temp; + } + if( Evalue > 195 && Evalue <= 240) + { + int temp1 = Evalue - 195; + float temp2 = (19.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 52 + temp; + } + if( Evalue > 240 && Evalue <= 285) + { + int temp1 = Evalue - 240; + float temp2 = (17.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 71 + temp; + } + if( Evalue > 285 && Evalue <= 330) + { + int temp1 = Evalue - 285; + float temp2 = (18.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 88 + temp; + } + if( Evalue > 330 && Evalue <= 375) + { + int temp1 = Evalue - 330; + float temp2 = (17.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 106 + temp; + } + if( Evalue > 375 && Evalue <= 385) + { + int temp1 = Evalue - 420; + float temp2 = (19.0 / 45.0); + int temp = (int)(temp1 * temp2); + Value += 123 + temp; + } + } + // Value += (Evalue - 15) / 10; + else + Value += (Evalue-15)/32; + //Log(MSG_INFO, "Number buff = %i",Value); + } + if(Buff) + { + if( Value + CurrentValue - DownValue >= CurrentValue ) + { + CurrentValue = NormalValue - DownValue + Value; + } + else + { + CurrentValue = 0; + } + } + else + { + CurrentValue = NormalValue + UpValue - Value; + } + NewValue.NewValue = CurrentValue; + NewValue.Value = Value; + return NewValue; +} + +// Build Buffs to Show +unsigned int CWorldServer::BuildBuffs( CCharacter* character ) +{ + BYTE buff1 = 0; + BYTE buff2 = 0; + BYTE buff3 = 0; + BYTE buff4 = 0; + //Build Debuffs and Buffs + //Up + if(character->Status->Attack_up != 0xff)//A_ATTACK: + buff2 += ATTACK_UP; + if(character->Status->Defense_up != 0xff)//A_DEFENSE: + buff2 += DEFENSE_UP; + if(character->Status->Magic_Defense_up != 0xff)//A_MDEFENSE_UP + buff2 += MDEFENSE_UP; + if(character->Status->Accury_up != 0xff)//A_ACCUR: + buff3 += HITRATE_UP; + if(character->Status->Dash_up != 0xff)//A_DASH: + buff1 += DASH_UP; + if(character->Status->Haste_up != 0xff)//A_HASTE: + buff2 += HASTE_UP; + if(character->Status->HP_up != 0xff)//A_HP: + buff1 += HP_UP; + if(character->Status->MP_up != 0xff)//A_MP: + buff1 += MP_UP; + if(character->Status->Critical_up != 0xff)//A_CRITICAL: + buff3 += CRITICAL_UP; + if(character->Status->Dodge_up != 0xff)//A_DODGE: + buff1 += DODGE_UP; + if(character->Status->ExtraDamage_up != 0xff)//A_Extra_Damage: + buff4 += DAMAGE_UP; + //Down + if(character->Status->Attack_down != 0xff) // A_ATTACK: + buff2 += ATTACK_DOWN; + if(character->Status->Defense_down != 0xff)//A_DEFENSE: + buff2 += DEFENSE_DOWN; + if(character->Status->Magic_Defense_down != 0xff) + buff2 += MDEFENSE_DOWN; + if(character->Status->Accury_down != 0xff)//A_ACCUR: + buff3 += HITRATE_DOWN; + if(character->Status->Dash_down != 0xff)//A_DASH: + buff1 += DASH_DOWN; + if(character->Status->Haste_down != 0xff)//A_HASTE: + buff2 += HASTE_DOWN; + if(character->Status->HP_down != 0xff)//A_HP: + buff1 += 0; + if(character->Status->MP_down != 0xff)//A_MP: + buff1 += 0; + if(character->Status->Critical_down != 0xff)//A_CRITICAL: + buff3 += CRITICAL_DOWN; + if(character->IsSummon( )) + buff4 += SUMMON; + if(character->Status->Stuned != 0xff)//A_STUN + buff4+= STUN; + if(character->Status->Poisoned != 0xff)//A_POISON + buff1+= POISONED; + if(character->Status->Muted != 0xff)//A_MUTE + buff3+= MUTED; + return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000); +} + +// Build Buffs to Show +unsigned int CWorldServer::BuildUpBuffs( CCharacter* character ) +{ + BYTE buff1 = 0; + BYTE buff2 = 0; + BYTE buff3 = 0; + BYTE buff4 = 0; + // Buffs + //Up + if(character->Status->Attack_up != 0xff)//A_ATTACK: + buff2 += ATTACK_UP; + if(character->Status->Defense_up != 0xff)//A_DEFENSE: + buff2 += DEFENSE_UP; + if(character->Status->Magic_Defense_up != 0xff)//A_MDEFENSE_UP + buff2 += MDEFENSE_UP; + if(character->Status->Accury_up != 0xff)//A_ACCUR: + buff3 += HITRATE_UP; + if(character->Status->Dash_up != 0xff)//A_DASH: + buff1 += DASH_UP; + if(character->Status->Haste_up != 0xff)//A_HASTE: + buff2 += HASTE_UP; + if(character->Status->HP_up != 0xff)//A_HP: + buff1 += HP_UP; + if(character->Status->MP_up != 0xff)//A_MP: + buff1 += MP_UP; + if(character->Status->Critical_up != 0xff)//A_CRITICAL: + buff3 += CRITICAL_UP; + if(character->Status->Dodge_up != 0xff)//A_DODGE: + buff1 += DODGE_UP; + if(character->Status->ExtraDamage_up != 0xff)//A_Extra_Damage: + buff4 += DAMAGE_UP; + return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000); +} +// Build Buffs to Show +unsigned int CWorldServer::BuildDeBuffs( CCharacter* character ) +{ + BYTE buff1 = 0; + BYTE buff2 = 0; + BYTE buff3 = 0; + BYTE buff4 = 0; + //Build Debuffs + //Down + if(character->Status->Attack_down != 0xff) // A_ATTACK: + buff2 += ATTACK_DOWN; + if(character->Status->Defense_down != 0xff)//A_DEFENSE: + buff2 += DEFENSE_DOWN; + if(character->Status->Magic_Defense_down != 0xff) + buff2 += MDEFENSE_DOWN; + if(character->Status->Accury_down != 0xff)//A_ACCUR: + buff3 += HITRATE_DOWN; + if(character->Status->Dash_down != 0xff)//A_DASH: + buff1 += DASH_DOWN; + if(character->Status->Haste_down != 0xff)//A_HASTE: + buff2 += HASTE_DOWN; + if(character->Status->HP_down != 0xff)//A_HP: + buff1 += 0; + if(character->Status->MP_down != 0xff)//A_MP: + buff1 += 0; + if(character->Status->Critical_down != 0xff)//A_CRITICAL: + buff3 += CRITICAL_DOWN; + if(character->IsSummon( )) + buff4 += SUMMON; + if(character->Status->Stuned != 0xff)//A_STUN + buff4+= STUN; + if(character->Status->Poisoned != 0xff)//A_POISON + buff1+= POISONED; + if(character->Status->Muted != 0xff)//A_MUTE + buff3+= MUTED; + return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000); +} + diff --git a/Src/World Server/Copy of battle.cpp b/Src/World Server/Copy of battle.cpp new file mode 100644 index 0000000..1edf3bd --- /dev/null +++ b/Src/World Server/Copy of battle.cpp @@ -0,0 +1,847 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +void CCharacter::DoAttack( ) +{ + //if(Status->Sleep <= 0xff) + //{ + // ClearBattle( Battle ); + // return; // can't take any action while we are asleep can we? + //} + //Log(MSG_INFO,"skill type %i selected. Chartype = %i",Battle->atktype, CharType); + if(Battle->iscasting == 1) + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + //Log(MSG_DEBUG,"Iscasting detected as true. Reset to false."); + if(CanAttack()) + { + if(IsPlayer()) + { + Log(MSG_DEBUG,"Should reach here once on normal attack. iscasting state = %i",Battle->iscasting); + } + ClearBattle( Battle ); + + StartAction(Enemy, NORMAL_ATTACK, 0); + + return; + } + else + { + return; + } + } + if(!CanAttack()) + { + return; + } + if(IsSummon()) + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL || (Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE)) + { + //Log(MSG_DEBUG,"No Enemy found"); + ClearBattle( Battle ); + return; + } + if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen + { + //Log(MSG_INFO,"Clearing Battle for this summon"); + ClearBattle( Battle ); + return; + } + if(Enemy->IsSummon()) + { + CMonster* thisMonster = reinterpret_cast(this); + if(thisMonster == NULL) + { + ClearBattle( Battle ); + return; + } + CMonster* otherMonster = reinterpret_cast(Enemy); + if(otherMonster == NULL) + { + ClearBattle( Battle ); + return; + } + if(thisMonster->owner == otherMonster->owner) + { + //Log(MSG_INFO,"Clearing Battle for this summon"); + ClearBattle( Battle ); + return; + } + } + } + CMap* map = GServer->MapList.Index[Position->Map]; + //Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype); + switch(Battle->atktype) + { + case NORMAL_ATTACK://normal attack + { + //if(!Status->CanAttack) + // return; //can't attack right now. + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + if(Enemy == this) + { + //Log(MSG_INFO,"WTF?? I AM trying to attack myself"); + ClearBattle( Battle ); + } + if(IsTargetReached( Enemy )) + { + //if(IsMonster()) + // Log(MSG_INFO,"Monster is trying to hit the player"); + NormalAttack( Enemy ); + if (Enemy->IsMonster()) // do monster AI script when monster is attacked. + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + //monster->DoAi(monster->thisnpc->AI, 3); + monster->DoAi(monster->monAI, 3); + } + } + } + break; + case SKILL_ATTACK://skill attack + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + if(IsTargetReached( Enemy, skill )) + { + SkillAttack( Enemy, skill ); + if (Enemy->IsMonster()) + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + monster->DoAi(monster->monAI, 3); + } + } + } + break; + case SKILL_BUFF://buffs + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + //Log(MSG_DEBUG,"this target is NULL"); + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //Log(MSG_DEBUG,"this skill is NULL"); + return; + } + if(IsTargetReached( Enemy, skill )) + { + //Log(MSG_DEBUG,"Skill successfully cast"); + BuffSkill( Enemy, skill ); + } + } + break; + case SKILL_AOE: + //case SKILL_SELF: //this is impossible. it can never be set + case AOE_TARGET: + { + //Log(MSG_DEBUG,"Called skill type %i",Battle->atktype); + CCharacter* Enemy = NULL; + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + ClearBattle( Battle ); + return; + } + if(Battle->atktype == AOE_TARGET) + { + float distance = GServer->distance( Position->current, Position->destiny ); + if(distance <= skill->range) + { + Position->destiny = Position->current; + AoeSkill( skill, Enemy ); + } + } + else + { + Position->skilltargetpos = Position->current; + AoeSkill( skill, NULL ); + } + } + break; + case BUFF_SELF: + { + // return; // can't cast skills right now + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + BuffSkill( this, skill ); + } + break; + case BUFF_AOE: + { + //Log(MSG_INFO,"BUFF AOE selected"); + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + + return; + } + AoeBuff( skill ); + } + break; + default: + return; + break; + } +} + + +// do normal attack +void CCharacter::NormalAttack( CCharacter* Enemy ) +{ + Enemy->OnBeAttacked( this ); + Position->destiny = Position->current; + // PY code + float mod = 0; + if(IsPlayer()) + { + mod = 23 + (Stats->Level / 1.5); + } + else + { + mod = 23 + (Stats->Level / 4); + + } + float attack = 0; + if(Stats->magicattack == 1) + { + attack = (float) Stats->Attack_Power / Enemy->Stats->Magic_Defense; + attack *= mod; + } + else + { + attack = (float) Stats->Attack_Power / Enemy->Stats->Defense; + attack *= mod; + } + //PY end + if(attack < 5) attack = 5; + float d_attack = attack / 100; + mod = GServer->RandNumber( 0, 10 ) * d_attack; + attack += mod; + long int hitpower = (long int)floor(attack + GServer->RandNumber(0, 10)); + if(IsPlayer( )) //temp fix to find balance btw monster and player + { + hitpower = (long int)floor(attack * (GServer->Config.PlayerDmg/100.00)); + hitpower+=((hitpower*(Stats->ExtraDamage))/100); + } + if(IsMonster( )) //temp fix to find balance btw monster and player + hitpower = (long int)floor(attack * (GServer->Config.MonsterDmg/100.00)); + bool critical = false; + if(hitpower <= 5) + { + hitpower = 5; + } + else + { + if(GServer->RandNumber(0,300)Critical) + { + hitpower = (long int)floor(hitpower * 1.5); + critical = true; + } + } + // dodge + unsigned int hitvalue = (unsigned int)floor((double)(Stats->Accury * 100 / Enemy->Stats->Dodge)); + if(hitvalue>100) hitvalue = 100; + if(GServer->RandNumber( 0, 100 )>hitvalue) + hitpower = 0; // dodged + if (hitpower > 0x7ff)//2047 packet size limit. + { + hitpower=0x7ff; + } + + //maybe this little line here will fix my ghosty problem + //they are definitely attacking. But no animation so maybe atktarget is empty + Battle->atktarget = Battle->target; + //Well it does make the damage show up at teh client but the animation is still dodgey + + if(IsMonster()) + { + //Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget); + } + //Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack ); + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, hitpower ); + Enemy->damagecounter += hitpower;// is for AI + } + Enemy->Stats->HP -= hitpower; + if(IsPlayer()) + Log(MSG_DEBUG,"Player attack did damage %i",hitpower); + if(IsMonster()) + Log(MSG_DEBUG,"Monster attack did damage %i",hitpower); + + + + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->atktarget ); + + if(Enemy->IsDead()) + { + CDrop* thisdrop = NULL; + ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) )); + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + thisdrop = Enemy->GetDrop( ); + if(thisdrop != NULL) + { + ADDFLOAT ( pak, thisdrop->pos.x*100 ); + ADDFLOAT ( pak, thisdrop->pos.y*100 ); + if(thisdrop->type == 1) + { + ADDWORD( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + { + ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); + } + ADDWORD ( pak, thisdrop->clientid ); + ADDWORD ( pak, thisdrop->owner ); + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, Enemy, thisdrop ); + OnEnemyDie( Enemy ); + } + else + { + ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0))); + GServer->SendToVisible( &pak, Enemy ); + } + ReduceABC( ); + Battle->lastAtkTime = clock( ); +} + +// do skill attack +bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill ) +{ + Position->destiny = Position->current; + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + UseAtkSkill( Enemy, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) + Stats->MP = 0; + //Battle->atktype = NORMAL_ATTACK; + //Battle->skilltarget = 0; + //Battle->atktarget = Battle->target; + //Battle->skillid = 0; + GServer->DoSkillScript( this, skill ); + Battle->lastAtkTime = clock( ); + if(Enemy->IsDead()) + ClearBattle(Battle); + Battle->iscasting = 1; + //Log(MSG_DEBUG,"Cast a skill. Iscasting set to true"); + return true; +} + +// do buff skill +bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill ) +{ + Position->destiny = Position->current; + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + //Log(MSG_DEBUG,"applied buff skill %i",skill->id); + UseBuffSkill( Target, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills? + if(!IsMonster()) + { + ClearBattle( Battle ); // clear battle for players when they use buff skills + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + } + else //Monsters need to be reset to normal attack and clear skill attacks. + { + //Battle->atktarget = Battle->target; + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + //StartAction(Target, NORMAL_ATTACK); + } + Battle->lastAtkTime = clock( ); + Battle->iscasting = 1; + return true; +} +//arnold +// do AoE skill +bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy ) +{ + Position->destiny = Position->current; + //Log(MSG_INFO,"Doing AOE Skill"); + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + CMap* map = GServer->MapList.Index[Position->Map]; + + //PY replacement code + if(IsPlayer() || IsSummon()) + { + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster == NULL) continue; + if(monster->clientid == clientid) continue; + if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill ); + } + } + if(IsMonster() && !IsSummon()) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + if(player->clientid == clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill ); + } + } + //PY end + + // this code appears to me to be completely wrong replaced with code above + /* + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster->clientid == clientid) continue; + if(IsSummon( ) || IsPlayer( )) + { + if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue; + } + else + { + if(!monster->IsSummon( )) continue; + } + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill ); + } + if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( ))) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player->clientid==clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill ); + } + }*/ + Battle->iscasting = 1; + //Log(MSG_DEBUG,"Cast a skill. Iscasting set to true"); + if(Enemy != NULL) + { + if(Enemy->IsDead( )) + ClearBattle( Battle ); + } + else + ClearBattle( Battle ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + Battle->lastAtkTime = clock( ); + + return true; +} + +bool CCharacter::AoeBuff( CSkills* skill ) +{ + //Log(MSG_INFO,"BUFF AOE called"); + Position->destiny = Position->current; + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + CMap* map = GServer->MapList.Index[Position->Map]; + + if(CharType != 2) //player || Summon || NPC. Anything that is not a monster + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + switch(skill->target) + { + case 1: //tPartyMember + if(player->Party->party == GetParty()) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member"); + if (player == this) //it is me so I had better cast it anyway ^-^ + { + UseBuffSkill( (CCharacter*)player, skill ); + } + else if(player->Party->party != NULL) // Don't cast the skill on players who are not in any party at all. + { + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + } + break; + case 2: //tClanMember + if (player->Clan == GetClan( ) && (player->Clan != 0 || player == this)) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member"); + if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) ) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 5: //tHostileCharacter + { + if (CharType == 2) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + } + break; + } + } + } + else if (CharType == 2) //monster + { + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster == NULL)continue; + switch(skill->target) + { + case tPartyMember: // party + break; + case 2: //tClanMember + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster"); + if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)monster, skill ); + } + break; + case tHostileCharacter: + break; + } + } + } + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + //Battle->atktarget = Battle->target; + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + ClearBattle( Battle ); + Battle->lastAtkTime = clock( ); + Battle->iscasting = 1; + return true; +} + + + +// use skill attack +void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff) +{ + long int skillpower = skill->atkpower; + UINT weapontype = 0; + float mod = 0; + float attack = 0; + if(IsPlayer()) + { + mod = 20 + (Stats->Level / 2); + } + else + { + mod = 15 + (Stats->Level / 2); + } + if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage + { + attack = skillpower / (Enemy->Stats->Magic_Defense / 2); + } + else + { + attack = skillpower / (Enemy->Stats->Defense / 2); + } + attack *= mod; + attack += (Stats->Attack_Power / 2); // let's add our AP to the skillpower to make it worthwhile to cast skills. + + skillpower = (int)attack; + bool bflag = false; + Enemy->OnBeAttacked( this ); + if(skillpower <= 0) skillpower = 0; + if(IsPlayer()) + { + skillpower += (int)(skillpower * (Stats->ExtraDamage))/100; + } + if (skill->atkpower == 0) + { + skillpower = 0; // If the skill has a 0 atk power, it isn't meant to damage + Log( MSG_INFO, "This skill does no damage" ); + //ClearBattle( Battle ); + return; // probably need to add some alternative code here for legitimate skills that have no attack power. + } + if(skillpower >2046)skillpower = 2046; + //Log(MSG_DEBUG,"Cast a skill. damage is %i",skillpower); + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, skillpower ); + Enemy->damagecounter += skillpower;// is for AI + } + Enemy->Stats->HP -= skillpower; + if(IsPlayer()) + Log(MSG_DEBUG,"Player Spell did damage %i",skillpower); + if(IsMonster()) + Log(MSG_DEBUG,"Monster spell did damage %i",skillpower); + BEGINPACKET( pak, 0x7b6 ); + ADDWORD ( pak, Enemy->clientid ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, 0x000007f8 ); + ADDBYTE ( pak, 0x00 ); + if (Enemy->Stats->HP<1) + { + ADDWORD ( pak, skillpower + 0x8000 ); + } + else + { + ADDWORD ( pak, skillpower + 0x2000 ); + } + if(Enemy->IsDead()) + { + //Log(MSG_DEBUG,"Enemy died"); + CDrop* thisdrop = NULL; + ADDDWORD ( pak, 16 ); + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + thisdrop = Enemy->GetDrop( ); + if(thisdrop != NULL) + { + ADDFLOAT ( pak, thisdrop->pos.x*100 ); + ADDFLOAT ( pak, thisdrop->pos.y*100 ); + if(thisdrop->type==1) + { + ADDWORD( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + { + ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); + } + ADDWORD ( pak, thisdrop->clientid ); + ADDWORD ( pak, thisdrop->owner ); + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, Enemy, thisdrop ); + OnEnemyDie( Enemy ); + } + else + { + ADDDWORD ( pak, 4 ); + GServer->SendToVisible( &pak, Enemy ); + bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) ); + + //Send (de)buff information to the whole world + if(skill->nbuffs > 0 && bflag) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Battle->skilltarget ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( ) ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Enemy ); + } + } + if (deBuff) return; + //Send skill animation to the world + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; +} + +// use buff skill +void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill ) +{ + bool bflag = false; + bflag = GServer->AddBuffs( skill, Target, GetInt( ) ); + if(skill->nbuffs > 0 && bflag == true) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( )/4 ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Target ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 1"); + } + + BEGINPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs); + //Log(MSG_INFO,"Target HP = %i",Target->Stats->HP); +} + +// Use a skill +bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target ) +{ + if (skill->atkpower > 0) + { + Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i", + ( skill->atkpower ), + ( (skill->range > 0) ? " AOE" : "" ), + ( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ), + ( skill->range ), + ( skill->target ) ); + } + for (int i = 0; i < 2; i++) { + Log(MSG_INFO, "Status ID: %i", skill->status[i]); + if (skill->status[i] == 0) continue; + CStatus* status = GServer->GetStatusByID(skill->status[i]); + if (status == NULL) continue; + /************************ + We'll probably need to use status->decrease (Figure out whether buf is up/down + status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat + (Recovery, continueing damage [1]) or Special (Status Effect [3]). + ************************/ + if (status->repeat == 1) // Continuous + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + } + else if (status->repeat == 2) // Once (Stat Boost) + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + + } else if (status->repeat == 3) // Status Effect (Poison,etc) + { + Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i], + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ( skill->duration ) ); + + } + } + return true; +} + + diff --git a/Src/World Server/Copy of charfunctions.cpp b/Src/World Server/Copy of charfunctions.cpp new file mode 100644 index 0000000..69f0bab --- /dev/null +++ b/Src/World Server/Copy of charfunctions.cpp @@ -0,0 +1,570 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +// return true if this character is monster +bool CCharacter::IsMonster( ) +{ + if(CharType == 2 || CharType == 3) + return true; + return false; +} + +// return true if this character is player +bool CCharacter::IsPlayer( ) +{ + if(CharType == 1) + return true; + return false; +} + +//return true if this character is an NPC +bool CCharacter::IsNPC( ) +{ + if(CharType == 4) + return true; + return false; +} + +// return true if HP is lower to 1 +bool CCharacter::IsDead( ) +{ + if(Stats->HP <= 0) + return true; + return false; +} + +// return true if this character is summon +bool CCharacter::IsSummon( ) +{ + if(this == NULL) return false; + if(CharType == 3)// this line is erroring. No idea why + return true; + return false; +} + +// Virtual function [return item drop] +CDrop* CCharacter::GetDrop( ) +{ + return NULL; +} + +// start action [attack] +void CCharacter::StartAction( CCharacter* Target, BYTE action, UINT skillid) +{ + + BEGINPACKET( pak, 0 ); + switch(action) + { + case NORMAL_ATTACK: + { + //Log( MSG_INFO, "case NORMAL_ATTACK"); + if(Target == NULL)return; + RESETPACKET( pak, 0x798 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, Stats->Move_Speed ); + ADDFLOAT ( pak, Target->Position->current.x * 100 ); + ADDFLOAT ( pak, Target->Position->current.y * 100 ); + Battle->target = Target->clientid; + Battle->atktarget = Target->clientid; + Battle->atktype = action; + Battle->skilltarget = 0; + Battle->bufftarget = 0; + Battle->skillid = 0; + Position->destiny = Target->Position->current; + Position->lastMoveTime = clock(); + } + break; + case SKILL_ATTACK: //Log( MSG_INFO, "case SKILL_ATTACK"); + case SKILL_BUFF: + { + if(Target == NULL)return; + RESETPACKET( pak, 0x7b3 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, skillid ); + ADDWORD ( pak, 0x0000 ); + ADDFLOAT ( pak, Target->Position->current.x * 100 ); + ADDFLOAT ( pak, Target->Position->current.y * 100 ); + Battle->target = Target->clientid; + + if(action == SKILL_ATTACK) + { + //Log( MSG_INFO, "case SKILL_ATTACK"); + Battle->skilltarget = Target->clientid; + Battle->bufftarget = 0; + } + else + { + //Log( MSG_INFO, "case SKILL_BUFF"); + Battle->bufftarget = Target->clientid; + Battle->skilltarget = 0; + } + + Battle->atktype = action; + Position->destiny = Target->Position->current; + Battle->skillid = skillid; + Position->lastMoveTime = clock(); + } + break; + case SKILL_AOE://Log( MSG_INFO, "case SKILL_AOE"); + case BUFF_SELF://Log( MSG_INFO, "case BUFF_SELF"); + case BUFF_AOE: + { + //Log( MSG_INFO, "case BUFF_AOE"); + RESETPACKET( pak, 0x7b2); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, skillid ); + Battle->atktype = action; + Battle->skillid = skillid; + //Log( MSG_INFO, "BUFF_AOE packet sent"); + } + break; + case AOE_TARGET: + { + //Log( MSG_INFO, "case AOE_TARGET"); + //Target is NULL for this function when cast by a player + //Target is NOT NULL when it comes from a monster + if(Target != NULL) //monster attack + { + Position->skilltargetpos.x = Target->Position->current.x; + Position->skilltargetpos.y = Target->Position->current.y; + } + RESETPACKET( pak, 0x7b4 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, skillid ); + ADDFLOAT ( pak, Position->skilltargetpos.x * 100 ); + ADDFLOAT ( pak, Position->skilltargetpos.y * 100 ); + Battle->atktype = action; + Battle->skillid = skillid; + Battle->skilltarget = 0; + Battle->bufftarget = 0; + } + break; + default: + return; + break; + } + GServer->SendToVisible( &pak, this ); + Battle->contatk = true; + + //Log(MSG_INFO,"Battle Attacktype = %i",Battle->atktype); + return; +} + +bool CCharacter::IsOnBattle( ) +{ + if(Battle->atktype == 0) + { + if(Battle->iscasting == 1) + { + clock_t etime = clock() - Battle->lastAtkTime; + if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed ) + { + //Log(MSG_DEBUG,"Iscasting is true while NOT in battle. Reset to false."); + Battle->iscasting = 0; + return false; + } + } + return false; + } + switch(Battle->atktype) + { + case NORMAL_ATTACK: + { + if(Battle->atktarget != 0) return true; + } + break; + case SKILL_ATTACK: + case AOE_TARGET: + { + if(Battle->skilltarget != 0 && Battle->skillid != 0) return true; + } + break; + case SKILL_BUFF: + { + if(Battle->bufftarget != 0 && Battle->skillid != 0) return true; + } + break; + case SKILL_AOE: + //case SKILL_SELF: //no such skill type + case BUFF_SELF: + case BUFF_AOE: + { + if(Battle->skillid != 0) return true; + } + break; + default: return false; + } +} + +bool CCharacter::CanAttack( ) +{ + + clock_t etime = clock() - Battle->lastAtkTime; + if (Stats->Attack_Speed < 1) + { + Log( MSG_INFO, "weapon with no attackspeed."); + return false; + } + if(Battle->iscasting == 1) + { + if( etime < CLOCKS_PER_SEC * 25 / Stats->Attack_Speed ) //should make the delay 4 times longer + { + //if(IsPlayer()) + // Log(MSG_INFO,"Cannot use magic attack yet. Last attack time = %i",Battle->lastAtkTime); + return false; + } + else + { + Battle->iscasting = 0; + //if(IsPlayer()) + // Log(MSG_INFO,"OK Now we can use a new (magic or normal) attack."); + return true; + } + } + else //normal attack + { + if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed ) + { + //if(IsPlayer()) + // Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime); + return false; + } + //if(IsPlayer()) + // Log(MSG_INFO,"OK Now we can use a normal attack"); + } + return true; +} + +// return true if target is reached +bool CCharacter::IsTargetReached( CCharacter* Enemy, CSkills* skill ) +{ + CMap* map = GServer->MapList.Index[Position->Map]; + if(map == NULL)return false; + float distance = GServer->distance( Position->current, Enemy->Position->current ); + if(skill == NULL) //it's a normal attack + { + if(distance <= Stats->Attack_Distance) + { + return true; + } + else + { + Position->destiny = Enemy->Position->current; + } + } + else + { + if(distance <= skill->range) + { + return true; + } + else + { + Position->destiny = Enemy->Position->current; + } + } + //Log(MSG_INFO,"Monster cannot attack. Too far away"); + return false; +} + +// return true if the character is moving +bool CCharacter::IsMoving( ) +{ + if(Position->current.x == Position->destiny.x && Position->current.y == Position->destiny.y ) + return false; + return true; +} + +// VIRTUAL [add player damage] +void CCharacter::AddDamage( CCharacter* enemy, long int hitpower) +{ + +} + +// return character target +CCharacter* CCharacter::GetCharTarget( ) +{ + CCharacter* thistarget = GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->target ); + if (thistarget != NULL) + return thistarget; + //if I don't currently have a target then return the character who just hit me + return GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->hitby ); +} + +// VIRTUAL [reduce ammon/bullets/cannons] +void CCharacter::ReduceABC( ) +{ + +} + +// VIRTUAL [get extra buff value] +int CCharacter::GetEVBuff( ) +{ + return 0; +} + +// VIRTUAL [return intelligence] +unsigned int CCharacter::GetInt( ) +{ + return 15; +} + + +// Check For Debuffs +void CCharacter::RefreshBuff( ) +{ + bool bflag = false; + for( UINT i=0;i<30;i++) + { + if(MagicStatus[i].Buff == 0) continue; + clock_t etime = clock() - MagicStatus[i].BuffTime; + if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC ) + { + switch(MagicStatus[i].Status) + { + case 18: // attack power up + case 19: // attack power down + case 48: // attack power up + if(i<15) + Status->Attack_up = 0xff; + else + Status->Attack_down = 0xff; + Stats->Attack_Power = GetAttackPower( ); + break; + case 20: // def up + case 21: // def down + case 49: // def up + if(i<15) + Status->Defense_up= 0xff; + else + Status->Defense_down = 0xff; + Stats->Defense = GetDefense( ); + break; + case 24: //accuracy up + case 25: //accuracy down + case 51: //attack accuracy up. + if(i<15) + Status->Accury_up= 0xff; + else + Status->Accury_down = 0xff; + Stats->Accury = GetAccury( ); + break; + case 22: // macic resistance up + case 23: // magic resistance down + case 50: // magic resistance up + if(i<15) + Status->Magic_Defense_up = 0xff; + else + Status->Magic_Defense_down = 0xff; + Stats->Magic_Defense = GetMagicDefense( ); + break; + case 28: //dodge up + case 29: //dodge down + case 53: //dodge rate up + if(i<15) + Status->Dodge_up = 0xff; + else + Status->Dodge_down = 0xff; + Stats->Dodge = GetDodge( ); + break; + case 14: //dash + case 15: //slow + case 46: //movement speed increased + if(i<15) + Status->Dash_up = 0xff; + else + Status->Dash_down = 0xff; + Stats->Move_Speed = GetMoveSpeed( ); + break; + case 16: // haste attack + case 17: // slow attack + case 47: // attack speed up + if(i<15) + Status->Haste_up = 0xff; + else + Status->Haste_down = 0xff; + Stats->Attack_Speed = GetAttackSpeed( ); + break; + case 26: // crit up + case 27: // crit down + case 52: // crit up + if(i<15) + Status->Critical_up = 0xff; + else + Status->Critical_down = 0xff; + Stats->Critical = GetCritical( ); + break; + case 12: // max HP up + case 44: // max HP up + if(i<15) + Status->HP_up = 0xff; + else + Status->HP_down = 0xff; + Stats->MaxHP = GetMaxHP( ); + break; + case 13: // max MP up + case 45: // max MP up + if(i<15) + Status->MP_up = 0xff; + else + Status->MP_down = 0xff; + Stats->MaxMP = GetMaxMP( ); + break; + case 32: // faint + Status->Stuned = 0xff; + Status->CanAttack = true; + //printf("removing stun\n"); + break; + case 7: case 8: case 9: case 10: case 11: //poisoned + Status->Poisoned = 0xff; + //printf("removing poison\n"); + break; + case 30: // muted + Status->Muted = 0xff; + Status->CanCastSkill = true; + break; + case 31: // sleep May need to be fixed later to accomodate multiple status effects. + Status->Sleep = 0xff; + Status->CanMove = true; + Status->CanCastSkill = true; + Status->CanAttack = true; + break; + case 54: //A_GMExtra_Damage: + case 36: //A_Extra_Damage: + if(i<15) + { + Status->ExtraDamage_up = 0xff; + Stats->ExtraDamage = 0xff; + } + else + { + Status->ExtraDamage_down = 0xff; + Stats->ExtraDamage = 0xff; + } + + break; + case 56: // Taunt + Status->Taunt = 0xff; + break; + } + + MagicStatus[i].Status = 0; + MagicStatus[i].Buff = 0; + MagicStatus[i].BuffTime = 0; + MagicStatus[i].Duration = 0; + MagicStatus[i].Value = 0; + bflag = true; + } + else if (MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11 && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds + { + Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself + MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC; + MagicStatus[i].Duration -= 1; + //printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); + + //A bunch of messy code to send dmg packet + BEGINPACKET( pak, 0x7b6 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, 0 ); + ADDDWORD ( pak, 0x000007f8 ); + ADDBYTE ( pak, 0x00 ); + ADDDWORD ( pak, MagicStatus[i].Status ); + + //If Enemy is killed + if( IsDead()) + { + //printf("char died\n"); + CDrop* thisdrop = NULL; + ADDDWORD ( pak, 16 ); + if( !IsSummon( ) && !IsPlayer( )) + { + thisdrop = GetDrop( ); + if( thisdrop!=NULL) + { + ADDFLOAT ( pak, thisdrop->pos.x*100 ); + ADDFLOAT ( pak, thisdrop->pos.y*100 ); + if( thisdrop->type==1) + { + ADDDWORD( pak, 0xccccccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + { + ADDDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); + } + ADDWORD ( pak, thisdrop->clientid ); + ADDWORD ( pak, thisdrop->owner ); + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, this, thisdrop ); + } + + //If enemy is still alive + else + { + ADDDWORD ( pak, 4 ); + GServer->SendToVisible( &pak, this ); + } + } + } + if(bflag) + { + BEGINPACKET( pak,0x7b7 ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, GServer->BuildBuffs( this ) ); + GServer->SendToVisible( &pak, this ); + } +} + +// VIRTUAL [return party pointer] +CParty* CCharacter::GetParty( ) +{ + return NULL; +} + +// VIRTUAL [return clan pointer] +CLAN* CCharacter::GetClan( ) +{ + return NULL; +} + +bool CCharacter::IsAttacking( ) +{ + if(IsOnBattle( )) + { + switch(Battle->atktype) + { + case SKILL_BUFF: + case BUFF_SELF: + case BUFF_AOE: + return false; + } + } + else return false; + return true; +} diff --git a/Src/World Server/MainProcess.cpp b/Src/World Server/MainProcess.cpp new file mode 100644 index 0000000..55c02d0 --- /dev/null +++ b/Src/World Server/MainProcess.cpp @@ -0,0 +1,478 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Map Process +PVOID MapProcess( PVOID TS ) +{ + bool only_npc = false; + while(GServer->ServerOnline) + { + pthread_mutex_lock( &GServer->PlayerMutex ); + pthread_mutex_lock( &GServer->MapMutex ); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + only_npc = true; + else + only_npc = false; + //continue; + + if(!only_npc) + { + // Player update //------------------------ + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if(!player->Session->inGame && player->Session->Respawned == 1) //only set in pakUserDied on receiving respawn packet 0x753 + { + Log( MSG_INFO, "ingame: = %i respawned: = %i",player->Session->inGame,player->Session->Respawned); + if(!player->RespawnPlayer(player)) //so we respawn the player + { + Log( MSG_INFO, "Player not respawned correctly"); + } + else + { + Log( MSG_INFO, "Player Respawned" ); + } + player->Session->inGame = true; + player->Session->Respawned = 2; //testing toggle + } + if(player->IsDead( )) continue; + if(player->UpdateValues( )) //checks if player is on a cart. otherwise returns true + { + //if(player->Session->Respawned == 2){Log( MSG_INFO, "Running UpdatePosition" );} + player->UpdatePosition( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran UpdatePosition" );} + } + + if(player->IsOnBattle( )) + { + //if(player->Session->Respawned == 2){Log( MSG_INFO, "Player is in battle so call DoAttack" );} + player->DoAttack( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran DoAttack" );} + } + player->RefreshBuff( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran refreshbuff" );} + player->PlayerHeal( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran playerheal" );} + player->Regeneration( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran regeneration" );} + player->CheckPlayerLevelUP( ); + //if(player->Session->Respawned == 2){Log( MSG_INFO, "ran Check Levelup" );} + if(3600000 < (UINT)GServer->round((clock( ) - player->Session->LogTime))) //1 hour since login + { + //if(player->Session->Respawned == 2){Log( MSG_INFO, "updating logtime stuff" );} + player->Session->logtime += 1; + player->Session->TotalLogTime ++; + GServer->DB->QExecute("UPDATE accounts SET logtime = %i, totlogtime = %i WHERE id = %i", player->Session->logtime, player->Session->TotalLogTime, player->Session->userid); + player->Session->LogTime = clock(); + } + //if(player->Session->Respawned == 2){Log( MSG_INFO, "Checking autosave" );} + if( GServer->Config.AUTOSAVE == 1 ) + { + clock_t etime = clock() - player->lastSaveTime; + if( etime >= GServer->Config.SAVETIME*1000 ) + { + player->savedata( ); + player->lastSaveTime = clock(); + } + //if(player->Session->Respawned == 2){Log( MSG_INFO, "saved player data" );} + } + //if(player->Session->Respawned == 2){Log( MSG_INFO, "Checking ItemXPTimer" );} + if(player->Stats->ItemXPTimer > 0) // does the player have a timed XPRate buff? + { + clock_t etime = clock() - player->Stats->ItemXPTime; + if(etime >= player->Stats->ItemXPTimer) // turn off timed XPRate buff when time exceeds timer + { + player->Stats->ItemXPTime = 0; + player->Stats->ItemXPRate = 1; + } + //if(player->Session->Respawned == 2){Log( MSG_INFO, "set XPRates and stuff" );} + } + player->Session->Respawned = 0; //resetting respawned status + } + // Monster update //------------------------ + pthread_mutex_lock( &map->MonsterMutex ); + //spawn TD monsters + unsigned nextmon = map->TDMobList[map->TDNextSpawn]; + if(nextmon != 0) + { + //Log(MSG_DEBUG,"Spawning TD mobs"); + clock_t etime = clock() - map->lastTDSpawnTime; + if(etime >= map->TDMobDelay) // check if elapsed time is greater than spawn delay + { + //spawn the next monster on the list + //Log(MSG_DEBUG,"Spawning TD mob of type %i at position %i in the list for map %i",nextmon, map->TDNextSpawn, map->id); + map->AddMonster(nextmon, GServer->WPList[map->id][1].pos, 0, 0, 0, 1, 0, 999 ); //spawn a TD mob with no AI + //Log(MSG_DEBUG,"Spawned mob in addmonster"); + map->TDMobList[map->TDNextSpawn] = 0; //clear the spawn + //Log(MSG_DEBUG,"cleared the spawn"); + map->TDNextSpawn++; //move the pointer + //Log(MSG_DEBUG,"moved the pointer"); + if(map->TDNextSpawn > 100)map->TDNextSpawn = 0; + map->lastTDSpawnTime = clock(); + } + } + //TD spawn end + + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* monster = map->MonsterList.at(j); + + //UINT thistimer = monster->AItimer; + clock_t etime = clock() - monster->lastAiUpdate; + //if(etime < monster->AItimer) continue; //not time to run AI for this monster yet + if(monster->hitcount == 0xFF)//this is a delay for new monster spawns this might olso fix invisible monsters(if they attack directly on spawning the client dosn't get the attack packet(its not in it's visible list yet)) + { + monster->hitcount = 0; + //monster->DoAi(monster->thisnpc->AI, 0); + monster->DoAi(monster->monAI, 0); + monster->lastAiUpdate=clock(); + } + + //still need this for special spawns + if(!map->IsNight( ) && monster->Status->nightonly)// if day, delete all night time monsters + { + //Log( MSG_INFO, "Night Only monster deleted. Type %i", monster->montype); + map->DeleteMonster( monster, true, j ); + continue; + } + if(map->IsNight() && monster->Status->dayonly) + { + //Log( MSG_INFO, "Day Only monster deleted. Type %i", monster->montype); + map->DeleteMonster( monster, true, j ); + continue; + } + //Do TD stuff + if(monster->monAI == 999 && etime >= monster->AItimer) //It's a TD mob + { + monster->UpdatePosition( ); + float distance = GServer->distance( monster->Position->current, monster->Position->destiny ); + //Log( MSG_INFO, "Found a TD monster %f from target. Waypoint type = %i stance %i speed %i", distance,GServer->WPList[map->id][monster->NextWayPoint].WPType,monster->Stats->stance, monster->Stats->Move_Speed); + //Log( MSG_INFO, "monster location X: %f Y: %f",monster->Position->current.x, monster->Position->current.y); + //Log( MSG_INFO, "WP location X: %f Y: %f ", GServer->WPList[map->id][monster->NextWayPoint].pos.x,GServer->WPList[map->id][monster->NextWayPoint].pos.y); + if(distance < 1) //monster has reached it's destination + { + switch(GServer->WPList[map->id][monster->NextWayPoint].WPType) + { + case 1: //Start waypoint + case 2: //regular waypoint. Increment waypoint counter. Set detiny to next waypoint + { + //Log( MSG_INFO, "Monster reached waypoint %i type: %i", monster->NextWayPoint,GServer->WPList[map->id][monster->NextWayPoint].WPType); + monster->NextWayPoint++; + monster->Position->destiny.x = GServer->WPList[map->id][monster->NextWayPoint].pos.x; + monster->Position->destiny.y = GServer->WPList[map->id][monster->NextWayPoint].pos.y; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x * 100 ); + ADDFLOAT ( pak, monster->Position->destiny.y * 100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); //should be walking + GServer->SendToVisible(&pak, monster); + } + break; + case 3: //Final waypoint. do stuff. suicide + { + //Log( MSG_INFO, "Reached final waypoint"); + //monster->Stats->HP = 0; + map->DeleteMonster( monster, true, j ); + //Log( MSG_INFO, "monster successfully deleted"); + continue; + } + break; + default: + Log( MSG_INFO, "WPType %i not recognized",GServer->WPList[map->id][monster->NextWayPoint].WPType); + break; + } + } + else + { + //send move packet again to try to synchronise the monster movement + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x * 100 ); + ADDFLOAT ( pak, monster->Position->destiny.y * 100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); //should be walking + GServer->SendToVisible(&pak, monster); + } + monster->lastAiUpdate = clock(); + } + //end TD stuff + + if(!monster->PlayerInRange( )) continue; + //if(!monster->UpdateValues( )) continue; + monster->UpdatePosition( ); + if(monster->IsOnBattle( )) + { + monster->DoAttack( ); // why was this commented? Monsters were not attacking + //monster->DoAi(monster->thisnpc->AI, 2); //don't use thisnpc->AI any more. + if(etime >= monster->AItimer) + { + monster->DoAi(monster->monAI, 2); + monster->lastAiUpdate = clock(); + } + //Log(MSG_INFO,"Monster type: %i current HP: %i",monster->montype, monster->Stats->HP); + } + else + { + //monster->DoAi(monster->thisnpc->AI, 1); + if(etime >= monster->AItimer) + { + monster->DoAi(monster->monAI, 1); + monster->lastAiUpdate = clock(); + } + } + monster->RefreshBuff( ); + if (monster->IsSummon()) + { + monster->SummonUpdate(monster,map, j); + continue; + } + if(monster->IsDead()) + { + if(clock() - monster->DeathDelayTimer > GServer->Config.DeathDelay) + { + //Log(MSG_DEBUG,"Found dead monster montype %i",monster->montype); + monster->OnDie( ); //all this does is give exp + //Log(MSG_DEBUG,"back from giving exp"); + monster->DoAi(monster->monAI, 5); + //Log(MSG_DEBUG,"ran AI"); + map->DeleteMonster( monster, true, j ); + //Log(MSG_DEBUG,"deleted monster"); + continue; + } + /*else + { + Log(MSG_DEBUG,"Dead monster found. waiting for death delay timer"); + }*/ + } + } + } + if(only_npc) + pthread_mutex_lock( &map->MonsterMutex ); + + //AIP for NPCs + for(UINT j=0;jNPCList.size();j++) + { + CNPC* npc = map->NPCList.at(j); + if(npc->thisnpc->AI != 0) + { + CAip* script = NULL; + for(unsigned k=0; k < GServer->AipList.size(); k++) + { + if (GServer->AipList.at(k)->AInumber == npc->thisnpc->AI) + { + script = GServer->AipList.at(k); + break; + } + } + if(script == NULL) + { + //Log( MSG_WARNING, "Invalid AI script for AI %i", npc->thisnpc->AI ); + continue; + } + UINT thistimer = script->minTime * 1000; //seems to be set in seconds in AIP + if(thistimer<(UINT)GServer->round((clock( ) - npc->lastAiUpdate))) //check AIP conditions when the timer calls for it + { + CNPCData* thisnpc = GServer->GetNPCDataByID( npc->npctype ); + if(thisnpc == NULL) + { + Log( MSG_WARNING, "Invalid montype %i", npc->npctype ); + continue; + } + CMonster* monster = new (nothrow) CMonster( npc->pos, npc->npctype, map->id, 0, 0 ); + monster->thisnpc = thisnpc; + monster->CharType = 4; + int AIP = monster->thisnpc->AI; + int lma_previous_eventID = npc->thisnpc->eventid; + monster->DoAi(monster->thisnpc->AI, 1); + //check if eventID changed, if we do it in AIP conditions / actions, it just fails... + if (lma_previous_eventID != npc->thisnpc->eventid) + { + //Log(MSG_WARNING,"Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !", npc->npctype, lma_previous_eventID, monster->thisnpc->eventid, map->id, npc->thisnpc->eventid); + npc->thisnpc->eventid = monster->thisnpc->eventid; + BEGINPACKET( pak, 0x790 ); + ADDWORD ( pak, npc->clientid ); + ADDWORD ( pak, npc->thisnpc->eventid ); + GServer->SendToMap(&pak, map->id); + } + + + if(AIP == GServer->Config.AIWatch) + Log(MSG_DEBUG,"NPC AI number %i successfully run",monster->thisnpc->AI); + if(monster->IsOnBattle()) + { + //Log(MSG_DEBUG,"NPC %i is registered as in battle",monster->thisnpc->id); + monster->DoAttack( ); + } + //map->DeleteMonster(monster); + //delete monster; + npc->lastAiUpdate = clock(); + if(AIP == GServer->Config.AIWatch) + Log(MSG_DEBUG,"LastAiUpdate clock reset"); + } + } + + //LMA: Sometimes another NPC does the job for you. + if(npc->thisnpc->eventid != GServer->ObjVar[npc->npctype][0]) + { + int new_event_id = GServer->ObjVar[npc->npctype][0]; + //Log(MSG_WARNING,"(2)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,npc->thisnpc->eventid,new_event_id,map->id,npc->thisnpc->eventid); + npc->thisnpc->eventid = new_event_id; + //LMA: We have to change the event ID here since we didn't send the clientID :( + BEGINPACKET( pak, 0x790 ); + ADDWORD ( pak, npc->clientid ); + ADDWORD ( pak, npc->thisnpc->eventid ); + GServer->SendToMap(&pak,map->id); + } + } + + pthread_mutex_unlock( &map->MonsterMutex ); + } + pthread_mutex_unlock( &GServer->MapMutex ); + pthread_mutex_unlock( &GServer->PlayerMutex ); + #ifdef _WIN32 + Sleep(GServer->Config.MapDelay); + #else + usleep(GServer->Config.MapDelay); + #endif + } + pthread_exit( NULL ); + return 0; +} + +// Visibility Process +PVOID VisibilityProcess(PVOID TS) +{ + while(GServer->ServerOnline) + { + pthread_mutex_lock( &GServer->PlayerMutex ); + pthread_mutex_lock( &GServer->MapMutex ); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + map->CleanDrops( );//moved for test + map->RespawnMonster( );//moved for test + if( map->PlayerList.size()<1 ) + continue; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if(!player->Session->inGame) continue; + if(!player->VisiblityList()) Log(MSG_WARNING, "Visibility False: %u", player->clientid ); + + } + } + pthread_mutex_unlock( &GServer->MapMutex ); + pthread_mutex_unlock( &GServer->PlayerMutex ); + #ifdef _WIN32 + Sleep(GServer->Config.VisualDelay); + #else + usleep(GServer->Config.VisualDelay); + #endif + } + pthread_exit(NULL); + return 0; +} + +// World Process +PVOID WorldProcess( PVOID TS ) +{ + while( GServer->ServerOnline ) + { + pthread_mutex_lock( &GServer->MapMutex ); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + continue; + map->UpdateTime( ); + pthread_mutex_lock( &map->DropMutex ); + //map->CleanDrops( ); + pthread_mutex_unlock( &map->DropMutex ); + pthread_mutex_lock( &map->MonsterMutex ); + //map->RespawnMonster( ); + pthread_mutex_unlock( &map->MonsterMutex ); + } + pthread_mutex_unlock( &GServer->MapMutex ); + GServer->RefreshFairy( ); + #ifdef _WIN32 + Sleep(GServer->Config.WorldDelay); + #else + usleep(GServer->Config.WorldDelay); + #endif + } + pthread_exit(NULL); + return 0; +} + +// Shutdown Server Process +void ShutdownServer(PVOID TS) +{ + int minutes = (int)TS; + #ifdef _WIN32 + Sleep(minutes*60000); + #else + usleep(minutes*60000); + #endif + Log( MSG_INFO, "Saving User Information... " ); + GServer->DisconnectAll(); + Log( MSG_INFO, "Waiting Process to ShutDown... " ); + GServer->ServerOnline = false; + int status,res; + res = pthread_join( GServer->WorldThread[0], (PVOID*)&status ); + if(res) + { + Log( MSG_WARNING, "World thread can't be joined" ); + } + else + { + Log( MSG_INFO, "World thread closed." ); + } + res = pthread_join( GServer->WorldThread[1], (PVOID*)&status ); + if(res) + { + Log( MSG_WARNING, "Visibility thread can't be joined" ); + } + else + { + Log( MSG_INFO, "Visibility thread closed." ); + } + res = pthread_join( GServer->MapThread[0], (PVOID*)&status ); + if(res) + { + Log( MSG_WARNING, "Map thread can't be joined" ); + } + else + { + Log( MSG_INFO, "Map thread closed." ); + } + Log( MSG_INFO, "All Threads Closed." ); + GServer->isActive = false; + pthread_exit(NULL); +} diff --git a/Src/World Server/Monster.cpp b/Src/World Server/Monster.cpp new file mode 100644 index 0000000..0fb0187 --- /dev/null +++ b/Src/World Server/Monster.cpp @@ -0,0 +1,195 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" + +// update attack values and destiny position +bool CMonster::UpdateValues( ) +{ + return true; //bypass for AIP + /*if(IsSummon( )) + { + CPlayer* thisclient = GetOwner( ); + if(thisclient!=NULL)if(thisclient->Stats->HP>0) + { + if(!IsOnBattle( ) && thisclient->IsAttacking( )) + { + Battle->target = thisclient->Battle->target; + Battle->atktarget = Battle->target; + Battle->atktype = NORMAL_ATTACK; + Battle->contatk = true; + CCharacter* Enemy = GetCharTarget( ); + if(Enemy!=NULL) + StartAction( Enemy, NORMAL_ATTACK ); + } + else + if(!IsOnBattle( )) + { + Position->source = thisclient->Position->current; + float distance = GServer->distance( Position->destiny , thisclient->Position->current ); + if((distance>15 && !IsOnBattle()) || distance>50) + Move( ); + } + } + else + { + UnspawnMonster( ); + return false; + } + } + if(!IsMoving( ) && !IsOnBattle( ) && CanMove( )) + { + clock_t etime = clock() - Position->lastMoveTime; + if(etime > 20*CLOCKS_PER_SEC) + Move( ); + } + if(!IsSummon( )) + { + CPlayer* player = GetNearPlayer( 10 ); + if(player!=NULL) + OnEnemyOnSight( (CCharacter*)player ); + CCharacter* Enemy = GetCharTarget( ); + if(Enemy!=NULL) + { + //need a skill timer here. It casts all teh time otherwise. + int randv = GServer->RandNumber(1, 100); + if(randv < 50 || GServer->Config.MobSkill == 0)//normal attack + { + StartAction( Enemy, NORMAL_ATTACK ); + } + else //skill attack + { + CSkills* skill = GServer->GetSkillByID(GServer->Config.MobSkill); + switch (skill->type) + { + case 6: + Log( MSG_DEBUG, "Monster used an attack skill. id = %i",GServer->Config.MobSkill); + StartAction( Enemy, SKILL_ATTACK, GServer->Config.MobSkill); + break; + case 17: + Log( MSG_DEBUG, "Monster used an AOE skill. id = %i",GServer->Config.MobSkill); + StartAction( Enemy, SKILL_AOE, GServer->Config.MobSkill); + break; + } + } + } + }*/ + if(IsMonster( )) + {} + return true; +} + +// Spawn a monster +void CMonster::SpawnMonster( CPlayer* player, CMonster* thismon ) +{ + BEGINPACKET( pak, 0x792 ); + ADDWORD ( pak, clientid ); + ADDFLOAT ( pak, Position->current.x*100 ); + ADDFLOAT ( pak, Position->current.y*100 ); + ADDFLOAT ( pak, Position->destiny.x*100 ); + ADDFLOAT ( pak, Position->destiny.y*100 ); + /* + if(IsDead( )) + { + ADDWORD ( pak, 0x0003 ); + ADDWORD ( pak, 0x0000 ); + } + else if(IsOnBattle( )) + { + ADDWORD ( pak, 0x0002 ); + ADDWORD ( pak, Battle->target ); + } + else if(IsMoving( )) + { + ADDWORD ( pak, 0x0001 ); + ADDWORD ( pak, 0x0000 ); + } + else + { + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + } + */ + ADDBYTE ( pak, 0x00 ); + ADDDWORD ( pak, 0x00000000 ); + + ADDDWORD ( pak, Stats->HP ); + if(thismon->owner != player->clientid) + { + CMap* map = GServer->MapList.Index[Position->Map]; + if(IsSummon( ) && map->allowpvp!=0) {ADDDWORD( pak, 0x00000064 );} //Hostil + else if (IsSummon( ) && map->allowpvp==0) {ADDDWORD ( pak, 0x00000000 );}//Friendly + else {ADDDWORD( pak, 0x00000064 );} //Hostil + } + else {ADDDWORD( pak, 0x00000000 );}//Friendly + ADDDWORD( pak, GServer->BuildBuffs( this ) ); + ADDWORD ( pak, montype ); + ADDWORD ( pak, 0x0000 ); + if(IsSummon( )) + { + ADDWORD( pak, owner ); + ADDWORD( pak, 0x0000 ); //id del skill (si es summon de skill) + } + player->client->SendPacket( &pak ); +} + +// UnSpawn a Monster Why the fuck do we need this?????????? +bool CMonster::UnspawnMonster( ) +{ + Log( MSG_INFO, "Unspawning monster %i ",this->montype); + GServer->MapList.Index[Position->Map]->DeleteMonster( this, true ); + return true; +} + +// Move a mob (random place) +void CMonster::Move( ) +{ + Position->destiny = GServer->RandInCircle( Position->source, 5 ); + Position->lastMoveTime = clock(); + //ClearBattle( Battle ); + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, Stats->Move_Speed ); //speed + ADDFLOAT ( pak, Position->destiny.x*100 ); + ADDFLOAT ( pak, Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + //ADDBYTE ( pak, 0x01 ); //replaced for AIP + ADDBYTE ( pak, Stats->stance ); + GServer->SendToVisible(&pak, this); +} + +// Move a mob (specified point) +void CMonster::MoveTo( fPoint nPos, bool randcircle ) +{ + if(randcircle) Position->destiny = GServer->RandInCircle( nPos, 5 ); + else Position->destiny = nPos; + Position->lastMoveTime = clock(); + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, 0x0000 ); // ??? + ADDWORD ( pak, Stats->Move_Speed ); + ADDFLOAT ( pak, Position->destiny.x*100 ); + ADDFLOAT ( pak, Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + //ADDBYTE ( pak, 0x01 ); // replaced for AIP + ADDBYTE ( pak, Stats->stance); + GServer->SendToVisible(&pak, this ); +} diff --git a/Src/World Server/MonsterDamage.cpp b/Src/World Server/MonsterDamage.cpp new file mode 100644 index 0000000..3b13448 --- /dev/null +++ b/Src/World Server/MonsterDamage.cpp @@ -0,0 +1,21 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" diff --git a/Src/World Server/MonsterEvents.cpp b/Src/World Server/MonsterEvents.cpp new file mode 100644 index 0000000..ae5ab90 --- /dev/null +++ b/Src/World Server/MonsterEvents.cpp @@ -0,0 +1,435 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" + +// called when a monster is attacked [attack/use atk skill/use buff/run/summon]//Enemy is the one attacking? +bool CMonster::OnBeAttacked( CCharacter* Enemy ) +{ + + Battle->hitby = Enemy->clientid; + if(!IsOnBattle( )) + { + CMap* map = GServer->MapList.Index[Position->Map]; + map->UpdateSpawnCounter( montype, Status->spawnid, 3); // update the counters + if(thisnpc->aggresive == 0) //prevent monsters with zero aggresion fighting back. + return true; + //StartAction( Enemy, NORMAL_ATTACK, 0 ); // temporary removal for testing + } + return true; +} + +// called when a monster die [give exp/give extra drop] +bool CMonster::OnDie( ) +{ + /* Don't need this now. AIP takes care of it. + CMap* map = GServer->MapList.Index[Position->Map]; + if(map->ghost!=0) + { + if((map->IsNight( ) || map->ghost==2) && !IsGhost( ) && !IsGhostSeed( ) && !IsSummon( )) + { + UINT gs = GServer->RandNumber( 0, 100 ); + if(gs<30) // 30% / 70% + { // spawn a ghost seed [for now will be 701 [level 1 ghost seed] ] + map->AddMonster( 701, Position->current, 0, Position->respawn, 0 ); + } + } + }*/ + + GServer->GiveExp( this ); + return true; +} +// called when we create a new monster [attack players?] +bool CMonster::OnSpawn( bool Attack ) +{ + if(IsGhost( ) && Attack ) + { + CPlayer* player = GetNearPlayer( ); + if(player == NULL) // no players or too far + return true; + StartAction( (CCharacter*)player, NORMAL_ATTACK, 0 ); + } + return true; +} + +// [not]called when a monster is almost dead [run/use only skills/call a healer? :S xD] +bool CMonster::OnAlmostDie( ) +{ + return true; +} + +// called when a enemy is on sight +bool CMonster::OnEnemyOnSight( CCharacter* Enemy ) +{ + //return true //enable for AI stuff + clock_t etime = clock() - lastSighCheck; + if(etime<5000) return true; + if(!IsOnBattle( )) + { + if(thisnpc->aggresive>1) + { + UINT aggro = GServer->RandNumber(2,15); + if(thisnpc->aggresive>=aggro && !IsGhostSeed( )) + { + StartAction( (CCharacter*) Enemy, NORMAL_ATTACK, 0 ); + } + else + if(IsGhostSeed( ) || thisnpc->aggresive>5) + MoveTo( Enemy->Position->current, true ); + } + lastSighCheck = clock(); + } + return true; +} + +// called when enemy die +bool CMonster::OnEnemyDie( CCharacter* Enemy ) +{ + Position->destiny = Position->source; + ClearBattle( Battle ); + MoveTo( Position->source ); + return true; +} + +bool CMonster::OnFar( ) +{ + if(!IsSummon())return true; + Position->destiny = Position->source; + ClearBattle( Battle ); + MoveTo( Position->source ); + return true; +} +/*bool CMonster::Guardiantree(CMonster* monster,CMap* map) +{ + return true; + if(Stats->HP>9000)monster->hitcount=0; + switch (monster->hitcount) + { + case 0: + if(Stats->HP<9000) + { + for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, 0, 10 ); + newmonster->hitcount=0; + } + monster->hitcount=1; + } + break; + case 1: + if(Stats->HP<8000) + { + /* for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + }*/ + /*monster->hitcount=2; + } + break; + case 2: + if(Stats->HP<7000) + { + for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + } + monster->hitcount=3; + } + break; + case 3: + if(Stats->HP<6000) + { + /* for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + }*/ + /*monster->hitcount=4; + } + break; + case 4: + if(Stats->HP<5000) + { + for(unsigned char i=0;i<5;i++) + { + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + } + monster->hitcount=5; + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GServer->GetNewClientID(); + thisdrop->type = 2; // ITEM + thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); + thisdrop->posMap = monster->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = 1; + thisdrop->item.count = thisdrop->amount; + thisdrop->owner = 0; + thisdrop->item.itemtype=10; + thisdrop->item.itemnum=rand()%4+10; + map->AddDrop( thisdrop ); + } + break; + case 5: + if(Stats->HP<4000) + { + /* for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + }*/ + /*monster->hitcount=6; + } + break; + case 6: + if(Stats->HP<3000) + { + for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + } + monster->hitcount=7; + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GServer->GetNewClientID(); + thisdrop->type = 2; // ITEM + thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); + thisdrop->posMap = monster->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = 1; + thisdrop->item.count = thisdrop->amount; + thisdrop->owner = 0; + thisdrop->item.itemtype=10; + thisdrop->item.itemnum=rand()%4+29; + map->AddDrop( thisdrop ); + } + break; + case 7: + if(Stats->HP<2000) + { + /* for(unsigned char i=0;i<5;i++){ + fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); + CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=0; + }*/ + /*monster->hitcount=8; + } + break; + default: + monster->hitcount=9; + + } + return true; +} +*/ +/*bool CMonster::Scarab(CMonster* monster,CMap* map) +{ + return true; + fPoint position; + CMonster* othermon; + CMonster* newmonster; + if (monster->hitcount>5)monster->hitcount=0; + switch (monster->hitcount) + { + case 0: + position = GServer->RandInCircle( monster->Position->current, 1 ); + newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=1; + GServer->ClearClientID( monster->clientid ); + for(UINT i=0;iMonsterList.size();i++) + { + othermon = map->MonsterList.at(i); + if(othermon==monster) + { + map->MonsterList.erase( map->MonsterList.begin()+i ); + delete monster; + } + } + break; + case 1: + position = GServer->RandInCircle( monster->Position->current, 1 ); + newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); + newmonster->hitcount=2; + GServer->ClearClientID( monster->clientid ); + for(UINT i=0;iMonsterList.size();i++) + { + othermon = map->MonsterList.at(i); + if(othermon==monster) + { + map->MonsterList.erase( map->MonsterList.begin()+i ); + delete monster; + } + } + break; + case 2: + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GServer->GetNewClientID(); + thisdrop->type = 2; // ITEM + thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); + thisdrop->posMap = monster->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = rand()%3+1; + thisdrop->item.count = thisdrop->amount; + thisdrop->owner = 0; + thisdrop->item.itemtype=12; + thisdrop->item.itemnum=rand()%10+11; + map->AddDrop( thisdrop ); + GServer->ClearClientID( monster->clientid ); + for(UINT i=0;iMonsterList.size();i++) + { + othermon = map->MonsterList.at(i); + if(othermon==monster) + { + map->MonsterList.erase( map->MonsterList.begin()+i ); + delete monster; + } + } + break; + } + return true; +}*/ +bool CMonster::SummonUpdate(CMonster* monster, CMap* map, UINT j) +{ + BEGINPACKET( pak, 0x799 ); + clock_t etime = clock() - lastDegenTime; + CPlayer* ownerclient = monster->GetOwner( ); + if( etime >= 4 * CLOCKS_PER_SEC && Stats->HP > 0 ) + { + Stats->HP -= 4; + lastDegenTime = clock(); + if (Stats->HP < 1||ownerclient == NULL||ownerclient->IsDead())//orphan check + { + Log( MSG_INFO, "Deleting orphan summon %i (summonupdate)",monster->montype); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, monster->clientid ); + ADDDWORD ( pak, 0x8005 ); + GServer->SendToVisible( &pak, monster ); + map->DeleteMonster( monster, true, j ); + return true; + } + else + if (montype > 800 && montype < 811) + { + /* + CPlayer* ownerclient = monster->GetOwner( ); + float distance = 0x9; + for(UINT i=0;iMapList.Index[Position->Map]->PlayerList.size();i++) + { + CPlayer* thisclient = GServer->MapList.Index[Position->Map]->PlayerList.at(i); + float tempdist = GServer->distance( Position->current, thisclient->Position->current ); + if(tempdistStats->HP += ((((montype-800)*5)+((ownerclient->Stats->Level)/10))+10); + thisclient->Stats->MP += (((montype-800)*5)+((ownerclient->Stats->Level)/25)); + BEGINPACKET( pak, 0x7b2); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x0b9d ); + ADDBYTE ( pak, 0x06 ); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x7b5); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x0b9d+0x3000+((ownerclient->Stats->Level%4)*0x4000) ); + ADDBYTE ( pak, ownerclient->Stats->Level/4 ); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x0b9d ); + thisclient->client->SendPacket(&pak); + } + }*/ + } + // SET MONSTER HEALTH + //hopefully this will 100% control the health of summons from the server end + BEGINPACKET( pak1, 0x79f ); + ADDWORD( pak1, clientid ); + ADDWORD( pak1, Stats->HP ); + ADDWORD( pak1, 0 ); + //thisclient->client->SendPacket( &pak ); + GServer->SendToVisible( &pak1, monster ); + } + else + if (Stats->HP < 1) + { + Log( MSG_INFO, "Deleting dead summon %i (summonupdate)",monster->montype); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, monster->clientid ); + ADDDWORD ( pak, 0x8005 ); + GServer->SendToVisible( &pak, monster ); + map->DeleteMonster( monster, true, j ); + } + + return true; +} + +void CMonster::DoAi(int ainumber,char type)//ainumber is monster->AI type is add=0 idle=1 attacking=2 attacked=3 after killing target=4 hp<1=5 +{ + CAip* script = NULL; + int AIWatch = GServer->Config.AIWatch; + int aiindex = (ainumber*0x10000)+(type*0x100); + if(type == 5) + { + //Log(MSG_INFO,"Monster died. Activating AI type 5"); + } + for(unsigned j=0; j < GServer->AipList.size(); j++) + { + if (GServer->AipList.at(j)->AipID == aiindex) + { + script = GServer->AipList.at(j); + + //if(ainumber == AIWatch)Log(MSG_DEBUG, "Record count = %i",script->recordcount[type]); + if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition type: %i Index: %i condition count %i", type, aiindex, script->ConditionCount); + int success = AI_SUCCESS; //needs to be AI_SUCCESS otherwise would not perform conditionless actions + int thisaction = 0; + for (dword i = 0; i < script->ConditionCount; i++) + { + int command = script->Conditions[i]->opcode; + if (command > 30 || command < 0) continue; + success = (*GServer->aiCondFunc[command])(GServer, this, script->Conditions[i]->data); + if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition %03u returned %d", command, success); + if (success == AI_FAILURE) + break; + } + if (success == AI_SUCCESS) + { + for (dword i = 0; i < script->ActionCount; i++) + { + int command = script->Actions[i]->opcode; + if (command > 38 || command < 0) continue; + success = (*GServer->aiActFunc[command])(GServer, this, script->Actions[i]->data); + if(ainumber == AIWatch)Log(MSG_DEBUG, "aiAction: %03u returned %d", command, success); + } + if(success == AI_SUCCESS) + return; //automatically return after performing the first successful action + } + aiindex++; + } + else if(GServer->AipList.at(j)->AipID > aiindex)return; + } + return; +} diff --git a/Src/World Server/MonsterStats.cpp b/Src/World Server/MonsterStats.cpp new file mode 100644 index 0000000..7319620 --- /dev/null +++ b/Src/World Server/MonsterStats.cpp @@ -0,0 +1,195 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" + +// Get Mob Attack Power +unsigned int CMonster::GetAttackPower( ) +{ + unsigned int attack; + attack = thisnpc->atkpower; + if(thisnpc->weapon != 0) + attack += GServer->EquipList[8].Index[thisnpc->weapon]->attackpower; + if(Status->Attack_up!=0xff) + attack += MagicStatus[Status->Attack_up].Value; + if(Status->Attack_down!=0xff) + attack -= MagicStatus[Status->Attack_down].Value; + return attack; +} + +// Get Monster Defense +unsigned int CMonster::GetDefense( ) +{ + unsigned int defense; + defense = thisnpc->defense; + if(thisnpc->subweapon!=0) + defense += GServer->EquipList[SUBWEAPON].Index[thisnpc->subweapon]->defense; + if(Status->Defense_up!=0xff) + defense += MagicStatus[Status->Defense_up].Value; + if(Status->Defense_down!=0xff) + defense -= MagicStatus[Status->Defense_down].Value; + if(defense == 0)defense = 1; //hopefully stops some division by zero errors. + return defense; +} + +// Get Monster atk speed +unsigned int CMonster::GetAttackSpeed( ) +{ + unsigned int aspeed = (unsigned int)thisnpc->atkspeed; + if(aspeed == 0) aspeed = 1; // prevent a /0 when hunting dragon egg + if(Status->Haste_up!=0xff) + aspeed += MagicStatus[Status->Haste_up].Value; + if(Status->Haste_down!=0xff) + aspeed -= MagicStatus[Status->Haste_down].Value; + return aspeed; +} + +// Get Monster Movement speed +unsigned int CMonster::GetMoveSpeed( ) +{ + unsigned int mspeed = 0; + if (Stats->stance == 0) //mWalking. All other stances produce running animation at client + { + mspeed = thisnpc->wspeed; + } + else + { + mspeed = thisnpc->rspeed; + } + if(Status->Dash_up!=0xff) + mspeed += MagicStatus[Status->Dash_up].Value; + if(Status->Dash_down!=0xff) + mspeed -= MagicStatus[Status->Dash_down].Value; + return mspeed; +} + +// return dodge +unsigned int CMonster::GetDodge( ) +{ + UINT dodge = 0; + dodge = thisnpc->dodge; + if(dodge == 0) dodge = thisnpc->defense; // prevent a /0 for hawk skill + if(Status->Dodge_up!=0xff) + dodge += MagicStatus[Status->Dodge_up].Value; + if(Status->Dodge_down!=0xff) + dodge -= MagicStatus[Status->Dodge_down].Value; + if(dodge == 0)dodge = 1; //hopefully stops some division by zero errors. + return dodge; +} + +// return accury +unsigned int CMonster::GetAccury( ) +{ + UINT hitrate = 0; + hitrate = thisnpc->hitrate; + if(Status->Accury_up!=0xff) + hitrate += MagicStatus[Status->Accury_up].Value; + if(Status->Accury_down!=0xff) + hitrate -= MagicStatus[Status->Accury_down].Value; + return hitrate; +} + +// return magic defense +unsigned int CMonster::GetMagicDefense( ) +{ + UINT mdef = 0; + mdef = thisnpc->magicdefense; + if(Status->Magic_Defense_up!=0xff) + mdef += MagicStatus[Status->Magic_Defense_up].Value; + if(Status->Magic_Defense_down!=0xff) + mdef -= MagicStatus[Status->Magic_Defense_down].Value; + if(mdef == 0)mdef = 1; //hopefully stops some division by zero errors. + return mdef; +} + +// return magic attack +unsigned int CMonster::GetMagicAttack( ) +{ + return thisnpc->magicattack; +} + + +// return max hp +unsigned int CMonster::GetMaxHP( ) +{ + UINT MaxHP = 0; + MaxHP = thisnpc->hp; + if(owner == 0) + MaxHP *= thisnpc->level; + //if(MaxHP == 0) + //MaxHP = thisnpc->shp; // Bullshit. npc->shp is the flying height of a monster such as a bee. Dumb ass!! + return MaxHP; +} + +unsigned int CMonster::GetCritical( ) +{ + return 60; //60 = 20% of 300 our critical probability +} + +float CMonster::GetAttackDistance( ) +{ + return thisnpc->atkdistance; +} + +unsigned int CMonster::GetXPRate( ) +{ + return 0; +} + +unsigned int CMonster::GetItemDropRate( ) +{ + return 0; +} + +unsigned int CMonster::GetItemDropCountRate( ) +{ + return 0; +} + +unsigned int CMonster::GetZulyDropRate( ) +{ + return 0; +} + + +// set monster stats values +bool CMonster::SetStats( ) +{ + if(thisnpc == NULL) return false; + Stats->Level = thisnpc->level; + Stats->Attack_Power = GetAttackPower( ); + Stats->Defense = GetDefense( ); + Stats->xprate = GetXPRate( ); + Stats->itemdroprate = GetItemDropRate( ); + Stats->zulydroprate = GetZulyDropRate( ); + Stats->Attack_Speed = GetAttackSpeed( ); + Stats->Move_Speed = GetMoveSpeed( ); + Stats->Dodge = GetDodge( ); + Stats->Accury = GetAccury( ); + Stats->Critical = GetCritical( ); + Stats->Magic_Defense = GetMagicDefense( ); + Stats->magicattack = GetMagicAttack( ); + Stats->Attack_Distance = GetAttackDistance( ); + for(int i=0;i<20;i++) + { + AIVar[i] = 0; + } + return true; +} diff --git a/Src/World Server/Party.cpp b/Src/World Server/Party.cpp new file mode 100644 index 0000000..72818cc --- /dev/null +++ b/Src/World Server/Party.cpp @@ -0,0 +1,375 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Party Actions [invite/leave/kick] +bool CWorldServer::pakPartyActions( CPlayer* thisclient, CPacket* P ) +{ + unsigned int action = GETBYTE((*P),0); + CMap* map = MapList.Index[thisclient->Position->Map]; + if(map == NULL)return true; + switch(action) + { + case 0x00://Invita a new party + case 0x01://invite a existent party + { + UINT clientid = GETWORD((*P),1); + if(thisclient->Party->party!=NULL) + { + if(thisclient->Party->party->Members.size()>=thisclient->Party->party->Capacity) + { + SendSysMsg( thisclient, "Party is full" ); + return true; + } + } + CPlayer* otherclient = map->GetPlayerInMap( clientid );// have to be in same map + if(otherclient == NULL) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x00 );//No encontro el ID + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + if(otherclient->Party->party != NULL) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x01 );//No puede ser solicitado (ya tiene party) + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + if(abs(otherclient->Stats->Level-thisclient->Stats->Level)>(Config.Partygap+1)) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x07 );//Level inapropiado + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + BEGINPACKET( pak, 0x7d0 ); + ADDBYTE ( pak, action ); + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, 0x00 ); + otherclient->client->SendPacketCpy( &pak ); + } + break; + case 0x02://Leave Party + { + if(thisclient->Party->party == NULL) + return true; + CParty* party = thisclient->Party->party; + if(party == NULL) + return true; + + bool pflag = false; + if(!party->RemovePlayer( thisclient )) //if this player is not in this party + return true; + if(party->Members.size()>1) + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* thismember = party->Members.at(i); + if(thismember == NULL) + return true; + BEGINPACKET( pak, 0x7d2 ); + ADDWORD ( pak, 0xff00 ); + ADDDWORD ( pak, thisclient->CharInfo->charid ); + if(!pflag) + { + ADDDWORD( pak, thismember->CharInfo->charid ); + if(thisclient->Party->IsMaster) + thismember->Party->IsMaster = true; + pflag = true; + } + thismember->client->SendPacketCpy( &pak ); + } + } + else + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* thismember = party->Members.at(i); + if(thismember == NULL) + return true; + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x05 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + thismember->client->SendPacket( &pak ); + thismember->Party->party = NULL; + thismember->Party->IsMaster = true; + } + RemoveParty( party ); + delete party; + } + } + break; + case 0x03: // Give leader + { + if(thisclient->Party->party == NULL) + return true; + unsigned int charid = GETDWORD((*P),1); + if( !thisclient->Party->IsMaster || thisclient->CharInfo->charid == charid ) + return true; + CPlayer* otherclient = GetClientByCID( charid ); + if(otherclient == NULL) + return true; + BEGINPACKET(pak, 0x07d1) + ADDBYTE(pak, 8); + ADDWORD(pak, otherclient->clientid); + ADDWORD(pak, 0); + otherclient->Party->IsMaster = true; + thisclient->Party->IsMaster = false; + thisclient->Party->party->SendToMembers( &pak ); + } + break; + case 0x81: //Kick from party + { + unsigned int charid = GETDWORD((*P),1); + if(thisclient->Party->party==NULL) + return true; + if(!thisclient->Party->IsMaster || thisclient->CharInfo->charid==charid) + return true; + CParty* party = thisclient->Party->party; + if(party == NULL) + return true; + CPlayer* thismember = party->GetMemberByCharID( charid ); + if(thismember == NULL) + return true; + + BEGINPACKET( pak, 0x7d1 ); // Kicked from party message + ADDBYTE ( pak, 0x80 ); + ADDDWORD ( pak, thismember->CharInfo->charid ); + thismember->client->SendPacketCpy( &pak ); + RESETPACKET( pak, 0x7d2 ); + ADDWORD ( pak, 0xff00 ); + ADDDWORD ( pak, thismember->CharInfo->charid ); + bool pflag = false; + if(!party->RemovePlayer( thismember )) //if this player is not in this party + return true; + if(party->Members.size()>1) + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* othermember = party->Members.at(i); + if(othermember == NULL) + return true; + if(!pflag) + { + ADDDWORD( pak, othermember->CharInfo->charid ); + if(thismember->Party->IsMaster) + othermember->Party->IsMaster = true; + pflag = true; + } + othermember->client->SendPacketCpy( &pak ); + } + } + else + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* othermember = party->Members.at(i); + if(othermember == NULL) + return true; + RESETPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x05 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + othermember->client->SendPacketCpy( &pak ); + othermember->Party->party = NULL; + othermember->Party->IsMaster = true; + } + RemoveParty( party ); + delete party; + } + } + break; + default: + Log(MSG_WARNING,"Party unknown action: %i", action); + } + return true; +} + +// Party Manager +bool CWorldServer::pakPartyManager( CPlayer* thisclient, CPacket* P ) +{ + unsigned int action = GETBYTE((*P),0); + switch(action) + { + case 0x02://Acepto + { + if(thisclient->Party->party!=NULL)// have party + return true; + unsigned int clientid = GETWORD((*P),1); + CPlayer* otherclient = GetClientByID( clientid, thisclient->Position->Map ); + + if(otherclient == NULL) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x00 );//No encontro el ID + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + CParty* party = otherclient->Party->party; + if(party != NULL) + { + if(party->Members.size()>=party->Capacity) + { + SendSysMsg( thisclient, "Party is Full" ); + return true; + } + } + if(abs(otherclient->Stats->Level-thisclient->Stats->Level)>(Config.Partygap+1)) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x07 );//Level inapropiado + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x02 );//Acepto Party + ADDWORD ( pak, otherclient->clientid ); + ADDBYTE ( pak, 0x00 ); + otherclient->client->SendPacketCpy( &pak ); + if( party == NULL ) + { // new party + CParty* thisparty = new CParty; + thisparty->counted = false; + thisparty->Pending_Exp = 0; + thisparty->AddPlayer( otherclient ); + AddParty( thisparty ); + otherclient->Party->IsMaster = true; + party = thisparty; + } + //Send Party Level and Party Exp + RESETPACKET( pak, 0x7d4 ); // + ADDBYTE ( pak, party->PartyLevel ); + ADDDWORD ( pak, party->Exp ); + thisclient->client->SendPacketCpy( &pak ); + thisclient->Party->IsMaster = false; + // Send New Party Member info to other players + RESETPACKET( pak, 0x7d2 ); + ADDBYTE ( pak, party->Option ); + ADDBYTE ( pak, 0x01 ); + ADDDWORD ( pak, thisclient->CharInfo->charid ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisclient->Stats->MaxHP ); + ADDWORD ( pak, thisclient->Stats->HP ); + ADDDWORD ( pak, 0x00000000 ); + ADDDWORD ( pak, 0x0000000f ); + ADDWORD ( pak, 0x1388 ); + ADDSTRING ( pak, thisclient->CharInfo->charname ); + ADDBYTE ( pak, 0x00 ); + party->SendToMembers( &pak ); + + // Send To New Party Member the members List + RESETPACKET( pak, 0x7d2 ); + ADDBYTE ( pak, party->Option ); + ADDBYTE ( pak, party->Members.size() ); + for(int i=0;iMembers.size();i++) + { + CPlayer* member= party->Members.at(i); + if(member == NULL) + continue; + ADDDWORD ( pak, member->CharInfo->charid ); + ADDWORD ( pak, member->clientid ); + ADDWORD ( pak, member->Stats->MaxHP ); + ADDWORD ( pak, member->Stats->HP ); + ADDDWORD ( pak, 0x00000000 ); + ADDDWORD ( pak, 0x0000000f ); + ADDWORD ( pak, 0x1388 ); + ADDSTRING ( pak, member->CharInfo->charname ); + ADDBYTE ( pak, 0x00 ); + } + thisclient->client->SendPacketCpy( &pak ); + party->AddPlayer( thisclient ); + } + break; + case 0x04://No acepto + { + unsigned int clientid = GETWORD((*P),1); + CPlayer* otherclient = GetClientByID( clientid, thisclient->Position->Map ); + if(otherclient == NULL) + { + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x00 );//No encontro el ID + ADDWORD ( pak, clientid ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacketCpy( &pak ); + return true; + } + BEGINPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x04 );//No acepto + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, 0x00 ); + otherclient->client->SendPacketCpy( &pak ); + } + break; + default: + Log(MSG_WARNING,"Party Manager unknown action: %i", action); + } + return true; +} + +// Party Chat +bool CWorldServer::pakPartyChat( CPlayer* thisclient, CPacket* P ) +{ + CParty* party = thisclient->Party->party; + if(party == NULL) + return true; + BEGINPACKET( pak, 0x786 ); + ADDWORD ( pak, thisclient->clientid ); + ADDSTRING ( pak, P->Buffer ); + ADDBYTE ( pak, 0 ); + party->SendToMembers( &pak ); + return true; +} + +// Change Party Options +bool CWorldServer::pakPartyOption( CPlayer* thisclient, CPacket* P ) +{ + if(!thisclient->Party->IsMaster) + return true; + CParty* party = thisclient->Party->party; + if(party == NULL) + return true; + party->Option = GETBYTE((*P),0); + BEGINPACKET( pak, 0x7d7 ); + ADDBYTE ( pak, party->Option ); + ADDBYTE ( pak, 0x00 ); + party->SendToMembers( &pak ); + return true; +} + +unsigned int CWorldServer::GetMaxPartyExp( unsigned int partylevel ) +{ + unsigned int exp = 540; + exp += 100 * partylevel + ( partylevel-2*10 ); + return exp; +} diff --git a/Src/World Server/QuestActions.cpp b/Src/World Server/QuestActions.cpp new file mode 100644 index 0000000..4e4a380 --- /dev/null +++ b/Src/World Server/QuestActions.cpp @@ -0,0 +1,296 @@ +#include "main.h" + +dword GetRewardValue(dword function, dword amount, CWorldClient* client, word nDupCNT){ + switch(function){ + case 0: + { + dword tempVal = amount + 0x1E; + tempVal *= client->GetStat(sCharm) + 0x0a; + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->stats_base[sLevel] + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 1: + { + dword tempVal = (client->stats_base[sLevel] + 3) * amount; + tempVal *= (client->GetStat(sCharm) >> 1) + client->stats_base[sLevel] + 0x28; + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x2710; + } + break; + case 2: + return (amount * nDupCNT); + break; + case 3: + case 5: + { + dword tempVal = amount + 0x14; + tempVal *= client->GetStat(sCharm) + 0x0a; + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->stats_base[sLevel] + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 4: + { + dword tempVal = amount + 0x2; + tempVal *= (client->GetStat(sCharm) + client->stats_base[sLevel] + 0x28); + tempVal *= 0x28;//Fame + 0x28 + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x222E0; + } + break; + case 6: + { + dword tempVal = amount + 0x14; + tempVal *= client->GetStat(sCharm) + client->stats_base[sLevel]; + tempVal *= 0x14;//Fame + 0x14 + tempVal *= (100 & 0xFFFF);//World Rate + return (tempVal / 0x2DC6C0) + amount; + } + break; + } + return amount; +} + +//Update Quest +QUESTREWD(000){ + GETREWDDATA(000); + + switch(data->btOp){//0 remove, 1 start, 4 select + case 0: + { + for(dword i = 0; i < 10; i++){ + if(client->quest.quests[i].QuestID != data->iQuestSN) continue; + + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + break; + } + } + break; + case 1: + { + for(dword i = 0; i < 10; i++){ + if(client->quest.quests[i].QuestID == 0) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].Time = clock_t(); + break; + } + } + break; + case 4: + { + client->ActiveQuest = data->iQuestSN; + } + break; + } + return QUEST_SUCCESS; +} + +//Update Quest Items +QUESTREWD(001){ + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(002){ + return QUEST_SUCCESS; +} + +//Update Stats +QUESTREWD(003){ + GETREWDDATA(003); + for(dword i = 0; i < data->iDataCnt; i++){ + qword address = i * 0x0C; + address += (qword)data; + address += 4; + STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if(!OperateValues(curAbil->btOp, &client->stats_base[curAbil->iType], curAbil->iValue)) + return QUEST_FAILURE; + } + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(004){ + return QUEST_SUCCESS; +} + +//Give Reward +QUESTREWD(005){ + GETREWDDATA(005); + + switch(data->btTarget){ + case 0://EXP + { + client->basic.exp += GetRewardValue(data->btEquation, data->iValue, client, 0); + CPacket pakout(0x79b); + pakout.Add(client->basic.exp); + pakout.Add(client->stats_base[sStamina]); + pakout.Add(0); + server->SendPacket(client, &pakout); + } + break; + case 1://Zuly + client->inv.money += GetRewardValue(data->btEquation, data->iValue, client, 1);//dunno nDupCount for this one! + CPacket pakout(0x71D); + pakout.Add(client->inv.money); + server->SendPacket(client, &pakout); + break; + case 2://Item + { + CItem nItem; + nItem.type = data->iItemSN / 1000; + nItem.id = data->iItemSN - (nItem.type * 1000); + if(nItem.IsStackable()){ + nItem.count = GetRewardValue(data->btEquation, data->iValue, client, 0); + }else{ + nItem.itemdata = 0; + nItem.durability = 40; + nItem.lifespan = 1000; + nItem.appraised = 1; + } + client->AddItem(&nItem, true); + } + break; + } + + return QUEST_SUCCESS; +} + +//Restore HP/MP +QUESTREWD(006){ + return QUEST_SUCCESS; +} + +//Teleport +QUESTREWD(007){ + GETREWDDATA(007); + client->MoveMap(data->iZoneSN, (float)data->iX, (float)data->iY); + return QUEST_SUCCESS; +} + +//Spawn Monster +QUESTREWD(008){ + return QUEST_SUCCESS; +} + +//Execute Quest Trigger +QUESTREWD(009){ + return QUEST_SUCCESS; +} + +//Reset Stats +QUESTREWD(010){ + client->stats_base[sStatPoints] = 0; + + client->stats_base[sStrength] = 15; + client->stats_base[sDexterity] = 15; + client->stats_base[sIntelligence] = 15; + client->stats_base[sConcentration] = 15; + client->stats_base[sCharm] = 10; + client->stats_base[sSensibility] = 10; + + for(int i = 0; i <= client->stats_base[sLevel]; i++) + client->stats_base[sStatPoints] += (i + 9); + + return QUEST_SUCCESS; +} + +//Update Quest Var +QUESTREWD(011){ + return QUEST_SUCCESS; +} + +//NPC Speak +QUESTREWD(012){ + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(013){ + return QUEST_SUCCESS; +} + +//Learn Skill +QUESTREWD(014){ + + return QUEST_SUCCESS; +} + +//Set Quest Flag +QUESTREWD(015){ + GETREWDDATA(015); + client->quest.SetFlag(data->nSN, (data->btOp == 1)?true:false); + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(016){ + return QUEST_SUCCESS; +} + +//Reset All Quest Flags +QUESTREWD(017){ + memset(&client->quest.flags, 0, 244); + return QUEST_SUCCESS; +} + +//Send Announcement +QUESTREWD(018){ + return QUEST_SUCCESS; +} + +//Execute Quest Trigger in Other Map +QUESTREWD(019){ + return QUEST_SUCCESS; +} + +//PvP Status +QUESTREWD(020){ + return QUEST_SUCCESS; +} + +//Set Respawn Position +QUESTREWD(021){ + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(022){ + return QUEST_SUCCESS; +} + +//Raise Clan Grade +QUESTREWD(023){ + return QUEST_SUCCESS; +} + +//Clan Money +QUESTREWD(024){ + return QUEST_SUCCESS; +} + +//Clan Points +QUESTREWD(025){ + return QUEST_SUCCESS; +} + +//Clan Skill +QUESTREWD(026){ + return QUEST_SUCCESS; +} + +//Clan Contribution +QUESTREWD(027){ + return QUEST_SUCCESS; +} + +//Clan Teleportation +QUESTREWD(028){ + return QUEST_SUCCESS; +} \ No newline at end of file diff --git a/Src/World Server/QuestConditions.cpp b/Src/World Server/QuestConditions.cpp new file mode 100644 index 0000000..0e1e2d9 --- /dev/null +++ b/Src/World Server/QuestConditions.cpp @@ -0,0 +1,229 @@ +#include "main.h" + +int OperateValues(byte btOp, int* value1PTR, int value2){ + int value1 = *value1PTR; + switch(btOp){ + case 0: + return (value1 == value2); + case 1: + return (value1 <= value2); + case 2: + return (value1 >= value2); + case 3: + return (value1 > value2); + case 4: + return (value1 < value2); + case 5: + (*value1PTR) = value2; + return 1; + case 6: + (*value1PTR) = value1 + value2; + return 1; + case 7: + (*value1PTR) = value1 - value2; + return 1; + default: + return 0; + } +} + +//Check Quest +QUESTCOND(000){ + GETCONDDATA(000); + for(dword i = 0; i < 10; i++){ + if(client->quest.quests[i].QuestID == data->iQuestSN) + return QUEST_SUCCESS; + } + return QUEST_FAILURE; +} + +//Check Quest Variable +QUESTCOND(001){ + return QUEST_SUCCESS; +} + +//Check Quest Variable +QUESTCOND(002){ + return QUEST_SUCCESS; +} + +//Check Stats +QUESTCOND(003){ + GETCONDDATA(003); + for(int i = 0; i < data->iDataCnt; i++){ + qword address = i * 0x0C; + address += (qword)data; + address += 4; + STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if(!OperateValues(curAbil->btOp, &client->stats_base[curAbil->iType], curAbil->iValue)) + return QUEST_FAILURE; + } + return QUEST_SUCCESS; +} + +//Check Items +QUESTCOND(004){ + GETCONDDATA(004); + for(int i = 0; i < data->iDataCnt; i++){ + //QWord for 64bit safe pointers. + qword address = i * 0x10; + address += (qword)data; + address += 4; + STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address; + if(curItem->uiItemSN == 0 && curItem->iWhere != 0){ + int itemHead = client->inv.items[curItem->iWhere].itemhead; + if(!OperateValues(curItem->btOp, &itemHead, curItem->uiItemSN)) + return QUEST_FAILURE; + } + Log(MSG_INFO,"uiItemSN: %u iWhere: %u iRequestCnt: %u btOp: %u", curItem->uiItemSN, curItem->iWhere, curItem->iRequestCnt, curItem->btOp); + } + return QUEST_SUCCESS; +} + +//Check Party +QUESTCOND(005){ + return QUEST_SUCCESS; +} + +//Near Point +QUESTCOND(006){ + GETCONDDATA(006); + client->UpdatePosition(); + if(data->iZoneSN != 0){ + if(client->basic.map != data->iZoneSN) + return QUEST_FAILURE; + } + + float dx = client->basic.pos.x - (float)data->iX; + float dy = client->basic.pos.y - (float)data->iY; + float distance = sqrt((dx*dx) + (dy*dy)); + return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE; +} + +//World Time +QUESTCOND(007){ + return QUEST_SUCCESS; +} + +//Quest Time +QUESTCOND(008){ + return QUEST_SUCCESS; +} + +//Check Skill +QUESTCOND(009){ + GETCONDDATA(009); + int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE; + for(dword i = 0; i < 90; i++){ + if(client->skill.Skills[i] == data->iSkillSN1) + return (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE; + } + return (data->btOp)?QUEST_FAILURE:QUEST_SUCCESS; +} + +//Unknown +QUESTCOND(010){ + return QUEST_SUCCESS; +} + +//Quest Variable +QUESTCOND(011){ + return QUEST_SUCCESS; +} + +//Execute Trigger in Zone +QUESTCOND(012){ + return QUEST_SUCCESS; +} + +//Near NPC +QUESTCOND(013){ + return QUEST_SUCCESS; +} + +//Check Quest Flag +QUESTCOND(014){ + GETCONDDATA(014); + bool dataOp = (data->btOp)?true:false; + return (client->quest.GetFlag(data->nSN) == dataOp)?QUEST_SUCCESS:QUEST_FAILURE; +} + +//Unknown +QUESTCOND(015){ + return QUEST_SUCCESS; +} + +//Zone Time +QUESTCOND(016){ + return QUEST_SUCCESS; +} + +//NPC Variables +QUESTCOND(017){ + return QUEST_SUCCESS; +} + +//Time on Date +QUESTCOND(018){ + return QUEST_SUCCESS; +} + +//Time on Day +QUESTCOND(019){ + return QUEST_SUCCESS; +} + +//Unknown +QUESTCOND(020){ + return QUEST_SUCCESS; +} + +//Unknown +QUESTCOND(021){ + return QUEST_SUCCESS; +} + +//Check Server/Channel +QUESTCOND(022){ + return QUEST_SUCCESS; +} + +//In Clan +QUESTCOND(023){ + return QUEST_SUCCESS; +} + +//Clan Position +QUESTCOND(024){ + return QUEST_SUCCESS; +} + +//Clan Contribution +QUESTCOND(025){ + return QUEST_SUCCESS; +} + +//Clan Grade +QUESTCOND(026){ + return QUEST_SUCCESS; +} + +//Clan Points +QUESTCOND(027){ + return QUEST_SUCCESS; +} + +//Clan Funds +QUESTCOND(028){ + return QUEST_SUCCESS; +} + +//Clan Members +QUESTCOND(029){ + return QUEST_SUCCESS; +} + +//Clan Skills +QUESTCOND(030){ + return QUEST_SUCCESS; +} \ No newline at end of file diff --git a/Src/World Server/QuestConditions.h b/Src/World Server/QuestConditions.h new file mode 100644 index 0000000..04a7b81 --- /dev/null +++ b/Src/World Server/QuestConditions.h @@ -0,0 +1,200 @@ +#define QUESTCOND(cond) int QUEST_COND_ ## cond ## (class CWorldServer* server, class CPlayer* client, byte* raw) +typedef int (*fpQuestCond)(class CWorldServer*, class CPlayer*, byte*); +#define GETCONDDATA(cond) STR_COND_ ## cond ## * data = (STR_COND_ ## cond ## *)raw; + +QUESTCOND(000);QUESTCOND(001);QUESTCOND(002); +QUESTCOND(003);QUESTCOND(004);QUESTCOND(005); +QUESTCOND(006);QUESTCOND(007);QUESTCOND(008); +QUESTCOND(009);QUESTCOND(010);QUESTCOND(011); +QUESTCOND(012);QUESTCOND(013);QUESTCOND(014); +QUESTCOND(015);QUESTCOND(016);QUESTCOND(017); +QUESTCOND(018);QUESTCOND(019);QUESTCOND(020); +QUESTCOND(021);QUESTCOND(022);QUESTCOND(023); +QUESTCOND(024);QUESTCOND(025);QUESTCOND(026); +QUESTCOND(027);QUESTCOND(028);QUESTCOND(029); +QUESTCOND(030); + +struct STR_COND_000 { + int iQuestSN; +}; + +struct STR_COND_001 { + int iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_COND_002 { + int iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_COND_003 { + int iDataCnt; + STR_ABIL_DATA* CheckData; +}; + +struct STR_COND_004 { + int iDataCnt; + STR_ITEM_DATA* CheckData; +}; + +struct STR_COND_005 { + byte btIsLeader; + int iLevel; + byte btReversed; +}; + +struct STR_COND_006 { + int iZoneSN; + int iX; + int iY; + int iZ; + int iRadius; +}; + +struct STR_COND_007 { + dword ulTime; + dword ulEndTime; +}; + +struct STR_COND_008 { + dword ulTime; + byte btOp; +}; + +struct STR_COND_009 { + int iSkillSN1; + int iSkillSN2; + byte btOp; +}; + +struct STR_COND_010 { + byte btLowPcnt; + byte btHighPcnt; +}; + +struct STR_COND_011 { + union { + byte btWho; + word usWho; + }; + word nVarNo; + int iValue; + byte btOp; +}; + +struct STR_COND_012 { + union { + word iZone; + dword ulZone; + }; + int iX; + int iY; + union { + int iEventID; + dword m_HashEventOBJ; + }; +}; + +struct STR_COND_013 { + int iNpcNo; +}; + +struct STR_COND_014 { + word nSN; + byte btOp; +}; + +struct STR_COND_015 { + word nNumber1; + word nNumber2; +}; + +struct STR_COND_016 { + union { + byte btWho; + dword ulWho; + }; + dword ulTime; + dword ulEndTime; +}; + +struct STR_COND_017 { + STR_NPCVAR NpcVar1; + STR_NPCVAR NpcVar2; + byte btOp; +}; + +struct STR_COND_018 { + byte btDate; + byte btHour1; + byte btMin1; + byte btHour2; + byte btMin2; +}; + +struct STR_COND_019 { + byte btWeekDay; + byte btHour1; + byte btMin1; + byte btHour2; + byte btMin2; +}; + +struct STR_COND_020 { + int iNo1; + int iNo2; +}; + +struct STR_COND_021 { + union { + byte btSelObjType; + dword ulSelObjType; + }; + int iRadius; +}; + +struct STR_COND_022 { + word nX; + word nY; +}; + +struct STR_COND_023 { + byte btReg; +}; + +struct STR_COND_024 { + word nPOS; + byte btOP; +}; + +struct STR_COND_025 { + word nCONT; + byte btOP; +}; + +struct STR_COND_026 { + word nGRD; + byte btOP; +}; + +struct STR_COND_027 { + word nPOINT; + byte btOP; +}; + +struct STR_COND_028 { + word iMONEY; + byte btOP; +}; + +struct STR_COND_029 { + word nMemberCNT; + byte btOP; +}; + +struct STR_COND_030 { + word nSkill1; + word nSkill2; + byte btOP; +}; diff --git a/Src/World Server/QuestTrigger.h b/Src/World Server/QuestTrigger.h new file mode 100644 index 0000000..82170e9 --- /dev/null +++ b/Src/World Server/QuestTrigger.h @@ -0,0 +1,61 @@ +#pragma once +#include "worldserver.h" +class CQuestTrigger { +public: + struct SQuestDatum { + int size; + int opcode; + byte* data; + }; + + string TriggerName; + dword TriggerHash; + + byte CheckNext; + + dword ConditionCount; + SQuestDatum** Conditions; + + dword ActionCount; + SQuestDatum** Actions; +private: +}; + +struct STR_NPCVAR { + dword iNpcNo; + word nVarNo; + word unused; +}; + +struct STR_ITEM_DATA { + dword uiItemSN; + dword iWhere; + dword iRequestCnt; + byte btOp; +}; + +struct STR_QUEST_DATA { + union { + dword iType; + struct { + word m_wVarNO; + word m_wVarTYPE; + }; + }; + word nValue; + byte btOp; +}; + +struct STR_ABIL_DATA { + dword iType; + int iValue; + byte btOp; +}; + +#define QUEST_FAILURE 0 +#define QUEST_SUCCESS 1 + +int OperateValues(byte btOp, int* value1PTR, int value2); + +#include "QuestConditions.h" +#include "quest/QuestActions.h" diff --git a/Src/World Server/ServerFunctions.cpp b/Src/World Server/ServerFunctions.cpp new file mode 100644 index 0000000..f00fd38 --- /dev/null +++ b/Src/World Server/ServerFunctions.cpp @@ -0,0 +1,898 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// from Paul_T +bool CWorldServer::SendPM( CPlayer* thisclient, const char* Format, ... ) +{ + char buf[512]; + va_list ap; + va_start( ap, Format ); + vsprintf( buf, Format, ap ); + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, "Server" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, buf ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket(&pak); + va_end( ap ); + return true; +} + +// from Paul_T +bool CWorldServer::SendGlobalMSG( CPlayer* thisclient, const char* Format, ... ) +{ + char buf[512]; + va_list ap; + va_start( ap, Format ); + vsprintf( buf, Format, ap ); + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, thisclient->CharInfo->charname ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, buf ); + ADDBYTE( pak, 0 ); + SendToAll(&pak); + va_end( ap ); + return true; +} + +// NPC Announce to the server +bool CWorldServer::NPCAnnounce( const char msg[200], const char npc[50]) +{ + BEGINPACKET( pak, 0x702 ); + ADDSTRING( pak, npc ); + ADDSTRING( pak, "> " ); + ADDSTRING( pak, msg); + ADDBYTE( pak, 0x00); + SendToAll( &pak ); + return true; +} + +// NPC Whisper to a character +bool CWorldServer::NPCWhisper( CPlayer* thisclient, CMonster* thismon, const char msg[200], const char npc[50] ) +{ + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, npc ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, msg ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket(&pak); + return true; +} + +// NPC Shout to the current map +bool CWorldServer::NPCShout( int map, const char msg[200], const char npc[50] ) +{ + BEGINPACKET(pak, 0x0785); + ADDSTRING ( pak, npc ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, msg ); + ADDBYTE ( pak, 0 ); + SendToMap ( &pak, map ); + return true; +} + +bool CWorldServer::DoSkillScript( CCharacter* character, CSkills* thisskill ) +{ + try + { + if(thisskill == 0) + return false; + if(thisskill->script == 0) + return false; + if(thisskill->svalue1 == 0) + return false; + fPoint position = RandInCircle( character->Position->current, 1 ); + CMap* map = MapList.Index[character->Position->Map]; + map->AddMonster( thisskill->svalue1, position, character->clientid,0,1 ); + return true; + } + catch (...) + { + Log(MSG_WARNING, "Error in CWorldServer::DoSkillScript( CCharacter* character, CSkills* thisskill )(serverFunctions.cpp)" ); + return false; + } +} + +// Build Drop. New experimental method. +CDrop* CWorldServer::GetNewDrop( CMonster* thismon ) +{ + //Log(MSG_DEBUG,"Monster %i killed. FInding a drop",thismon->montype); + CPlayer* thisclient = GServer->GetClientByCID(thismon->MonsterDrop->firsthit); + if(thisclient == NULL)return NULL; + if( thisclient->Session->codedebug ) + SendPM(thisclient, "Drops function called after killing monster type %i",thismon->montype); + + int randv; + int i; + CDrop* newdrop = new (nothrow) CDrop; + + if(newdrop == NULL) + { + Log(MSG_WARNING, "newDrop is NULL [getdrop]" ); + return NULL; + } + CMDrops* thisdrop = NULL; + CMDrops* testdrop = NULL; + + newdrop->clientid = GetNewClientID( ); + newdrop->posMap = thismon->Position->Map; + newdrop->pos = RandInCircle( thismon->Position->current, 3 ); + //newdrop->droptime = time(NULL); + newdrop->owner = thismon->MonsterDrop->firsthit; + newdrop->thisparty = thismon->thisparty; + ClearItem(newdrop->item); + + + + int droprate = 80; // base drop percentage for monsters your own level + int DropFloor = Config.BASE_DROP_CHANCE; + int leveldif = (int)((float)thismon->thisnpc->level - (float)thisclient->Stats->Level); + float mod = droprate * 0.01 * leveldif; + int dropchance = int(droprate + mod); + if(dropchance < DropFloor) dropchance = DropFloor; //always a small chance of a drop even when the mob is more than 20 levels beneath your own + if(thismon->thisnpc->level == 1) + dropchance = 80; + //if( thisclient->Session->codedebug ) + // SendPM(thisclient, "Drop chance (based on level diference) = %i", dropchance ); + int droprand = GServer->RandNumber(0,100); + if (droprand > dropchance) + { + if( thisclient->Session->codedebug ) + SendPM(thisclient, "Drop rate check failed with %i. No drop this time",droprand); + return NULL; // no drop here. not this time anyway. + } + + + // Each monster has its own rates for zuly and item drops defined in the stb + // first check for Zuly drops + randv = RandNumber( 1, 100); + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "randv = %i. money = %i. zulyrate = %i. ", randv,thismon->thisnpc->money,thisclient->Stats->zulydroprate ); + //} + if(randv <= (thismon->thisnpc->money + thisclient->Stats->zulydroprate)) // zuly drop + { + newdrop->type = 1; //zuly + newdrop->amount = thismon->thisnpc->level * 5 * (Config.ZULY_RATE + thisclient->Stats->zulydroprate) + RandNumber( 1, 10 ); + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Zuly drop selected " ); + } + return newdrop; + } + //now check for item drops + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "randv = %i. Item = %i. Itemrate = %i", randv,thismon->thisnpc->item,thisclient->Stats->itemdroprate ); + //} + if(randv <= (thismon->thisnpc->item + thisclient->Stats->itemdroprate + thismon->thisnpc->money)) // Item drop + { + //OK got an item. Now we find which one + //Map drop or mob drop? map = 50% mob = 50%. Maybe should be configurable or modified by monster data? + int randv2 = RandNumber( 1, 100); + if(randv2 <= 50) //map drop + { + for(i=0;iMDropList.size();i++) + { + testdrop = GServer->MDropList.at(i); + if(testdrop == NULL) return NULL; + if(testdrop->Dropid == thismon->Position->Map) + { + thisdrop = testdrop; + } + } + + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Map drop selected for map %i. ", thismon->Position->Map ); + } + } + else // mob drop + { + for(i=0;iMDropList.size();i++) + { + testdrop = GServer->MDropList.at(i); + if(testdrop == NULL) return NULL; + if(testdrop->Dropid == thismon->thisnpc->dropid) + { + thisdrop = testdrop; + } + } + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Mob drop selected for dropid %i. ", thismon->thisnpc->dropid ); + } + } + if(thisdrop == NULL) + { + Log(MSG_WARNING, "Thisdrop is NULL [getdrop]" ); + return NULL; + } + newdrop->type = 2; //Item + dword randitem = RandNumber( 1, thisdrop->TotalChance ); + UINT itemindex = 0; + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "random chance %i", randitem); + //} + + i=0; + do + { + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "Index: %i Item id: %i chance: %i", i, thisdrop->item[i], thisdrop->RunningTotal[i]); + //} + i++; + } + while (randitem > thisdrop->RunningTotal[i]); + itemindex = i; + + if(thisdrop->item[itemindex] == 0) + { + Log(MSG_DEBUG, "item at itemindex %i does not exist",itemindex ); + return NULL; //no item listed for this dropid + } + //ok we found an item. Now we process it. + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "Found an item to drop itemindex: %i id: %i. ", itemindex, thisdrop->item[itemindex] ); + //} + if(thisdrop->item[itemindex] < 100000) + { + newdrop->item.itemtype = thisdrop->item[itemindex] / 1000; + newdrop->item.itemnum = thisdrop->item[itemindex] % 1000; + } + else + { + newdrop->item.itemtype = thisdrop->item[itemindex] / 1000000; + newdrop->item.itemnum = thisdrop->item[itemindex] % 1000000; + } + //if( thisclient->Session->codedebug ) + //{ + // SendPM(thisclient, "Item type = %i. item number = %i", newdrop->item.itemtype,newdrop->item.itemnum ); + //} + newdrop->item.lifespan = 10 + rand()%80; + switch( newdrop->item.itemtype ) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: + newdrop->item.type = GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->type; + newdrop->item.raretype = GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->raretype; + break; + case 10: + newdrop->item.type = GServer->UseList.Index[newdrop->item.itemnum]->type; + newdrop->item.raretype = 0; + break; + case 11: + newdrop->item.type = GServer->JemList.Index[newdrop->item.itemnum]->type; + newdrop->item.raretype = 0; + break; + case 12: + newdrop->item.type = GServer->NaturalList.Index[newdrop->item.itemnum]->type; + newdrop->item.raretype = 0; + break; + case 14: + newdrop->item.type = GServer->PatList.Index[newdrop->item.itemnum]->type; + newdrop->item.raretype = 0; + break; + default: + Log(MSG_DEBUG, "itemtype %i does not exist",newdrop->item.itemtype ); + return NULL; + break; + } + float dmod = 0; //random number from 0 to 100 made up of 5 sub numbers to keep + //the average value near to 50 + for(i=0; i<5; i++) + { + float r1 = rand()%20; + dmod += r1; + } + newdrop->item.durability = 10 + (int)dmod; + newdrop->item.appraised = true; + newdrop->item.stats = 0; + newdrop->item.count = 1; + + if( newdrop->item.itemtype == 8 || newdrop->item.itemtype == 9 ) + { + //This probability is now configurable + int psocked = rand()%101; //Probability of finding a socketed item + if( psocked < Config.SlotChance) //default should be around 5% needs to be rare + { + newdrop->item.socketed = true; + } + else + { + newdrop->item.socketed = false; + } + } + else + { + newdrop->item.socketed = false; + } + + float TmpMod = thisclient->Attr->Cha / 30; + int chamod = (int)floor(TmpMod); + if(chamod < 0) chamod = 0; + int basedrop = thisclient->Stats->itemdroprate; //Base number of items to be dropped. + int maxdrop = basedrop + Config.DropMultiple; + if(newdrop->item.itemtype == 11) //gems only get 1 + newdrop->item.count = 1; + if( newdrop->item.itemtype == 10 || newdrop->item.itemtype == 12 ) + { + newdrop->item.count = RandNumber(basedrop, maxdrop); + if(thismon->thisnpc->level == 1) + newdrop->item.count = 1; //limit the drop rate of items from level 1 event mobs + if(newdrop->item.count == 0) + newdrop->item.count = 1; + if(newdrop->item.type == 314) //skillbooks + newdrop->item.count = 1; + if(newdrop->item.itemtype == 12) + { + newdrop->item.count = RandNumber(1, 6) + RandNumber(0, thisclient->Stats->itemdropcountrate); //basic type 12 items + //some item types have their own rules + if(newdrop->item.type == 428) //special skill items such as mana jams and scrolls + newdrop->item.count = RandNumber(1, 3); + if(newdrop->item.itemnum > 300 && newdrop->item.itemnum < 360) //bullets get a lot higher count. + { + newdrop->item.count *= 10; + newdrop->item.count += 10; + } + } + } + else if( newdrop->item.itemtype >1 && newdrop->item.itemtype !=7 && newdrop->item.itemtype < 10) + { + // check to see if the item will be refined + int prefine = rand()%100; //Probability of finding a refined item + int refinechance = Config.RefineChance; + if(prefine < refinechance) // default = 5% + { + int refinelevel = rand()%101; //which level of refine do we actually get + if( refinelevel < 5) //default 5% + newdrop->item.refine = 4 * 16; + else if( refinelevel < 15 ) //10% + newdrop->item.refine = 3 * 16; + else if(refinelevel < 35 ) // 20% + newdrop->item.refine = 2 * 16; + else // 65% + newdrop->item.refine = 16; + } + else //95% + newdrop->item.refine = 0; + + int StatChance = Config.StatChance; + if(newdrop->item.raretype > 0) // it's a blue item + { + newdrop->item.durability += 10; //make blues a little more durable + newdrop->item.appraised = false; //blues need to be appraised + newdrop->item.stats = RandNumber(0, 29) * 10 + 1; // stats can only be 1,11,21,,,,,291 + } + else // not a blue item + { + int pstats = RandNumber(1, 100); + if( pstats < StatChance) // default 5% + newdrop->item.stats = RandNumber(0, 29) * 10 + 1; // stats can only be 1,11,21,,,,,291 + } + } + newdrop->item.gem = 0; + if(newdrop->item.count == 0) + newdrop->item.count = 1; //just in case this is causing bugged items. + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Dropped item type: %i ID: %i count %i", newdrop->item.itemtype, newdrop->item.itemnum,newdrop->item.count); + } + return newdrop; + + } + return NULL; + // did not qualify to drop anything this time +} + +// Build Drop the PY way +CDrop* CWorldServer::GetPYDrop( CMonster* thismon, UINT droptype ) +{ //if droptype = 1 then it is a normal drop. if it is 2 then it is a potential side drop. + //type 2 removed a it is now handled by AIP + //Log( MSG_INFO, "PYDrops function selected" ); + //Log( MSG_INFO, "monster is %i", thismon->montype ); + CDrop* newdrop = new (nothrow) CDrop; + /* + if(newdrop == NULL) + { + Log(MSG_WARNING, "Error allocing memory [getdrop]" ); + return NULL; + } + newdrop->clientid = GetNewClientID( ); + newdrop->posMap = thismon->Position->Map; + newdrop->pos = RandInCircle( thismon->Position->current, 3 ); + //newdrop->droptime = time(NULL); + newdrop->owner = thismon->MonsterDrop->firsthit; + newdrop->thisparty = thismon->thisparty; + ClearItem(newdrop->item); + + CPlayer* thisclient = GServer->GetClientByCID(thismon->MonsterDrop->firsthit); + if(thisclient == NULL)return NULL; + + // code to modify drop chance for different levels + //float charm = 0; + //float droprate = thismon->thisnpc->dropchance; + int droprate = Config.DROP_RATE; + int DropFloor = Config.BASE_DROP_CHANCE; + int leveldif = (int)((float)thismon->thisnpc->level - (float)thisclient->Stats->Level); + float mod = droprate * 0.01 * leveldif; + int dropchance = int(droprate + mod); + if(dropchance < DropFloor) dropchance = DropFloor; //always a small chance of a drop even when the mob is more than 20 levels beneath your own + if(thismon->thisnpc->level == 1) + dropchance = 80; + if( thisclient->Session->codedebug ) + SendPM(thisclient, "Drop rate (based on level diference) = %i", dropchance ); + int droprand = GServer->RandNumber(0,100); + if (droprand > dropchance) + { + if( thisclient->Session->codedebug ) + SendPM(thisclient, "Drop rate check failed with %i. No drop this time",droprand); + return NULL; // no drop here. not this time anyway. + } + int DropListSize = MDropList.size(); + CItemType prob[MDropList.size()]; + bool isdrop = false; + int n = 0; + int test = 0; + long int probmax = 0; + int itemnumber[MDropList.size()]; + int itemtype[MDropList.size()]; + int probability[MDropList.size()]; + int alternate[MDropList.size()][8]; + + if( thismon->IsGhost()) + { + // Stuff to do if the mob is a ghost of any type + int selection = 1 + rand()%10; + if( selection <= 5 ) + { + newdrop->type = 1; //Drop Zuly. + newdrop->amount = thismon->thisnpc->level * 10 * Config.ZULY_RATE + RandNumber( 1, 10 ); + return newdrop; + } + else + { + for(int i=0; itype = 2; + CMDrops* thisdrop = GServer->SkillbookList.at(i); + if(thisdrop->level_min <= thismon->thisnpc->level && thisdrop->level_max >= thismon->thisnpc->level) + { + itemnumber[n] = thisdrop->itemnum; + itemtype[n] = thisdrop->itemtype; + probability[n] = thisdrop->prob; + probmax += thisdrop->prob; + n++; + } + } + } + } + else // Stuff to do if the mob isn't a ghost + { + int dropmode = 0; + int randv = RandNumber( 1, 100); + // Each monster has its own rates for zuly and item drops defined in the stb + // We also add a small amount based on the player's ItemDropRate and ZulyDroprate. + dropchance = thismon->thisnpc->item + thismon->thisnpc->money + thisclient->Stats->itemdroprate + thisclient->Stats->zulydroprate; + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "randv = %i. total drop chance = %i. money = %i. zulyrate = %i. Item = %i. Itemrate = %i", randv,dropchance,thismon->thisnpc->money,thisclient->Stats->zulydroprate,thismon->thisnpc->item,thisclient->Stats->itemdroprate ); + } + if(randv > dropchance)return NULL; // did not qualify to drop anything this time + if(randv <= (thismon->thisnpc->money + thisclient->Stats->zulydroprate)) // zuly drop instead of item drop + { + newdrop->type = 1; + newdrop->amount = thismon->thisnpc->level * 5 * Config.ZULY_RATE + RandNumber( 1, 10 ); + return newdrop; + } + + int randomdrop = GServer->RandNumber(1, 100); + for(int i=0; iMDropList.at(i); + if(thisdrop->mob != 0 && thisdrop->mob != thismon->montype) + isdrop = false; + if(thisdrop->map != 0 && thisdrop->map != thismon->Position->Map) + isdrop = false; + if(thisdrop->level_max > thisdrop->level_min && (thismon->thisnpc->level < thisdrop->level_min || thismon->thisnpc->level > thisdrop->level_max)) + isdrop = false; + + test = GServer->RandNumber(1, 10000); + if(thisdrop->prob < test) + isdrop = false; + if(isdrop == true) + { + itemnumber[n] = thisdrop->itemnum; + itemtype[n] = thisdrop->itemtype; + alternate[n][0] = 0; + for(int j=1;i<8;i++) + { + alternate[n][j] = thisdrop->alt[j]; + } + n++; + } + } + } + int newn = n; + if(n == 0) + return NULL; + int maxitems = n; + // randomize the item from the list + n = GServer->RandNumber(0, maxitems); + newdrop->item.itemnum = itemnumber[n]; + newdrop->item.itemtype = itemtype[n]; + newdrop->type = 2; + if( thisclient->Session->codedebug ) + { + for(int j=0;jitem.itemtype,newdrop->item.itemnum); + } + + newdrop->item.lifespan = 10 + rand()%80; + float dmod = 0; //random number from 0 to 100 made up of 5 sub numbers to keep + //the average value near to 50 + for(int i=0; i<5; i++) + { + float r1 = rand()%20; + dmod += r1; + } + newdrop->item.durability = 10 + (int)dmod; + if( newdrop->item.itemtype == 8 || newdrop->item.itemtype == 9 ) + { + //This probability is now configurable from WorldServer.conf + int psocked = rand()%101; //Probability of finding a socketed item + if( psocked < Config.SlotChance) //default should be around 5% needs to be rare + { + newdrop->item.socketed = true; + } + else + { + newdrop->item.socketed = false; + } + } + else + { + newdrop->item.socketed = false; + } + //Log( MSG_INFO, "Socket are OK"); + newdrop->item.appraised = false; + newdrop->item.stats = 0; + newdrop->item.count = 1; + + //int chamod = 0; + int chamod = (int)floor(thisclient->Attr->Cha / 30); + if(chamod < 0) chamod = 0; + int basedrop = thisclient->Stats->itemdroprate; //Base number of items to be dropped. + int maxdrop = basedrop + Config.DropMultiple + chamod; + if(newdrop->item.itemtype == 11) //gems only get 1 + newdrop->item.count = 1; + if( newdrop->item.itemtype == 10 || newdrop->item.itemtype == 12 ) + { + newdrop->item.count = RandNumber(basedrop, maxdrop); + if(thismon->thisnpc->level == 1) + newdrop->item.count = 1 + chamod; //limit the drop rate of items from level 1 event mobs + if(newdrop->item.count == 0) + newdrop->item.count = 1; + if(newdrop->item.type == 314) //skillbooks + newdrop->item.count = 1; + if(newdrop->item.itemtype == 10) + { + if(newdrop->item.itemnum >= 441 && newdrop->item.itemnum <= 888)// skillbooks + newdrop->item.count = 1; + } + + if(newdrop->item.itemtype == 12) + { + newdrop->item.count = RandNumber(0, 6) + RandNumber(0, thisclient->Stats->itemdropcountrate); //basic type 12 items + //some item types have their own rules + if(newdrop->item.type == 428) //special skill items such as mana jams and scrolls + newdrop->item.count = RandNumber(0, 3); + if(newdrop->item.itemnum > 300 && newdrop->item.itemnum < 360) //bullets get a lot higher count. + { + newdrop->item.count *= 10; + newdrop->item.count += 10; + } + } + } + else if( newdrop->item.itemtype >1 && newdrop->item.itemtype !=7 && newdrop->item.itemtype < 10) + { + // check to see if the item will be refined + int prefine = rand()%100; //Probability of finding a refined item + int refinechance = Config.RefineChance; + if(prefine < refinechance) // default = 5% + { + int refinelevel = rand()%101; //which level of refine do we actually get + if( refinelevel < 5) //default 5% + newdrop->item.refine = 4 * 16; + else if( refinelevel < 15 ) //10% + newdrop->item.refine = 3 * 16; + else if(refinelevel < 35 ) // 20% + newdrop->item.refine = 2 * 16; + else // 65% + newdrop->item.refine = 16; + } + else //99% + newdrop->item.refine = 0; + + // will the item be a blue? + bool blue = false; + int bluechance1 = RandNumber( 1, 100); + int bluechance2 = Config.BlueChance + chamod; + if( thisclient->Session->codedebug ) + Log( MSG_INFO, "Blue chance = %i", bluechance2); + //This probability is now configurable from WorldServer.conf. CHA also has an effect + if(bluechance1 < bluechance2) // some percentage of drops will be specials or blues whenever one is available. + { + if( thisclient->Session->codedebug ) + Log( MSG_INFO, "Selected a blue item"); + int p = 1; + while(alternate[n][p] != 0 && p < 8) + { + p++; + } + if(p > 1) // blues available for this item + { + //Log( MSG_INFO, "blue item available"); + p--; + int bluenum = RandNumber( 1, p); + newdrop->item.itemnum = alternate[n][bluenum]; + blue = true; + } + } + //is the item a blue or other special or unique type? + if(newdrop->item.itemtype < 10 || newdrop->item.itemtype == 14) //equipable or PAT + { + if(GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->raretype != 0) + blue = true; + } + // will the items get stats? All blue items will. Uniques count as blues. + int pstats = rand()%101; //Probability of the item having stats. default = 5% + if(blue == true) + { + pstats = 0; + } + int StatChance = Config.StatChance; + if( pstats < StatChance) // default 5% + newdrop->item.stats = RandNumber( 1, 300); + } + newdrop->item.gem = 0; + //newdrop->droptime = clock(); //moved to AddDrop function + */ + return newdrop; + +} + + +UINT CWorldServer::GetColorExp( UINT playerlevel,UINT moblevel, UINT exp ) +{ +/* +purple = +16 - 200% +red = +10 - 150% +orange = +4 - 125% +yellow = same - 100% +green = -4 - 80% +blue = -9 - 50% +white = -15 - 20% +*/ + int dif = moblevel - playerlevel; + //Log(MSG_INFO,"Level dif = %i XP factor is %i",dif,factor); + if(dif > 15) //purple + exp = (UINT)floor(exp * 2.0); + else if(dif > 10) //red + exp = (UINT)floor(exp * 1.5); + else if(dif > 4) //orange + exp = (UINT)floor(exp * 1.25); + else if(dif >= 0) //yellow + exp = (UINT)floor(exp * 1.0); + else if(dif > -4) //green + exp = (UINT)floor(exp * 0.8); + else if(dif > -9) //blue + exp = (UINT)floor(exp * 0.5); + if(dif <= -9) //white + exp = (UINT)floor(exp * 0.2); + + if(playerlevel < 10) // lower the rate for the first 10 levels + exp = (UINT)floor(exp * 0.5); + return exp; +} + +bool CWorldServer::SendSysMsg( CPlayer* thisclient, string message ) +{ + BEGINPACKET( pak, 0x7df ); + ADDBYTE ( pak, 0xf1 ); + ADDSTRING ( pak, message.c_str( )); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; +} + + +// teleport to specificated map and position +bool CWorldServer::TeleportTo ( CPlayer* thisclient, int map, fPoint position ) +{ + if(map > 70) + return true; + MapList.Index[map]->TeleportPlayer( thisclient, position, false ); + return true; +} + + +// Learn Skill +bool CWorldServer::LearnSkill( CPlayer* thisclient, UINT skill, bool cost ) +{ +/* +0 - ya aprendido +1 - aprendio +2 - no job +3 - falta otro skill +4 - no status points (lvl) +5 - no se puede subir de lvl el skill +6 - numero incorrecto de skill +7 - no tienes suficientes sp +*/ + BEGINPACKET(pak1, 0 ); + BEGINPACKET(pak2, 0 ); + BEGINPACKET(pak3, 0 ); + //int b=1; + CSkills* thisskill = SkillList.at( skill); + char skilltype = thisskill->type; + //int skillpoints = thisskill->sp; + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Attempting to learn a new skill. id = %i skill type = %i Cost to learn = %i", thisskill->id,thisskill->type,thisskill->sp ); + SendPM(thisclient, buffer); + } + int newslot = thisclient->GetNewSkillSlot(skilltype); + //CSkills* thisskill = 0; + if (newslot != 0xffff) + { + switch(skilltype) + { + case 0: + thisclient->bskills[newslot]= skill; + if (cost) + thisclient->CharInfo->SkillPoints -= thisskill->sp; + RESETPACKET(pak1, 0x7b0); + ADDBYTE(pak1, 1); + ADDBYTE(pak1, newslot); + ADDWORD(pak1, skill); + ADDWORD(pak1, thisclient->CharInfo->SkillPoints); + thisclient->client->SendPacket(&pak1); + thisclient->SetStats(); + return true; + case 1: + thisclient->askill[newslot]= skill; + if (cost) + thisclient->CharInfo->SkillPoints-=thisskill->sp; + RESETPACKET(pak2, 0x7b0); + ADDBYTE(pak2, 1); + ADDBYTE(pak2, 30+newslot); + ADDWORD(pak2, skill); + ADDWORD(pak2, thisclient->CharInfo->SkillPoints); + thisclient->client->SendPacket(&pak2); + thisclient->SetStats(); + return true; + case 2: + thisclient->pskill[newslot]= skill; + if (cost) + thisclient->CharInfo->SkillPoints-=thisskill->sp; + RESETPACKET(pak3, 0x7b0); + ADDBYTE(pak3, 1); + ADDBYTE(pak3, 60+newslot); + ADDWORD(pak3, skill); + ADDWORD(pak3, thisclient->CharInfo->SkillPoints); + thisclient->client->SendPacket(&pak3); + thisclient->SetStats(); + return true; + default: + return false; + } + } + return false; +} + +//add / remove a Fairy +void CWorldServer::DoFairyStuff(CPlayer* targetclient, int action) +{ + BEGINPACKET( pak, 0x7dc ); + ADDBYTE( pak, action ); + ADDWORD( pak, targetclient->clientid); + SendToVisible( &pak, targetclient ); +} + +void CWorldServer::DoFairyFree( int fairy ) +{ + GServer->FairyList.at(fairy)->assigned = false; + GServer->FairyList.at(fairy)->ListIndex = 0; + + if(GServer->Config.FairyTestMode == 0) + GServer->FairyList.at(fairy)->WaitTime = GServer->Config.FairyWait * (rand()% GServer->GetFairyRange(1)+ GServer->GetFairyRange(0)); + + GServer->FairyList.at(fairy)->LastTime = clock(); +} + +void CWorldServer::RefreshFairy( ) +{ + //fairy: The wait timer should be possibibly bigger when there is few people online. + if (Config.FairyMode == 1 && ClientList.size() > 2) //if fairy mode on and a few people online + { + for (int i=0; iLastTime <= ( clock() - (FairyList.at(i)->WaitTime * 60 * CLOCKS_PER_SEC) ) && !FairyList.at(i)->assigned ) // if fairy hour is now + { + if(ClientList.size() > 2) //stop solo players from getting fairied and screwing up their stats during testing. + { + int value = rand()%(int)(ClientList.size()-1) + 1; //choose random value in clientlist + CPlayer* targetclient = (CPlayer*) ClientList.at(value)->player; + if(targetclient == NULL) + return; + FairyList.at(i)->ListIndex = value; + FairyList.at(i)->LastTime = clock(); + FairyList.at(i)->assigned = true; + FairyList.at(i)->scroll = false; // Scroll value set to false. this is NOT a scroll buff fairy + targetclient->Fairy = true; + targetclient->FairyListIndex = i; // FairyList index number of our actual fairy + DoFairyStuff(targetclient, 1); // spawn fairy to target + targetclient->SetStats(); + } + } + if ( (FairyList.at(i)->LastTime + (Config.FairyStay*60*CLOCKS_PER_SEC)) <= clock() && FairyList.at(i)->assigned) // id time for our fairy to go away + { + CPlayer* oldclient = (CPlayer*) ClientList.at(FairyList.at(i)->ListIndex)->player; + if(FairyList.at(i)->scroll == true) //this is a scroll fairy + { + GServer->Config.ScrollFairy --; //reduce count of scroll fairies by one + if(GServer->Config.ScrollFairy < 0)GServer->Config.ScrollFairy = 0; //Make sure we don't go negative + GServer->Config.FairyMax = (int)ceil((float)GServer->ClientList.size() / 50.0) + GServer->Config.ScrollFairy; //recalculate the maximum number of fairies + FairyList.at(i)->scroll = false; //reset scroll value + } + DoFairyFree(i); + oldclient->Fairy = false; + oldclient->FairyListIndex = 0; + DoFairyStuff(oldclient, 0); // unspawn fairy + oldclient->SetStats(); + } + } + } + // this close fairies after their time if GM de activate fairy mode when some fairies are assigned. + if (Config.FairyMode == 0 && ClientList.size() > 1){ // if serevr mode off and someone online + for (int i=0; iLastTime + (Config.FairyStay*60*CLOCKS_PER_SEC)) <= clock() && FairyList.at(i)->assigned) + { + CPlayer* oldclient = (CPlayer*) ClientList.at(FairyList.at(i)->ListIndex)->player; + DoFairyFree(i); + oldclient->Fairy = 0; + oldclient->FairyListIndex = 0; + DoFairyStuff(oldclient, 0); + oldclient->SetStats(); + } + } + } +} diff --git a/Src/World Server/Signal.cpp b/Src/World Server/Signal.cpp new file mode 100644 index 0000000..bfc133a --- /dev/null +++ b/Src/World Server/Signal.cpp @@ -0,0 +1,113 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" +/* +/ from signal.h + * NOTE: SIGINT is produced when the user presses Ctrl-C. + * SIGILL has not been tested. + * SIGFPE doesn't seem to work? + * SIGSEGV does not catch writing to a NULL pointer (that shuts down + * your app; can you say "segmentation violation core dump"?). + * SIGTERM comes from what kind of termination request exactly? + * SIGBREAK is indeed produced by pressing Ctrl-Break. + * SIGABRT is produced by calling abort. +*/ + +void StartSignal( ) +{ + signal( SIGINT, HandleSignal ); + signal( SIGILL, HandleSignal ); + signal( SIGFPE, HandleSignal ); + signal( SIGSEGV, HandleSignal ); + signal( SIGTERM, HandleSignal ); + signal( SIGBREAK, HandleSignal ); + signal( SIGABRT, HandleSignal ); +} + +void StopSignal( ) +{ + signal( SIGINT, 0 ); + signal( SIGILL, 0 ); + signal( SIGFPE, 0 ); + signal( SIGSEGV, 0 ); + signal( SIGTERM, 0 ); + signal( SIGBREAK, 0 ); + signal( SIGABRT, 0 ); +} + +void HandleSignal( int num ) +{ + switch(num) + { + case SIGINT:/* Interactive attention */ + Log( MSG_ERROR, "Signal received: SIGINT" ); + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + delete GServer; + raise(num); + break; + case SIGILL:/* Illegal instruction */ + Log( MSG_ERROR, "Signal received: SIGILL, Server will be closed, Trying to save..." ); + GServer->DisconnectAll(); + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + delete GServer; + raise(num); + break; + case SIGFPE:/* Floating point error */ + Log( MSG_ERROR, "Signal received: SIGFPE, Server will be closed, Trying to save..." ); + GServer->DisconnectAll(); + delete GServer; + raise(num); + break; + case SIGSEGV:/* Segmentation violation */ + Log( MSG_ERROR, "Signal received: SIGSEGV, Server will be closed, Trying to save..." ); + GServer->DisconnectAll(); + #ifdef _WIN32 + Sleep(1000); + #else + usleep(1000); + #endif + delete GServer; + raise(num); + break; + + case SIGTERM:/* Termination request */ + Log( MSG_INFO, "Signal received: SIGTERM, Server will be closed" ); + pthread_create( &GServer->WorldThread[SHUTDOWN_THREAD], NULL, (void *(__cdecl *)(void *))ShutdownServer, (PVOID)0); + break; + #ifdef _WIN32 + case SIGBREAK:/* Control-break */ + Log( MSG_INFO, "Signal received: SIGBREAK, Server will be closed" ); + pthread_create( &GServer->WorldThread[SHUTDOWN_THREAD], NULL, (void *(__cdecl *)(void *))ShutdownServer, (PVOID)0); + break; + #endif + default: + cout << "Unknown Signal Number: " << num << endl; + raise(num); + } +} diff --git a/Src/World Server/SpawnMonsterCode.txt b/Src/World Server/SpawnMonsterCode.txt new file mode 100644 index 0000000..c3fbea6 --- /dev/null +++ b/Src/World Server/SpawnMonsterCode.txt @@ -0,0 +1,53 @@ +// Spawn a monster +void CMonster::SpawnMonster( CPlayer* player, CMonster* thismon ) +{ + BEGINPACKET( pak, 0x792 ); + ADDWORD ( pak, clientid ); + ADDFLOAT ( pak, Position->current.x*100 ); + ADDFLOAT ( pak, Position->current.y*100 ); + ADDFLOAT ( pak, Position->destiny.x*100 ); + ADDFLOAT ( pak, Position->destiny.y*100 ); + + if(IsDead( )) + { + ADDWORD ( pak, 0x0003 ); + ADDWORD ( pak, 0x0000 ); + } + else if(IsOnBattle( )) + { + ADDWORD ( pak, 0x0002 ); + ADDWORD ( pak, Battle->target ); + } + else if(IsMoving( )) + { + ADDWORD ( pak, 0x0001 ); + ADDWORD ( pak, 0x0000 ); + } + else + { + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + } + + ADDBYTE ( pak, thisnpc->stance ); + ADDWORD ( pak, Stats->HP ); + + ADDDWORD ( pak, Stats->HP ); + if(thismon->owner != player->clientid) + { + CMap* map = GServer->MapList.Index[Position->Map]; + if(IsSummon( ) && map->allowpvp!=0) {ADDWORD( pak, 0x0064 );} //Hostil + else if (IsSummon( ) && map->allowpvp==0) {ADDWORD ( pak, 0x0000 );}//Friendly + else {ADDWORD( pak, 0x0064 );} //Hostil + } + else {ADDWORD( pak, 0x0000 );}//Friendly + ADDDWORD( pak, GServer->BuildBuffs( this ) ); + ADDWORD ( pak, montype ); + ADDWORD ( pak, 0x0000 ); + if(IsSummon( )) + { + ADDWORD( pak, owner ); + ADDWORD( pak, 0x0000 ); // + } + player->client->SendPacket( &pak ); +} \ No newline at end of file diff --git a/Src/World Server/WorldMonster.cpp b/Src/World Server/WorldMonster.cpp new file mode 100644 index 0000000..84739a1 --- /dev/null +++ b/Src/World Server/WorldMonster.cpp @@ -0,0 +1,70 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" + +// Monster Contructor +CMonster::CMonster( fPoint MonsterPosition, UINT MonsterType, UINT MonsterMap, UINT Owner, UINT spawnid ) +{ + montype = MonsterType; + SpawnTime = clock( ); + lastSighCheck = clock( ); + MonsterDrop = NULL; + Position->source = MonsterPosition; + Position->current = MonsterPosition; + Position->destiny = MonsterPosition; + Position->Map = MonsterMap; + Position->respawn = spawnid; + Position->lastMoveTime = clock(); + // Magic status (buffs) + for(unsigned int i=0;i<30;i++) + { + MagicStatus[i].Status = 0; + MagicStatus[i].Buff = 0; + MagicStatus[i].Value = 0; + MagicStatus[i].Duration = 0; + MagicStatus[i].BuffTime = 0; + } + + // Monster drop + MonsterDrop = new MDrop; + assert(MonsterDrop); + MonsterDrop->drop = false; + MonsterDrop->firsthit = 0; + MonsterDrop->firstlevel = 0; + MonsterDrop->mobdrop = NULL; + MonsterDrop->mapdrop = NULL; + clientid = GServer->GetNewClientID(); + owner = Owner; + thisparty = NULL; + if(owner == 0) CharType = TMONSTER; + else CharType = TSUMMON; + +} + +// Monster destructor +CMonster::~CMonster( ) +{ + if(MonsterDrop!=NULL); + delete MonsterDrop; + for(UINT i=0;i PlayersDamage; + + // Monster Functions + void DoAi(int ainumber,char type); + //bool Guardiantree(CMonster* monster, CMap* map); + bool Scarab(CMonster* monster, CMap* map); + bool SummonUpdate(CMonster* monster, CMap* map, UINT j); + bool SetStats( ); + bool UnspawnMonster( ); + bool IsGhost( ); + bool IsGhostSeed( ); + bool CanMove( ); + void OnEnemyDie( void* enemy ); + bool ReadyToMove( ); + void MoveTo( fPoint nPos, bool randcircle=true ); + void Move( ); + CPlayer* GetOwner( ); + void SpawnMonster( CPlayer* player, CMonster* thismon ); + bool PlayerInRange( ); + CPlayer* GetNearPlayer( UINT mdist = 20 ); + void AddDamage( CCharacter* enemy, long int hitpower); + CDrop* GetDrop( ); + + // + bool IsMonster( ); + bool UpdateValues( ); + + // Stats + unsigned int GetAttackPower( ); + unsigned int GetDefense( ); + unsigned int GetXPRate( ); + unsigned int GetItemDropRate( ); + unsigned int GetItemDropCountRate( ); + unsigned int GetZulyDropRate( ); + unsigned int GetGreyDrops( ); + unsigned int GetDodge( ); + unsigned int GetAccury( ); + unsigned int GetMagicDefense( ); + unsigned int GetMagicAttack( ); + unsigned int GetCritical( ); + unsigned int GetAttackSpeed( ); + unsigned int GetMoveSpeed( ); + unsigned int GetMaxHP( ); + float GetAttackDistance( ); + + // Events + bool OnBeAttacked( CCharacter* Enemy ); + bool OnEnemyOnSight( CCharacter* Enemy ); + bool OnDie( ); + bool OnEnemyDie( CCharacter* Enemy ); + bool OnSpawn( bool ); + bool OnAlmostDie( ); + bool OnFar( ); +}; +#endif diff --git a/Src/World Server/WorldServer.h b/Src/World Server/WorldServer.h new file mode 100644 index 0000000..d9fb8f2 --- /dev/null +++ b/Src/World Server/WorldServer.h @@ -0,0 +1,458 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + + some change from the original eich source + + parts copied from/based on work from ExJem +*/ +//#define debugging +#ifndef __ROSE_SERVERS__ +#define __ROSE_SERVERS__ +#include "datatypes.h" +#include "sockets.h" +#include "worldmonster.h" +#include "quest/CRoseArray.hpp" +#include "quest/CRoseFile.hpp" +#include "quest/CStrStb.hpp" +#include "quest/CIntStb.hpp" +#include "quest/QuestTrigger.h" +#include "aip/Aip.h" +#include "worldmap.h" +#include "player.h" +#include "character.h" +#include "party.h" +#include "stbhandler.h" +#include "stlhandler.h" +#include "strhashing.h" + +#define MAXVISUALRANGE 90 +#define MINVISUALRANGE 80 +#define ClearItem(i) { i.appraised=false; i.count=0; i.durability=0; i.itemnum=0; i.itemtype=0; i.lifespan=0; i.refine=0; i.socketed=false; i.stats=0; i.gem=0; } + +#define WORLD_THREAD 0 +#define VISUALITY_THREAD 1 +#define SHUTDOWN_THREAD 2 + +#define MAX_MAPS 115 + +// Main loginserver server class +class CWorldServer : public CServerSocket +{ + public: + //------------------ MAIN (worldserver.cpp) + CWorldServer ( string ); + CClientSocket* CreateClientSocket( ); + ~CWorldServer( ); + bool OnReceivePacket( CClientSocket* thisclient, CPacket* P ); + void ReceivedISCPacket( CPacket* pak ); // Raven0123 + void SendISCServerInfo( ); + void OnClientDisconnect( CClientSocket* thisclient ); + void DeleteClientSocket( CClientSocket* thisclient ); + bool OnServerReady( ); + void SpawnMonsters( ); + bool Ping( ); + void LoadConfigurations( const char* ); + void LoadCommandLevels( void ); + void ServerLoop( ); + void LoadEncryption( ); + bool pakGiveItem( CPlayer* thisclient, unsigned char itemtype, unsigned short itemid, unsigned short count ); + bool pakGiveExp( CPlayer* thisclient, unsigned exp ); + bool pakGiveMoney( CPlayer* thisclient, unsigned amount ); + bool pakTeleport( CPlayer* thisclient, unsigned short map, float x, float y ); + bool pakLearnSkill( CPlayer* thisclient, unsigned short skillid ); + void DePopulateZone( CPlayer* thisclient, int zonex, int zoney ); + bool pakQuestTrigger( CPlayer* thisclient, CPacket* pak ); + + //------------------ WORLD PROCESS (worldprocess.cpp) + bool GiveExp( CMonster* thismon ); + + //------------------ BUFFS (buff.cpp) + CBValue GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff=true, bool Status=false ); + //CBValue GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff=true ); + bool CheckABuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i); + //bool CheckDBuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i); + bool AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue , bool flag); + //bool AddDeBuffs( CSkills* thisskill, CCharacter* character, int Evalue ); + bool AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue ); + bool InstantBuff( CSkills* thisskill, CCharacter* character, int Evalue, int i ); + + + //------------------ SERVER EXTRAS (extrafunctions.cpp) + bool IsValidItem(UINT type, UINT id ); + unsigned BuildItemShow(CItem thisitem); + UINT GetUIntValue( const char* s , void* var=NULL ); + int GetIntValue( const char* s , void* var=NULL ); + char* GetStrValue( const char* s , void* var=NULL ); + bool SendSysMsg( CPlayer* thisclient, string message ); + UINT RandNumber( UINT init, UINT range, UINT seed=0 ); + UINT GetColorExp( UINT playerlevel,UINT moblevel, UINT exp ); + bool CheckInventorySlot( CPlayer* thisclient, int slot ); + bool pakGMClass( CPlayer* thisclient, char* classid ); + bool pakGMKillInRange( CPlayer* thisclient, int range ); + bool pakGMHide( CPlayer* thisclient, int mode ); + void SendToVisible( CPacket* pak, CPlayer* thisclient, bool thisclients=true ); + void SendToVisible( CPacket* pak, CPlayer* thisclient, CPlayer* xotherclient ); + + void SendToVisible( CPacket* pak, class CCharacter* character, CDrop* thisdrop=NULL ); + + bool IsMonInCircle( CPlayer* thisclient, CMonster* thismon, float radius ); + bool IsPlayerInCircle( CPlayer* thisclient, CPlayer* otherclient, float radius ); + CUseInfo* GetUseItemInfo( CPlayer* thisclient, UINT slot ); + bool IsVisible( CPlayer* thisclient, CPlayer* otherclient ); + bool IsMonInCircle( fPoint center, fPoint position, float radius ); + bool IsVisible( CPlayer* thisclient, CMonster* thismon ); + bool IsVisible( CPlayer* thisclient, CDrop* thisdrop ); + CItem GetItemByHeadAndData( unsigned head, unsigned data ); + bool IsVisible( CPlayer* thisclient, CNPC* thisnpc ); + void SendToVisible( CPacket* pak, CMonster* thismon, CDrop* thisdrop=NULL ); + void SendToVisible( CPacket* pak, CDrop* thisdrop ); + CPlayer* GetClientByUserName( char *username ); + UINT BuildBuffs( CCharacter* player ); + UINT BuildUpBuffs( CCharacter* player ); + UINT BuildDeBuffs( CCharacter* player ); + void CheckForLevelUp ( CPlayer* thisclient ); + bool isSkillTargetFriendly( CSkills* thisskill ); + CRespawnPoint* GetRespawnByID( UINT id ); + CPlayer* GetClientByCID( UINT id, UINT map=0 ); + CPlayer* GetClientByCharName( char* name ); + CPlayer* GetClientByID( UINT id, UINT map=0 ); + CTeleGate* GetTeleGateByID( UINT id ); + UINT BuildItemHead( CItem thisitem ); + UINT BuildItemData( CItem thisitem ); + CMonster* GetMonsterByID( UINT id, UINT map ); + CSpawnArea* GetSpawnArea( UINT id, UINT map=0 ); +#ifdef USEIFO + CMobGroup* GetMobGroup( UINT id, UINT map=0 ); +#endif + bool DeleteSpawn( CSpawnArea* spawn ); + CNPCData* GetNPCDataByID( UINT id ); + void SendToMap( CPacket* pak, int mapid ); + unsigned BuildItemRefine(CItem thisitem ); + CMDrops* GetDropData( UINT id ); + CRespawnPoint* GetRespawnByMap( int id ); + CSkills* GetSkillByID( UINT id ); + CStatus* GetStatusByID( UINT id ); + void ClearClientID( UINT id ); + CDrop* GetDropByID( UINT id, UINT map=0 ); + CNPC* GetNPCByID( UINT id, UINT map=0 ); + CDrop* GetNewDrop( CMonster* thismon ); + CDrop* GetPYDrop( CMonster* thismon, UINT droptype ); + void SendToAll( CPacket* pak ); + UINT GetNewClientID( ); + void DisconnectAll(); + CPlayer* GetClientByUserID( UINT userid ); + UINT GetMatIDfromRecipe (UINT CraftLevel, UINT matType); + UINT GetFairyRange( UINT part ); + void RefreshFairy( ); + inline dword GetStorageFee(dword iBasePrice, dword iPriceRate, dword uiDupCnt) {return ((((iBasePrice*5) / 0x3E8)+1)*(iPriceRate+1)) * uiDupCnt;}; + + + //------------------ Fairies --------------------- + void DoFairyStuff( CPlayer* targetclient, int action ); + void DoFairyFree( int fairy ); + + //------------------ MATH (extrafunctions.cpp) + fPoint RandInCircle(fPoint center, float radius); + fPoint RandInPoly(fPoint p[], int pcount); + fPoint RandInTriangle(fPoint p1, fPoint p2, fPoint p3); + float AreaOfTriangle(fPoint p1, fPoint p2, fPoint p3); + float distance( fPoint pos1, fPoint pos2 ); + + //------------------ PACKETS (worldpackets.cpp) + bool pakItemMall( CPlayer* thisclient, CPacket* P ); + bool pakWeight( CPlayer* thisclient, CPacket* P ); + bool pakPrintscreen( CPlayer* thisclient, CPacket* P ); + bool pakModifiedItemDone( CPlayer* thisclient, CPacket* P ); + bool pakSpawnDrop( CPlayer* thisclient, CDrop* thisdrop ); + bool pakChangeStorage( CPlayer* thisclient, CPacket* P ); + bool pakModifiedItem( CPlayer* thisclient, CPacket* P ); + bool pakPartyManager( CPlayer* thisclient, CPacket* P ); + bool pakLevelUpSkill( CPlayer *thisclient, CPacket* P ); + bool pakChangeStance( CPlayer* thisclient, CPacket* P ); + bool pakCharDSClient( CPlayer* thisclient, CPacket* P ); + bool pakChangeEquip( CPlayer* thisclient, CPacket* P ); + bool pakStartAttack( CPlayer* thisclient, CPacket* P ); + bool pakSpawnNPC( CPlayer* thisclient, CNPC* thisnpc ); + bool pakRideRequest( CPlayer* thisclient, CPacket* P ); + bool pakPartyActions( CPlayer* thisclient, CPacket* P ); + bool pakPartyOption( CPlayer* thisclient, CPacket* P ); + bool pakTradeAction( CPlayer* thisclient, CPacket* P ); + bool pakChangeCart( CPlayer* thisclient, CPacket* P ); + bool pakSkillSelf ( CPlayer* thisclient, CPacket* P ); + bool pakDoIdentify( CPlayer *thisclient, CPacket *P ); + bool pakNormalChat( CPlayer* thisclient, CPacket* P ); + bool pakCharSelect( CPlayer* thisclient, CPacket* P ); + bool pakStartSkill( CPlayer* thisclient, CPacket* P ); + bool pakRepairHammer( CPlayer* thisclient, CPacket* P ); + bool pakRepairItem( CPlayer* thisclient, CPacket* P ); + bool pakMoveSkill( CPlayer* thisclient, CPacket* P ); + bool pakCloseShop( CPlayer* thisclient, CPacket* P ); + bool pakStoreZuly( CPlayer* thisclient, CPacket* P ); + bool pakPartyChat( CPlayer* thisclient, CPacket* P ); + bool pakGameGuard( CPlayer* thisclient, CPacket* P ); + bool pakOpenShop( CPlayer* thisclient, CPacket* P ); + bool pakShowShop( CPlayer* thisclient, CPacket* P ); + bool pakSellShop( CPlayer* thisclient, CPacket* P ); + bool pakMoveChar( CPlayer* thisclient, CPacket* P ); + bool pakStopChar( CPlayer* thisclient, CPacket* P ); + bool pakPickDrop( CPlayer* thisclient, CPacket* P ); + bool pakAddStats( CPlayer* thisclient, CPacket* P ); + bool pakShowHeal( CPlayer* thisclient, CPacket* P ); + bool pakSkillAOE( CPlayer* thisclient, CPacket* P ); + bool pakidentify( CPlayer* thisclient, CPacket* P ); + bool pakUserDied( CPlayer* thisclient, CPacket* P ); + bool pakSaveTown( CPlayer* thisclient, CPacket* P ); + bool pakTradeAdd( CPlayer* thisclient, CPacket* P ); + bool pakUseItem ( CPlayer* thisclient, CPacket* P ); + bool pakWhisper( CPlayer* thisclient, CPacket* P ); + bool pakEquipABC(CPlayer* thisclient, CPacket *P ); + bool pakStorage( CPlayer* thisclient, CPacket* P ); + bool pakBuyShop( CPlayer* thisclient, CPacket* P ); + bool pakDoEmote( CPlayer* thisclient, CPacket* P ); + bool pakNPCBuy( CPlayer* thisclient, CPacket* P ); + bool pakDoDrop( CPlayer* thisclient, CPacket* P ); + bool pakShout( CPlayer* thisclient, CPacket* P ); + bool pakCraft( CPlayer* thisclient, CPacket* P ); + bool pakRespawn( CPlayer* thisclient, CPacket* P ); + bool pakDoID( CPlayer* thisclient, CPacket* P ); + bool pakGate( CPlayer* thisclient, CPacket* P ); + bool pakExit( CPlayer* thisclient, CPacket* P ); + bool pakPing( CPlayer* thisclient, CPacket* P ); + bool SendLevelUPtoChar(CPlayer* thisclient ); + void pakPlayer( CPlayer *thisclient ); + void pakInventory( CPlayer *thisclient ); + void pakQuestData( CPlayer *thisclient ); + void pakClearUser( CPlayer* thisclient ); + bool LearnSkill( CPlayer* thisclient, UINT skill, bool cost = true ); + + //-------------------------- Clan functions + bool pakClanManager ( CPlayer* thisclient, CPacket* P ); + bool pakCreateClan ( CPlayer* thisclient, CPacket* P ); + + //------------------ GM COMMANDS (gmcmds.cpp) + bool pakGMMoveTo( CPlayer* thisclient, fPoint position ); + bool pakGMTeleAllHere( CPlayer* thisclient ); + bool GMShowTargetInfo( CPlayer* thisclient ); + bool pakGMServerInfo( CPlayer* thisclient ); + bool pakGMHeal( CPlayer* thisclient, int hpamount, int mpamount ); + bool pakGMStat( CPlayer* thisclient, const char* statname, int statvalue ); + bool pakGMItemtoplayer(CPlayer* thisclient, char* name , UINT itemid , UINT itemtype , UINT itemamount , UINT itemrefine , UINT itemls, UINT itemstats , UINT itemsocket) ; + bool pakGMItem( CPlayer* thisclient, UINT itemid , UINT itemtype , UINT itemamount , UINT itemrefine , UINT itemls, UINT itemstats , UINT itemsocket ); + bool pakGMTeleOtherPlayer( CPlayer *thisclient, char* name, int map, float x, float y ); + bool pakGMZuly( CPlayer* thisclient, int mode, int amount, char* charname ); + bool pakGMTele( CPlayer* thisclient, int map, float x, float y ); + bool pakGMMon( CPlayer* thisclient, int montype, int moncount, int monAI ); + bool pakGMZulygive(CPlayer* thisclient, char* name, int zuly); + bool pakGMFairyto(CPlayer* thisclient, char* name, int mode); + bool pakGMManageFairy(CPlayer* thisclient, int mode); + bool pakGMChangeFairyWait(CPlayer* thisclient, int newvalue); + bool pakGMChangeFairyStay(CPlayer* thisclient, int newvalue); + bool pakGMChangeFairyTestMode(CPlayer* thisclient, int mode); + bool pakGMTelePlayerHere( CPlayer* thisclient, char* name ); + bool pakGMLevel( CPlayer* thisclient, int level , char* name); + bool pakGMTeleToPlayer( CPlayer* thisclient, char* name ); + bool pakGMDoEmote( CPlayer* thisclient, int emotionid ); + bool pakGMCommand( CPlayer* thisclient, CPacket* P ); + bool ReloadMobSpawn( CPlayer* thisclient, int id ); + bool pakGMKick( CPlayer* thisclient, char* name ); + bool pakGMAnn( CPlayer* thisclient, CPacket *P ); + bool pakGMBan( CPlayer* thisclient, char* name ); + bool pakGMInfo(CPlayer* thisclient, char* name); + bool pakGMNpc(CPlayer* thisclient, int npcid, int dialogid); + bool pakGMGotomap( CPlayer* thisclient, int map ); + bool pakGMEffect( CPlayer* thisclient, int effect); + bool pakGMMute( CPlayer* thisclient, char* name, int time); + bool pakGMPartylvl( CPlayer* partyclient, int level ); + bool pakGMChangePlayerDmg(CPlayer* thisclient, int rate); + bool pakGMChangeMonsterDmg(CPlayer* thisclient, int rate); + bool pakGMChangeMSpeedModif(CPlayer* thisclient, int modif); + bool pakGMChangeCfmode(CPlayer* thisclient, int mode); + bool pakGMUnionPoints(CPlayer* thisclient, char* name, int nb_points); + + //------------------ CHAR SERVER COMMANDS (worldpackets.cpp) + bool TeleportTo ( CPlayer* thisclient, int map, fPoint position ); + bool pakCSCharSelect( CPlayer* thisclient, CPacket* P ); + bool pakCSReady( CPlayer* thisclient, CPacket* P ); + + //----------------- Server StartUp Functions (startup.cpp) + bool LoadSTBData( ); + bool LoadZoneData( ); + bool LoadConsItem( ); + bool LoadSellData( ); + bool LoadProductItem( ); + bool LoadPatItem( ); + bool LoadNaturalItem( ); + bool LoadJemItem( ); + bool LoadEquip( ); + bool LoadItemStats( ); + bool LoadTeleGateData( ); + bool LoadMonsterSpawn( ); + bool LoadMobGroups( ); + bool LoadRespawnData( ); + bool LoadWayPoints( ); + bool LoadBreak( ); + bool LoadGrades(); //LMA: loading refines (grades) bonuses. + + bool LoadNewDrops( ); + bool LoadPYDropsData( ); + bool LoadSkillBookDropsData( ); + bool LoadConfig( ); + bool LoadLTB( ); + + bool LoadStatusData( ); + bool LoadSkillData( ); + bool LoadMonsters( ); + bool LoadNPCData( ); + bool LoadUpgrade( ); + bool LoadNPCs( ); + bool LoadSTB( ); + bool LoadAIP( ); + bool CleanConnectedList( ); + + void LoadZones(); + + // QSD Fuctions + void ReadQSD(strings path, dword index); + void LoadQuestData(); + inline int round(double x) {return int(x > 0.0 ? x + 0.5 : x - 0.5);}; + // AIP Functions + void ReadAIP(strings path, dword index); + void LoadAipData(); + + // Server Functions + bool SendPM( CPlayer* thisclient, const char* Format, ... ); + bool SendGlobalMSG( CPlayer* thisclient, const char* Format, ... ); + //bool CWorldServer::NPCShout( CMonster* thismon, char msg[200], char npc[50] ); + bool NPCShout( int thismap, const char msg[200], const char npc[50] ); + bool NPCMessage( CMonster* thismon, const char msg[200], const char npc[50] ); + bool NPCWhisper( CPlayer* thisclient, CMonster* thismon, const char msg[200], const char npc[50] ); + bool NPCAnnounce( const char msg[200], const char npc[50] ); + UINT GetMaxPartyExp( UINT partylevel ); + bool DoSkillScript( CCharacter* character, CSkills* thisskill ); + + bool AddParty( CParty* ); + bool RemoveParty( CParty* ); + + UINT ClientIDList[0x10000]; // Clients List + UINT ClientIDIndex; // Index pointer for Client ID List + SOCKET csock; // Socket for accessing the char server + char* cct; // Encryption table for char server + + vector TeleGateList; // Telegates List + vector StatusList; // Status List + vector SkillList; // Skills List + vector MDropList; // Drops List + + map NpcNameList; //LMA: npc name map. + map STLNameList; //LMA: Name for all things taken from STL. + map STLCommentList; //LMA: Comment for all things taken from STL. + //IfoObject WarpGate; //LMA: Warp Gate for Union. + + CGrade GradeList[10]; + int maxGrades; //Nb grades (refines) + + vector SkillbookList; // Skillbook drop list + + vector NPCData; // NPC/Mobs List + //vector AIPData; // AIP filename list + vector PartyList; // Party List + vector FairyList; // Fairy List + std::map< unsigned, CNPCTrigger* > NPCActionList; + + struct UpgradeList + { + UINT BindRune_Level; + UINT Talisman_Level; + UINT BindRune_Success; + UINT Talisman_Success; + }; + + + CItemStas StatsList[500]; + UpgradeList upgrade[10]; + CEquipList EquipList[10]; + CJemList JemList; + CNaturalList NaturalList; + CPatList PatList; + CProductList ProductList; + CBreakList BreakList; + CSellList SellList; + CUseList UseList; + CMapList MapList; + + CSTBData STB_NPC; // NPC data + CSTBData STB_SKILL; // Skill data + CSTBData STB_STATUS; // Status Data + CSTBData STB_QUEST; // Quest data + CSTBData STB_ITEM[14]; // Item data + CSTBData STB_DROP; // Drop list + CSTBData STB_ZONE; // Zone data + CSTBData STB_PRODUCT; // Crafting data + CSTBData STB_BREAK; // Break data + CSTBData STB_SELL; // npc sell index + CSTBData ListGrade; // LMA: used to store the refine bonuses (% and +). + CSTBData ListUpGrade; // Bindrune and Tlisman upgrade data + + vector TriggerList; + fpQuestCond qstCondFunc[31]; + fpQuestRewd qstRewdFunc[51]; + vector AipList; + //vector AIPList; + UINT maxSpawnId; + fpAiCond aiCondFunc[35]; + fpAiAct aiActFunc[40]; + int ObjVar[2000][20]; //NPC variables used in AI + UINT ThisSession; //session ID from database + UINT ServerStartTime; //time the server was started + UINT ServerRunTime; //Hours the server has been running + CLTBstring Ltbstring[200]; + TowerDef WPList[100][100]; //Way point list for tower def. [mapid][wp id] + //int AIindex[500][6][20][1]; + + clock_t lastServerStep; // Last Update + clock_t LastUpdateTime; // Store the last update World Time + bool ServerOnline; + string filename; + + + pthread_t WorldThread[3]; + pthread_t MapThread[200]; + pthread_attr_t at; + pthread_mutex_t MapMutex,SQLMutex,PlayerMutex; + + CDatabase* DB; + + //Test thingies + unsigned ATTK_SPEED_MOD; + unsigned HIT_DELAY_MOD; + unsigned MOVE_SPEED_MOD; + +}; +extern class CWorldServer* GServer; + +PVOID VisibilityProcess( PVOID TS ); +PVOID MapProcess( PVOID TS ); +PVOID WorldProcess( PVOID TS ); +void ShutdownServer( PVOID TS ); + +void StartSignal( ); +void StopSignal( ); +void HandleSignal( int num ); +// ----------------------------------------------------------------------------------------- + +#endif diff --git a/Src/World Server/Worldprocess.cpp b/Src/World Server/Worldprocess.cpp new file mode 100644 index 0000000..499a39c --- /dev/null +++ b/Src/World Server/Worldprocess.cpp @@ -0,0 +1,165 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Give Exp the KTRose way +bool CWorldServer::GiveExp( CMonster* thismon ) +{ + //Log(MSG_DEBUG,"Awarding EXP"); + int tmpMult = 1; + if( thismon->owner != 0) // Summon + { + MapList.Index[thismon->Position->Map]->DeleteMonster( thismon ); + return true; + } + // Give Experience Drops and Quest Items + vector PartyExp; + vector PartyList; + for(UINT i=0;iPlayersDamage.size();i++) // this is the list of players who did damage to the monster who just died + { + MonsterDamage* thisplayer = thismon->PlayersDamage.at(i); + CPlayer* thisclient = GetClientByCID( thisplayer->charid, thismon->Position->Map ); + if( thisplayer->damage > 0 && thisclient != NULL ) //player did some damage. just making sure + { + //Log(MSG_DEBUG,"Player did %i damage max = %i",thisplayer->damage,thismon->Stats->MaxHP); + float MyPercent = (float)thisplayer->damage / thismon->Stats->MaxHP; //Figure out how much of the actual kill I am responsible for + if (MyPercent > 1.5) + MyPercent = 1.5; //set overkill ceiling + if( thisclient->Battle->target == thismon->clientid ) + { + ClearBattle( thisclient->Battle ) + thisclient->Position->destiny = thisclient->Position->current; + //Log(MSG_DEBUG,"(GiveExp) Destiny set to current position X: %f Y: %f.",thisclient->Position->current.x,thisclient->Position->current.y); + } + if( thismon->MonsterDrop->firsthit == thisclient->CharInfo->charid ) + { + for( int q=0;q<10;q++) + { + // Give Quest Item + if( thisclient->quest.quests[q].QuestID != 0 ) + { + Log(MSG_DEBUG,"Giving quest reward item for quest %i",thisclient->quest.quests[q].QuestID); + BEGINPACKET( pak, 0x731 ) + ADDWORD ( pak, thismon->montype ); + thisclient->client->SendPacket( &pak ); + break; + } + } + } + //assign my own exp for monsters that I personally damaged + unsigned int exp = (unsigned int)floor(thismon->thisnpc->exp * MyPercent); + //unsigned int exp = (unsigned int)ceil((double)((thismon->thisnpc->exp * thisplayer->damage) / (thismon->thisnpc->hp*thismon->thisnpc->level))); + exp = exp * Config.EXP_RATE * tmpMult; //calculate base exp for this client. No medals or stuff accounted for yet + //Log(MSG_DEBUG,"MaxXP: %i config rate: %i percent: %f Total XP: %i", thismon->thisnpc->exp, Config.EXP_RATE, MyPercent, exp); + thisclient->CharInfo->Pending_Exp += (exp * thisclient->Stats->xprate); //store exp into thisclient's pending_exp using personal xprate adjustments + + if( thisclient->Party->party != NULL ) //player is in a party so scan the party members + { + //Log(MSG_DEBUG,"Player is in a party"); + CParty* party = thisclient->Party->party; //assign a party + if(party == NULL) + return true; + //Log(MSG_DEBUG,"party found. Counted = %i", party->counted); + for(UINT p=0;pMembers.size();p++) //loop through all the members in the party + { + //Log(MSG_DEBUG,"member %i being parsed", p); + if(!party->counted) + { + //Log(MSG_DEBUG,"party added to list. level = %i", party->PartyLevel); + party->counted = true; //tag the party so we don't add it to the list twice + PartyList.push_back( party ); //we will need this list later to convert pending exp to real exp + } + CPlayer* thismember = party->Members.at(p); //find a party member + if(thismember == NULL) + return true; + float RawTmpExp = (exp * (party->PartyLevel + 25) / 50); + unsigned int tempxp =(unsigned int)(floor)(RawTmpExp); + if (tempxp < 1)tempxp = 1; + //Log(MSG_DEBUG,"member %i pending exp = %i tempxp: %i", p, thismember->CharInfo->Pending_Exp, tempxp); + thismember->CharInfo->Pending_Exp += tempxp; // add a percentage of thisclient's non-adjusted exp to all party members including himself + //Log(MSG_DEBUG,"member %i pending exp (after tempxp) = %i", p, thismember->CharInfo->Pending_Exp); + } + if(party->PartyLevel < 50) //only give party exp if party level is under 50 + { + party->Pending_Exp += exp; //add thisclient's non-adjusted xp to the pending exp of the party + } + } + else //not in a party so deal with all the exp now + { + //SendPM(thisclient, "You receive %i EXP",thisclient->CharInfo->Pending_Exp); + thisclient->CharInfo->Exp += thisclient->CharInfo->Pending_Exp; + thisclient->CharInfo->Pending_Exp = 0; + if(!thisclient->CheckPlayerLevelUP()) + { + BEGINPACKET( pak, 0x79b ); + ADDDWORD ( pak, thisclient->CharInfo->Exp ); + ADDWORD ( pak, thisclient->CharInfo->stamina ); + ADDWORD ( pak, 0 ); + thisclient->client->SendPacket( &pak ); + } + } + //Log(MSG_DEBUG,"Player rewarded %i experience after all mods",thisexp); + } + } + + //new way PY + //Log(MSG_DEBUG,"Parsing party list now"); + for(int p=0;pExp, thisparty->Pending_Exp ); + thisparty->counted = false; //reset the boolean for next time + for(UINT i=0;iMembers.size();i++) //loop through all the members in the party + { + CPlayer* thismember = thisparty->Members.at(i); //find a party member + if(thismember == NULL) + return true; + thismember->CharInfo->Exp += thismember->CharInfo->Pending_Exp; + //Log(MSG_DEBUG,"Added pending exp %i to regular exp %i for member %i", thismember->CharInfo->Pending_Exp, thismember->CharInfo->Exp, i); + thismember->CharInfo->Pending_Exp = 0; + if(!thismember->CheckPlayerLevelUP( )) + { + BEGINPACKET( pak, 0x79b ); + ADDDWORD ( pak, thismember->CharInfo->Exp ); + ADDWORD ( pak, thismember->CharInfo->stamina ); + ADDWORD ( pak, 0 ); + thismember->client->SendPacket( &pak ); + } + } + thisparty->Exp += thisparty->Pending_Exp; + thisparty->Pending_Exp = 0; + if( thisparty->Exp > GetMaxPartyExp(thisparty->PartyLevel)) //level up the party + { + thisparty->PartyLevel++; + thisparty->Exp -= GetMaxPartyExp(thisparty->PartyLevel-1); + } + BEGINPACKET( pak, 0x7d4 ); + ADDBYTE ( pak, thisparty->PartyLevel ); + ADDDWORD ( pak, thisparty->Exp ); + thisparty->SendToMembers( &pak ); + } + return true; +} diff --git a/Src/World Server/aip/AiActions.cpp b/Src/World Server/aip/AiActions.cpp new file mode 100644 index 0000000..f2e20a1 --- /dev/null +++ b/Src/World Server/aip/AiActions.cpp @@ -0,0 +1,1344 @@ +// Props to ExJam for this code :D +#include "../WorldServer.h" + +// extern CRandomMersenne rg; + +//Stop all actions +AIACT(000) +{ + //SetCMD_Stop (Stop all actions i guess?) + //entity->StopAll(); + CMonster* monster = reinterpret_cast(entity); + ClearBattle( monster->Battle ); + monster->Position->destiny = monster->Position->current; + return AI_SUCCESS; +} + +//Do Action +AIACT(001) +{ + //byte cAction; //Pos: 0x00 + //CObjMOB::Set_EMOTION (pak 781) + GETAIACTDATA(001); + BEGINPACKET( pak, 0x781 ); + ADDWORD ( pak, data->cAction ); + ADDWORD ( pak, 0); + ADDWORD ( pak, entity->clientid ); + GServer->SendToVisible(&pak, entity,NULL ); + return AI_SUCCESS; +} + +//Say LTB String +AIACT(002) +{ + //Client side say text + //Log(MSG_DEBUG, "Say LTB String"); + //dword iStrID; Pos: 0x00 + GETAIACTDATA(002); + + //Think this is crashing the server. Taken out for the time being. + //lets have the NPC also say the LTB string over the regular message system. This will show up green ^-^ + CMonster* thisMonster = reinterpret_cast(entity); + //if(thisMonster->CharType == TNPC) // check if it is an NPC and not a normal monster. + //{ + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, GServer->Ltbstring[data->iStrID].NPCname ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, GServer->Ltbstring[data->iStrID].LTBstring ); + ADDBYTE( pak, 0 ); + GServer->SendToVisible(&pak, thisMonster); + //} + return AI_SUCCESS; +} + +//Move (walk or run) to a random location within iDistance of my present location +AIACT(003) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //move randomly within iDistance use randincircle + GETAIACTDATA(003); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;// * 2;//Get it to our coord system! + entity->Position->destiny = GServer->RandInCircle(entity->Position->current, iDist); + //int randDis = rand()%(iDist << 1); + //float nX = (entity->Position->current.x + randDis) - iDist; + //entity->Position->destiny.x=nX; + //randDis = rand()%(iDist << 1); + //float nY = (entity->Position->current.y + randDis) - iDist; + //entity->Position->destiny.y = nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + entity->Position->lastMoveTime = clock(); + entity->Battle->atktype=0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, entity->Position->destiny.x*100 ); + ADDFLOAT ( pak, entity->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; + +} + +//Move (walk or run) to a random location within iDistance of my spawn location +AIACT(004) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //move randomly within iDistance of spawn position + GETAIACTDATA(004); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;//Get it to our coord system! + entity->Position->destiny = GServer->RandInCircle(entity->Position->source, iDist); + //int randDis = rand()%(iDist << 1); + //float nX = (monster->Position->source.x + randDis) - iDist; + //monster->Position->destiny.x=nX; + //randDis = rg.IRandom(0, iDist << 1); + //randDis = rand()%(iDist << 1); + //float nY = (monster->Position->source.y + randDis) - iDist; + //monster->Position->destiny.y=nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + monster->Position->lastMoveTime = clock(); + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + + return AI_SUCCESS; +} + +//Move (walk or run) to a random location within iDistance of my findChar location +AIACT(005) +{ + //byte cSpeed; //Pos: 0x00 + //move randomly within 200 of "m_pFindCHAR" + GETAIACTDATA(005); + if(entity->findChar == NULL) + return AI_FAILURE; + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + entity->findChar->UpdatePosition(); + + int iDist = 2;//Get it to our coord system! + entity->Position->destiny = GServer->RandInCircle(entity->findChar->Position->current, iDist); + + //int randDis = rand()%(iDist << 1); + //float nX = (entity->findChar->Position->current.x + randDis) - iDist; + //entity->Position->destiny.x=nX; + + + //randDis = rand()%(iDist << 1); + //float nY = (entity->findChar->Position->current.y + randDis) - iDist; + //entity->Position->destiny.y=nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + + return AI_SUCCESS; +} + +//Run and Attack aiobj within iDistance that has the lowest or greatest cAbType +AIACT(006) +{ + //dword iDistance; //Pos: 0x00 + //byte cAbType; //Pos: 0x04 + //byte cMoreLess; //Pos: 0x05 + //Run and Attack aiobj within iDistance that has the lowest or greatest cAbType + //cAbType + //0 = Level + //1 = Attack + //2 = Defense + //3 = "Res"? Magic Resistance? + //4 = HP + //5 = Charm + GETAIACTDATA(006); + + dword eCount = 0; + int highestAB = -9999999; + // CWorldEntity* highestEntity = NULL; + int lowestAB = 9999999; + // CWorldEntity* lowestEntity = NULL; + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + return AI_FAILURE; //monster has no agression value so cannot attack a player + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + CMap* map= GServer->MapList.Index[entity->Position->Map]; + CCharacter* Target = NULL; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + if(GServer->IsMonInCircle( monster->Position->current, player->Position->current, data->iDistance)) + { + switch(data->cAbType) + { + case 0: //level + if(data->cMoreLess == 0) + { + if(player->Stats->Level > lowestAB) + { + lowestAB = player->Stats->Level; + Target = player; + }else continue; + }else{ + if(player->Stats->Level < highestAB) + { + highestAB = player->Stats->Level; + Target = player; + }else continue; + } + break; + case 1: //Attack power + if(data->cMoreLess == 0) + { + if(player->Stats->Attack_Power > lowestAB) + { + lowestAB = player->Stats->Attack_Power; + Target = player; + }else continue; + }else{ + if(player->Stats->Attack_Power < highestAB) + { + highestAB = player->Stats->Attack_Power; + Target = player; + }else continue; + } + break; + case 2: //defense + if(data->cMoreLess == 0) + { + if(player->Stats->Defense> lowestAB) + { + lowestAB = player->Stats->Defense; + Target = player; + }else continue; + }else{ + if(player->Stats->Defense < highestAB) + { + highestAB = player->Stats->Defense; + Target = player; + }else continue; + } + break; + case 3: //Magic Def + if(data->cMoreLess == 0) + { + if(player->Stats->Magic_Defense> lowestAB) + { + lowestAB = player->Stats->Magic_Defense; + Target = player; + }else continue; + }else{ + if(player->Stats->Magic_Defense < highestAB) + { + highestAB = player->Stats->Magic_Defense; + Target = player; + }else continue; + } + break; + case 4: // HP + if(data->cMoreLess == 0) + { + if(player->Stats->HP> lowestAB) + { + lowestAB = player->Stats->HP; + Target = player; + }else continue; + }else{ + if(player->Stats->HP < highestAB) + { + highestAB = player->Stats->HP; + Target = player; + }else continue; + } + break; + case 5: // Charm + if(data->cMoreLess == 0) + { + if(player->Attr->Cha > lowestAB) + { + lowestAB = player->Attr->Cha; + Target = player; + }else continue; + }else{ + if(player->Attr->Cha < highestAB) + { + highestAB = player->Attr->Cha; + Target = player; + }else continue; + } + break; + default: + break; + } + } + } + // OK we found the new Target player. now attack him. + monster->Stats->stance = 1; + monster->SetStats(); + monster->StartAction( Target, NORMAL_ATTACK, 0 ); + + return AI_SUCCESS; +} + +//Special attack +AIACT(007) +{ + //CObjCHAR::Special_ATTACK + //Log(MSG_DEBUG, "AIACT(007)"); + return AI_SUCCESS; +} + +//Move (4) +AIACT(008) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //move towards target. Stop at a specified precentage away from target + //Log(MSG_DEBUG, "AIACT(008)"); + GETAIACTDATA(008); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + { + return AI_FAILURE; + } + target->UpdatePosition(); + int iDist = data->iDistance;//Get it to our coord system! + float xDist = target->Position->current.x - monster->Position->current.x; + float yDist = target->Position->current.y - monster->Position->current.y; + float fDistance = GServer->distance( entity->Position->current, target->Position->current); + float nX = monster->Position->current.x - (iDist * xDist / fDistance); + float nY = monster->Position->current.y - (iDist * yDist / fDistance); + monster->Position->destiny.x = nX; + monster->Position->destiny.y = nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + monster->Position->lastMoveTime = clock(); + ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + //entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} + +//Convert Monster to new monster (1) +AIACT(009) +{ + //word wMonster; //Pos: 0x00 + //Transform into wMonster + GETAIACTDATA(009); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); + int oldmon = monster->montype; + map->ConverToMonster(monster, data->wMonster,1); + return AI_SUCCESS; +} + +//Spawn Monster (2) +AIACT(010) +{ + //word wMonster; //Pos: 0x00 + //Spawn wMonster at current X,Y + GETAIACTDATA(010); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + fPoint position; + position = GServer->RandInCircle( entity->Position->current, 1 ); + map->AddMonster( data->wMonster, position, 0, 0, 1 ); + return AI_SUCCESS; +} + +//Call for backup +AIACT(011) +{ + //dword iDistance; //Pos: 0x00 + //dword iNumOfMonster; //Pos: 0x04 + //force iNumOfMonster of same team within iDistance to attack my target + //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + //if(target == NULL) return AI_FAILURE; + //Log(MSG_DEBUG, "AIACT(011)"); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + GETAIACTDATA(011); + + int chrCount = 0; + dword eCount = 0; + int searchDistance = data->iDistance; + + CMap* map = GServer->MapList.Index[entity->Position->Map]; + dword entityCount = map->MonsterList.size(); + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + if(other->CharType != TMONSTER) continue; + + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + chrCount++; + other->Battle->target = entity->Battle->target; + other->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); + + // other->Attack(target); + + if(chrCount >= data->iNumOfMonster) return AI_SUCCESS; + } + + return AI_SUCCESS; +} + +//start attack against closest player +AIACT(012) +{ + //Log(MSG_DEBUG, "AIACT(012)"); + //Run and attack "m_pNearCHAR" Nearest Character + // entity->Attack(entity->nearChar); + // Log(MSG_DEBUG, "AIACT(012)"); + if(entity->nearChar == NULL) + { + Log(MSG_DEBUG,"No target found in entity->nearchar. Now why is that?"); + return AI_FAILURE; + } + //entity->Battle->target=entity->nearChar->clientid; + CMonster* monster = reinterpret_cast(entity); + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + monster->Stats->stance = 1; + monster->SetStats(); + monster->StartAction( (CCharacter*) entity->nearChar, NORMAL_ATTACK, 0 ); + // Log(MSG_DEBUG, "AIACT(012)%i",entity->nearChar->clientid); + return AI_SUCCESS; +} + +//run and attack findChar +AIACT(013) +{ + //Log(MSG_DEBUG, "AIACT(013)"); + //Run and attack "m_pFindCHAR" Character found from aiobj loop things + // entity->Attack(entity->findChar); + if(entity->findChar == NULL) + { + Log(MSG_DEBUG,"No target found in entity->findchar. Now why is that?"); + return AI_FAILURE; + } + //entity->Battle->target=entity->findChar->clientid; + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + return AI_FAILURE; //monster has no agression value so cannot attack a player + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + monster->Stats->stance = 1; + monster->SetStats(); + monster->Battle->target = entity->findChar->clientid; + monster->StartAction( (CCharacter*) entity->findChar, NORMAL_ATTACK, 0 ); + return AI_SUCCESS; +} + +//get help from other monsters of the same monster type +AIACT(014) +{ + //dword iDistance; //Pos: 0x00 + //find aiobj within iDistance and force them to attack my target + // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + // if(target == NULL) return AI_FAILURE; + GETAIACTDATA(014); + CMonster* monster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + return AI_FAILURE; + int monstercount = 0; + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* othermon = map->MonsterList.at(j); + if(othermon == NULL) + continue; + if(othermon == monster) + continue; + if(othermon->montype != monster->montype) + continue; + if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) + { + othermon->StartAction( target, NORMAL_ATTACK, 0); + } + } + return AI_SUCCESS; +} + +//Retaliate against the last character that hit me +AIACT(015) +{ + //Run and attack "m_pDestCHAR" Blah? + //Log(MSG_DEBUG, "AIACT(015)"); + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as monster has no aggression"); + return AI_FAILURE; //monster has no agression value so cannot attack a player + } + if(monster->IsOnBattle()) + { + //already in battle so we don't want it switching all the time. maybe 75% of the time it will do nothing? + if(GServer->RandNumber(0,100) > 25) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as we got a really bad random number %i"); + return AI_FAILURE; //denied!!! + } + if(monster->Battle->hitby == monster->Battle->target) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as monster is already in combat with this player %i",monster->Battle->target); + return AI_FAILURE; //no need to initiate combat. I am already fighting this player + } + } + // otherwise change target to the person who just hit me + monster->Stats->stance = 1; + monster->SetStats(); + monster->Battle->target = monster->Battle->hitby; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + { + //Log(MSG_DEBUG, "I seem to have lost my target so I can't attack any more"); + return AI_FAILURE; + } + //Log(MSG_DEBUG, "Starting normal attack against opponent %i",target->clientid); + monster->StartAction( target, NORMAL_ATTACK, 0 ); + return AI_SUCCESS; +} + +//Run away to a point within distance iDistance of my spawn point +AIACT(016) +{ + //dword iDistance; + //Run Away! + //Log(MSG_DEBUG, "AIACT(016)"); + GETAIACTDATA(016); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;//Get it to our coord system! + entity->Position->destiny = GServer->RandInCircle(entity->Position->source, iDist); + //float radius = data->iDistance; + //float angle = rand()%(360); + // float angle = rg.IRandom(0,360); + // entity->UpdatePosition(); + // entity->MoveTo(entity->basic.pos.x + (radius * cos(angle)), entity->basic.pos.y + (radius * sin(angle))); + //float nX = (entity->Position->source.x + (radius * cos(angle))); + //entity->Position->destiny.x=nX; + //float nY = (entity->Position->source.y + (radius * cos(angle))); + //entity->Position->destiny.y=nY; + + monster->Stats->stance = 1; + monster->SetStats(); + + entity->Position->lastMoveTime = clock(); + //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, entity->Position->destiny.x*100 ); + ADDFLOAT ( pak, entity->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, 0x01 ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} + +//Drop Item +AIACT(017) +{ + //word item0; Pos: 0x00 + //word item1; Pos: 0x02 + //word item2; Pos: 0x04 + //word item3; Pos: 0x06 + //word item4; Pos: 0x08 + //word item5; pos: 0x0a dummy as a spacer + //dword btToOwner; //Pos: 0x0c + //Random drop one of item 1-5 + GETAIACTDATA(017); + //srand(time(NULL)); + int itemRand = rand()%(5); + //int itemRand = rg.IRandom(0,360); + int nItem = data->items[itemRand]; + if (nItem == 0)return AI_FAILURE; + CMonster* monster = reinterpret_cast(entity); + //CItem dropItem; + CDrop* newdrop = new (nothrow) CDrop; + newdrop->clientid = GServer->GetNewClientID( ); + newdrop->posMap = entity->Position->Map; + newdrop->pos = GServer->RandInCircle( entity->Position->current, 3 ); + newdrop->droptime = time(NULL); + if(data->btToOwner == 1) + { + newdrop->owner = monster->MonsterDrop->firsthit; + newdrop->thisparty = monster->thisparty; + } + else + { + newdrop->owner = 0; + newdrop->thisparty = 0; + } + ClearItem(newdrop->item); + newdrop->amount = 1; + newdrop->type = 2; + newdrop->item.itemnum = nItem % 1000; + newdrop->item.itemtype = nItem / 1000; + if(newdrop->item.IsStackable()) + { + newdrop->item.count = 1; + } + else + { + newdrop->item.stats = 0; + newdrop->item.durability = 40; + newdrop->item.lifespan = 1000; + newdrop->item.socketed = 0; + newdrop->item.appraised = 1; + newdrop->item.refine = 0; + } + //Log(MSG_DEBUG, "item type= %i item num = %i", newdrop->item.itemtype, newdrop->item.itemnum); + newdrop->item.count = 1; + + + CMap* map = GServer->MapList.Index[newdrop->posMap]; + map->AddDrop( newdrop ); + // entity->DropEntityItem(&dropItem); + + return AI_SUCCESS; +} + +//Make nearby monsters attack my target +AIACT(018) +{ + //word cMonster; //Pos: 0x00 + //word wHowMany; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + //make wHowMany monsters of type cMonster within iDistance attack my target + // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + // if(target == NULL) return AI_FAILURE; + GETAIACTDATA(018); + CMonster* monster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + return AI_FAILURE; + int monstercount = 0; + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* othermon = map->MonsterList.at(j); + if(othermon == NULL) + continue; + if(othermon == monster) + continue; + if(othermon->montype != data->cMonster) + continue; + if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) + { + othermon->StartAction( target, NORMAL_ATTACK, 0); + monstercount++; + if(monstercount >= data->wHowMany) + return AI_SUCCESS; + } + } + return AI_SUCCESS; +} + +//Start attack +AIACT(019) +{ + //Run and attack "m_pNearCHAR" //Identical to 012 + return F_AI_ACT_012(server, entity, raw); +} + +//Spawn Monster (5) +AIACT(020) +{ + //word cMonster; //Pos: 0x00 + //byte btPos; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + + //Spawn cMonster within iDistance of btPos + + //btPos 0 = my pos + //btPos 1 = "m_pDestCHAR" pos + //btPos 2 = targets pos + GETAIACTDATA(020); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + if(monster == NULL) return AI_FAILURE; + + fPoint position; + + if(data->btPos == 0) + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + } + else if(data->btPos == 1) + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + //dest char + } + else if(data->btPos == 2) + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + position = GServer->RandInCircle( target->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + } + //Log(MSG_DEBUG, "AIACT(020)option spawn %i distance %i monstertype %i",data->btPos,data->iDistance,data->cMonster); + return AI_SUCCESS; +} + +//Unknown +AIACT(021) +{ + //Does absolutely nothing + return AI_SUCCESS; +} + +//Unknown +AIACT(022) +{ + //Does absolutely nothing + return AI_SUCCESS; +} + +//ghostseeds and ghosts and player summons commit suicide +AIACT(023) +{ + //Commit suicide + entity->Stats->HP = -1; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, entity->clientid ); + ADDDWORD ( pak, 0x8005 ); + GServer->SendToVisible( &pak, entity ); + //Log(MSG_DEBUG, "AIACT(023) suicide monstertype %i",thisMonster->montype); + //thisMonster->DoAi(thisMonster->thisnpc->AI, 5); + //now release the id in the monsterlist + + //map->DeleteMonster( thisMonster); + + // something weird going on here. Client ids not being relinquished. Deletemonster does not help. it crashes the server. + + return AI_SUCCESS; +} + +//Do Skill +AIACT(024) +{ + //byte btTarget; //Pos: 0x00 + //word nSkill; //Pos: 0x02 + //word nMotion; //Pos: 0x04 //8 = SKILL_ATTACK 6 = magic + + //Use nSkill & nMotion? + //btTarget 0 = m_pFindCHAR + //btTarget 1 = target + //btTarget 2 = self skill + GETAIACTDATA(024); + // Log(MSG_DEBUG, "Do Skill btTarget %i nSkill %i nMotion %i",data->btTarget,data->nSkill,data->nMotion); + // Log(MSG_DEBUG, "entity %i",entity->clientid); + // Log(MSG_DEBUG, "entity->findChar %i",entity->findChar->clientid); + // Log(MSG_DEBUG, "entity->nearChar %i", entity->nearChar->clientid); + CSkills* thisskill = GServer->GetSkillByID( data->nSkill ); + if(thisskill == NULL)return AI_FAILURE; + CCharacter* monster = entity; + //if(monster->Battle->atktype != 0) + //{ + // Log(MSG_DEBUG,"Cannot apply battle attacktype. Monster already has one"); + // return AI_FAILURE; + //} + //Log(MSG_DEBUG,"AIACT(024)skill attack id: %i target: %i type: %i btTarget: %i nMotion: %i", data->nSkill, thisskill->target, thisskill->skilltype, data->btTarget, data->nMotion); + switch (thisskill->skilltype) + { + case 3: //damage action + case 6: //magic attack + { + CCharacter* target = entity->GetCharTarget( ); + //Log(MSG_DEBUG,"SKILL_ATTACK or DAMAGE_ACTION selected"); + if(target==NULL) + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + //Log(MSG_DEBUG,"SKILL_ATTACK selected. target: %i",target->clientid); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + break; + case 7: //AOE magic attack + { + CCharacter* target = entity->GetCharTarget( ); + //Log(MSG_DEBUG,"AOE_TARGET selected"); + if(target == NULL) + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + //Log(MSG_DEBUG,"AOE_TARGET selected. target: %i",target->clientid); + monster->StartAction( (CCharacter*) target, AOE_TARGET, data->nSkill ); + } + break; + case 8: //self buff + { + //Log(MSG_DEBUG,"BUFF_SELF selected."); + monster->StartAction( NULL, BUFF_SELF, data->nSkill ); + } + break; + case 9: //skill buff + case 13://skill buff + case 11: //heal + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) //some skills always return a NULL target here + { + if(entity->nearChar == NULL) // maybe there really is no possible target. + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + else //so we are going to have them select a NEW target (if possible) rather than return AI_FAILURE + { + //Log(MSG_DEBUG,"SKILL_BUFF type 13 selected. NEW target: %i",entity->nearChar->clientid); + CCharacter* newtarget = entity->nearChar; + if(newtarget == NULL) + return AI_FAILURE; + monster->StartAction( (CCharacter*) newtarget, SKILL_BUFF, data->nSkill ); + } + } + else + { + //Log(MSG_DEBUG,"SKILL_BUFF type %i selected. target: %i",thisskill->skilltype, target->clientid); + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + break; + case 10: //heal AOE centered on self + //Log(MSG_DEBUG,"BUFF_AOE selected. Skill id %i", data->nSkill); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + break; + case 17: //skill AOE centered on self + { + //Log(MSG_DEBUG,"SKILL_AOE selected. Skill id %i", data->nSkill); + monster->StartAction( NULL, SKILL_AOE, data->nSkill ); + } + break; + default: + { + //Log(MSG_WARNING,"Skill type not recognized. type: %i",thisskill->skilltype); + } + break; + } + /* + if (data->btTarget == 0) + { + //CCharacter* target = entity->GetCharTarget(); + //if(target==NULL)return AI_FAILURE; + //CCharacter* monster = entity; //defined twice???? + Log(MSG_DEBUG,"BUFF_AOE selected"); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + } + else if (data->btTarget == 1) + { + CCharacter* target = entity->GetCharTarget( ); + if(target==NULL)return AI_FAILURE; + if(data->nMotion == 8) + { + Log(MSG_DEBUG,"SKILL_ATTACK selected"); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + else + { + Log(MSG_DEBUG,"SKILL_BUFF selected"); + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + else if (data->btTarget == 2) + { + if(thisskill->target == tYourself) + { + Log(MSG_DEBUG,"BUFF_SELF selected"); + monster->StartAction( entity, BUFF_SELF, data->nSkill ); + } + else if (thisskill->target == tAlly) + { + Log(MSG_DEBUG,"BUFF_AOE selected"); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + } + else if (thisskill->target == tHostileCharacter) + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL)return AI_FAILURE; + if(data->nMotion == 8) + { + Log(MSG_DEBUG,"SKILL_ATTACK selected"); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + else + { + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + }*/ + return AI_SUCCESS; +} + +//Set Variable (1) +AIACT(025) +{ + // if(entity->_EntityType != ENTITY_NPC) return AI_FAILURE; + //byte btVarIDX Pos: 0x00 + //dword iValue Pos: 0x04 + //byte btOp Pos: 0x08 + GETAIACTDATA(025); + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return AI_FAILURE; + if(data->btVarIDX > 19) return AI_FAILURE; + short tempval = GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX]; + switch(data->btOp) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + break; + case 9: + tempval++; + break; + default: + return AI_FAILURE; + break; + } + if(tempval < 0)tempval = 0; + GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX] = tempval; + if(data->btVarIDX == 0) //changing index 0 changes event variable at client too. Possible others too but for now just 0 + { + //Log(MSG_DEBUG,"AIACT(025) eventID has changed from %i to %i",monster->thisnpc->eventid,tempval); + monster->thisnpc->eventid = tempval; + } + return AI_SUCCESS; +} + +//Set Variable (2) +AIACT(026) +{ + //Set WorldVAR + //word nVarNo; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + GETAIACTDATA(026); + //PY: we don't know how to use worldVar so here are some fun things we can do with custom AI + switch(data->nVarNo) + { + case 1: //change world exp rate in the config + GServer->Config.EXP_RATE = data->iValue; + return AI_SUCCESS; + break; + default: + return AI_SUCCESS; + break; + } +} + +//Set Economy Variable. Could have some fun with tis in the future +AIACT(027) +{ + //Set EconomyVAR + //word nVarNo; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + return AI_SUCCESS; +} + +//Shout/Ann LTB String +AIACT(028) +{ + GETAIACTDATA(028); + //byte btMsgType Pos: 0x00 + //dword iStrID Pos: 0x04 + dword npcId = 0; + //if(entity->_EntityType == ENTITY_NPC) + CMonster* thisMonster = reinterpret_cast(entity); + switch(data->btMsgType) + { + case 0: //current field + // don't think this is possible. No way to get a character to send it to?? + break; + case 1: //shout to map + //Log(MSG_INFO,"AI -- NPC Shouts to map %i",entity->Position->Map); + GServer->NPCShout(entity->Position->Map,GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); + break; + case 2: //announce to server. + GServer->NPCAnnounce(GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); + break; + default: + break; + } + + //npcId = reinterpret_cast(entity)->NpcID; + // }else if(entity->_EntityType == ENTITY_MONSTER){ + // npcId = reinterpret_cast(entity)->MonID; + // }else{ + // return AI_FAILURE; + // } + // std::map::iterator triggerITR = server->NpcNames.find(npcId); + // if(triggerITR == server->NpcNames.end()) return AI_FAILURE; + + // char* npcName = triggerITR->second; + // char* sayStr = server->lngAi->Data(data->iStrID, 1); + // if(sayStr == NULL) return AI_FAILURE; + // if(data->btMsgType == 0){ + //Chat + // CPacket pakout(0x783); + // pakout.Add(entity->_ClientID); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // }else if(data->btMsgType == 1){ + //Shout + // CPacket pakout(0x785); + // pakout.Add(npcName); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // }else if(data->btMsgType == 2){ + //Announce + // CPacket pakout(0x702); + // pakout.AddBytes((byte*)npcName, strlen(npcName)); + // pakout.Add('>'); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // } + // delete [] sayStr; + //Log(MSG_DEBUG, "AIACT(028)"); + return AI_SUCCESS; +} + +//Move to my owner's location +AIACT(029) +{ + //Move within 20% of the distance of my "CALLER" + GETAIACTDATA(029); + // if(entity->_EntityType != ENTITY_MONSTER) return AI_FAILURE; + // CMonster* thisMonster = reinterpret_cast(entity); + // CWorldEntity* caller = thisMonster->thisZone->GetEntity(thisMonster->_CallerID); + // if(caller == NULL) return AI_FAILURE; + // caller->UpdatePosition(); + // thisMonster->UpdatePosition(); + if(!entity->IsMonster( )) return AI_FAILURE; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL) return AI_FAILURE; + caller->UpdatePosition(); + thisMonster->UpdatePosition(); + thisMonster->Stats->stance = 1; + thisMonster->SetStats(); + thisMonster->Position->source = caller->Position->current; + int iDist = GServer->RandNumber(5, 20); //move to between 5% and 20% of owners position + float xDist = thisMonster->Position->current.x - thisMonster->Position->source.x; + float yDist = thisMonster->Position->current.y - thisMonster->Position->source.y; + float nX = thisMonster->Position->source.x + (xDist * iDist / 100); + float nY = thisMonster->Position->source.y + (yDist * iDist / 100); + thisMonster->Position->destiny.x = nX; + thisMonster->Position->destiny.y = nY; + thisMonster->Position->lastMoveTime = clock(); + ClearBattle( thisMonster->Battle );// this also seems to clear the attackers battle?? + //entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, thisMonster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, thisMonster->Position->destiny.x*100 ); + ADDFLOAT ( pak, thisMonster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, thisMonster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + + return AI_SUCCESS; +} + +//Do Trigger +AIACT(030) +{ + //word lenszTrigger; //Pos: 0x00 + //char* szTrigger; //Pos: 0x02 + GETAIACTDATA(030); + char* tempName = reinterpret_cast(&data->szTrigger) - 2; + dword hash = MakeStrHash(tempName); + //Log(MSG_INFO, "Execute Quest Trigger %s[%d] [%08x]", tempName, data->lenszTrigger, hash); + //make a dummy player from this monster + CClientSocket* thisclient = new CClientSocket; + CPlayer* player = new CPlayer( thisclient ); + if(player == NULL) + { + Log(MSG_INFO, "Player not found in AIACT 30"); + return AI_FAILURE; + } + thisclient->player = (void*) player; + player->Position = entity->Position; + + int quest = player->ExecuteQuestTrigger(hash); + if(quest == QUEST_SUCCESS) + return AI_SUCCESS; + return AI_FAILURE; +} + +//set monster's attack target to the same as the owner +AIACT(031) +{ + //Log(MSG_DEBUG, "AIACT(031)"); + //Attack my "CALLER"(s target) if he is not an ally (must be caller's target...) + if(!entity->IsMonster( )) return AI_FAILURE; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL) return AI_FAILURE; + thisMonster->Position->source = caller->Position->current; + CCharacter* target = map->GetCharInMap(caller->Battle->target); + if(target == NULL) return AI_FAILURE; + // need to prevent the summon from attacking other summons of the same player + CMonster* otherMonster = reinterpret_cast(target); + if(otherMonster->owner == thisMonster->owner) return AI_FAILURE; + thisMonster->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); + //Log(MSG_DEBUG, "AIACT(031) summon attacking player's target"); + + return AI_SUCCESS; +} + +//Set PK flag for specified zone +AIACT(032) +{ + //word nZoneNo; //Pos: 0x00 + //byte btOnOff; //Pos: 0x02 + //Set PK Flag (btOnOff) in nZoneNo + GETAIACTDATA(032); + //Log(MSG_DEBUG, "AIACT(032) zone (1)"); + return AI_SUCCESS; +} + +//Set regen mode for specified zone +AIACT(033) +{ + //word nZoneNo //if 0, current map + //word nNewValue + //if nNewValue == 2 -> toggle + //else set regen system nNewValue (0 = off, 1 = on) + GETAIACTDATA(033); + //Log(MSG_DEBUG, "AIACT(033) zone (2)"); + return AI_SUCCESS; +} + +//Item (?) +AIACT(034) +{ + //word nItemNum; //Pos: 0x00 + //word nCount; //Pos: 0x02 + //Give item to "CALLER" + GETAIACTDATA(034); + if(!entity->IsMonster( )) return AI_SUCCESS; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CPlayer* caller = map->GetPlayerInMap( thisMonster->owner); + if(caller == NULL) return AI_SUCCESS; + int nItem = data->nItemNum; + if (nItem == 0)return AI_SUCCESS; + char itemtypes[15] = {4,0,0,0,0,0,0,0,0,0,1,2,2,4,3}; + CItem thisitem; + thisitem.Clear(); + thisitem.itemtype = nItem / 1000; + thisitem.itemnum = nItem % 1000; + thisitem.count = data->nCount; + if( itemtypes[thisitem.itemtype] == 0 ) + { + thisitem.appraised = true; + thisitem.lifespan = 100; + thisitem.gem = 0; + thisitem.durability = GServer->STB_ITEM[thisitem.itemtype-1].rows[thisitem.itemnum][29]; + } + int thisslot = caller->AddItem( thisitem ); + //Log(MSG_DEBUG,"Item type = %i item ID = %i Count = %i Slot = %i",thisitem.itemtype,thisitem.itemnum,thisitem.count,thisslot); + if (thisslot == 0xffff) // Fail + { + BEGINPACKET( pak, 0x7a7); + ADDWORD(pak, 0x00); + ADDBYTE(pak, 0x03); + ADDBYTE(pak, 0x00); + caller->client->SendPacket(&pak); + return AI_SUCCESS; + } + else // Success + { + BEGINPACKET( pak, 0x71f); + ADDBYTE(pak, 0x01); + ADDBYTE(pak, thisslot); + ADDWORD(pak, caller->items[thisslot].GetPakHeader( ) ); + ADDDWORD(pak, caller->items[thisslot].GetPakData( ) ); + caller->client->SendPacket(&pak); + } + //Log(MSG_DEBUG, "AIACT(034) Item (?)"); + return AI_SUCCESS; +} + +//Set Variable (4) +AIACT(035) +{ + //Set AiVAR + //word nVarIDX; Pos: 0x00 + //dword iValue; Pos: 0x04 + //byte btOp; Pos: 0x08 + GETAIACTDATA(035); + CMonster* thisMonster = reinterpret_cast(entity); + if(data->nVarIDX >19)return AI_SUCCESS; + int tempval = thisMonster->AIVar[data->nVarIDX]; + switch(data->btOp) + { + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + if(tempval <= 0) + { + thisMonster->AIVar[data->nVarIDX] = 0; + return AI_SUCCESS; + } + break; + case 9: + tempval++; + break; + default: + return AI_SUCCESS; + break; + } + thisMonster->AIVar[data->nVarIDX] = tempval; + //thisMonster->thisnpc->Objvar->SetVar(data->btOp, data->iValue); //code not working + return AI_SUCCESS; +} + +//Monster (1) +AIACT(036) +{ + //word nMonster; //Pos: 0x00 + //byte btMaster; //Pos: 0x02 + //Spawn nMonster at my position (has btMaster)? + GETAIACTDATA(036); + // CWorldEntity* master = NULL; + // if(data->btMaster) master = entity; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + CMap* map = GServer->MapList.Index[entity->Position->Map]; + int owner = 0; + if(data->btMaster == 1) + owner = monster->clientid; + map->AddMonster( data->nMonster, entity->Position->current, 0, monster->Status->spawnid, 10 ); + //Log(MSG_DEBUG, "AIACT(36) monster (1) no code here"); + return AI_SUCCESS; +} + +//Monster (2) +AIACT(037) +{ + //word nMonster; //Pos: 0x00 + //word nPos; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + //byte btMaster; //Pos: 0x08 + + //Spawn nMonster at nPos within iDistance (has btMaster)? + //nPos = 0 my pos + //nPos = 1 m_pDestCHAR pos + //nPos = 2 target pos + GETAIACTDATA(037); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + if(monster == NULL) return AI_FAILURE; + int owner = 0; + if(data->btMaster == 1) + owner = monster->clientid; + fPoint position; + + if(data->nPos == 0) //spawn monster in a circle with radius iDistance around my current position + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + } + else if(data->nPos == 1) //spawn monster in a circle with radius iDistance around my destiny position + { + position = GServer->RandInCircle( entity->Position->destiny, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + //dest char + } + else if(data->nPos == 2) //spawn monster in a circle with radius iDistance around my target's current position + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + position = GServer->RandInCircle( target->Position->current, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + } + return AI_SUCCESS; +} + +//TDEF code. move to next waypoint +AIACT(038) +{ + //byte cSpeed pos 0x00 + GETAIACTDATA(038); + CMonster* monster = reinterpret_cast(entity); + monster->NextWayPoint ++; + monster->Position->destiny.x = GServer->WPList[monster->Position->Map][monster->NextWayPoint].X; + monster->Position->destiny.y = GServer->WPList[monster->Position->Map][monster->NextWayPoint].Y; + monster->Stats->stance = data->cSpeed; + entity->Position->lastMoveTime = clock(); + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} diff --git a/Src/World Server/aip/AiActions.cpp.bak b/Src/World Server/aip/AiActions.cpp.bak new file mode 100644 index 0000000..556660d --- /dev/null +++ b/Src/World Server/aip/AiActions.cpp.bak @@ -0,0 +1,1472 @@ +// Props to ExJam for this code :D +#include "../WorldServer.h" + +// extern CRandomMersenne rg; + +//Stop all actions +AIACT(000) +{ + //SetCMD_Stop (Stop all actions i guess?) + //entity->StopAll(); + CMonster* monster = reinterpret_cast(entity); + ClearBattle( monster->Battle ); + monster->Position->destiny = monster->Position->current; + return AI_SUCCESS; +} + +//Do Action +AIACT(001) +{ + //byte cAction; //Pos: 0x00 + //CObjMOB::Set_EMOTION (pak 781) + GETAIACTDATA(001); + BEGINPACKET( pak, 0x781 ); + ADDWORD ( pak, data->cAction ); + ADDWORD ( pak, 0); + ADDWORD ( pak, entity->clientid ); + GServer->SendToVisible(&pak, entity,NULL ); + return AI_SUCCESS; +} + +//Say LTB String +AIACT(002) +{ + //Client side say text + //Log(MSG_DEBUG, "Say LTB String"); + //dword iStrID; Pos: 0x00 + GETAIACTDATA(002); + + //Think this is crashing the server. Taken out for the time being. + //lets have the NPC also say the LTB string over the regular message system. This will show up green ^-^ + //CMonster* thisMonster = reinterpret_cast(entity); + //if(thisMonster->CharType == TNPC) // check if it is an NPC and not a normal monster. + //{ + // BEGINPACKET( pak, 0x0784 ); + // ADDSTRING( pak, GServer->Ltbstring[data->iStrID].NPCname ); + // ADDBYTE( pak, 0 ); + // ADDSTRING( pak, GServer->Ltbstring[data->iStrID].LTBstring ); + // ADDBYTE( pak, 0 ); + // GServer->SendToVisible(&pak, thisMonster); + //} + return AI_SUCCESS; +} + +//Move (1) +AIACT(003) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //move randomly within iDistance (individual x and y so its within a square) + GETAIACTDATA(003); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;// * 2;//Get it to our coord system! + //srand(time(NULL)); + int randDis = rand()%(iDist << 1); + float nX = (entity->Position->current.x + randDis) - iDist; + entity->Position->destiny.x=nX; + //srand(time(NULL)); + randDis = rand()%(iDist << 1); + float nY = (entity->Position->current.y + randDis) - iDist; + entity->Position->destiny.y = nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + entity->Position->lastMoveTime = clock(); + //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + entity->Battle->atktype=0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, entity->Position->destiny.x*100 ); + ADDFLOAT ( pak, entity->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + //Log(MSG_DEBUG, "move1 stance %i",data->cSpeed); + //Log(MSG_DEBUG, "move(1)"); + return AI_SUCCESS; +} + +//Move (2) +AIACT(004) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //move randomly within iDistance of spawn position (square) Position->source + GETAIACTDATA(004); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;//Get it to our coord system! + int randDis = rand()%(iDist << 1); + float nX = (monster->Position->source.x + randDis) - iDist; + monster->Position->destiny.x=nX; + //randDis = rg.IRandom(0, iDist << 1); + randDis = rand()%(iDist << 1); + float nY = (monster->Position->source.y + randDis) - iDist; + monster->Position->destiny.y=nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + monster->Position->lastMoveTime = clock(); + //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + //Log(MSG_DEBUG, "move2 stance %i",data->cSpeed); + + return AI_SUCCESS; +} + +//Move (3) +AIACT(005) +{ + //byte cSpeed; //Pos: 0x00 + //move randomly within 200 of "m_pFindCHAR" (square) + GETAIACTDATA(005); + if(entity->findChar == NULL) + return AI_FAILURE; + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + entity->findChar->UpdatePosition(); + + int iDist = 2;//Get it to our coord system! + + + int randDis = rand()%(iDist << 1); + float nX = (entity->findChar->Position->current.x + randDis) - iDist; + entity->Position->destiny.x=nX; + + + randDis = rand()%(iDist << 1); + float nY = (entity->findChar->Position->current.y + randDis) - iDist; + entity->Position->destiny.y=nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + //entity->MoveTo(nX, nY); + //entity->Position->lastMoveTime = clock(); + //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + // Log(MSG_DEBUG, "move3 stance %i",data->cSpeed); + // Log(MSG_DEBUG, "move (3)"); + // RESETPACKET(pak, 0x783); + // ADDWORD(pak,entity->findChar->clientid);//entity->clientid); + // ADDSTRING(pak,"hello there"); + // ADDBYTE(pak,0); + // GServer->SendToVisible(&pak, entity); + // server->SendPacketToZone(entity, &pakout); + + return AI_SUCCESS; +} + +//Move (?) +AIACT(006) +{ + //dword iDistance; //Pos: 0x00 + //byte cAbType; //Pos: 0x04 + //byte cMoreLess; //Pos: 0x05 + //Run and Attack aiobj within iDistance that has the lowest or greatest cAbType + //cAbType + //0 = Level + //1 = Attack + //2 = Defense + //3 = "Res"? Magic Resistance? + //4 = HP + //5 = Charm + GETAIACTDATA(006); + + dword eCount = 0; + int highestAB = -9999999; + // CWorldEntity* highestEntity = NULL; + int lowestAB = 9999999; + // CWorldEntity* lowestEntity = NULL; + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + return AI_FAILURE; //monster has no agression value so cannot attack a player + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + CMap* map= GServer->MapList.Index[entity->Position->Map]; + CCharacter* Target = NULL; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + if(GServer->IsMonInCircle( monster->Position->current, player->Position->current, data->iDistance)) + { + switch(data->cAbType) + { + case 0: //level + if(data->cMoreLess == 0) + { + if(player->Stats->Level > lowestAB) + { + lowestAB = player->Stats->Level; + Target = player; + }else continue; + }else{ + if(player->Stats->Level < highestAB) + { + highestAB = player->Stats->Level; + Target = player; + }else continue; + } + break; + case 1: //Attack power + if(data->cMoreLess == 0) + { + if(player->Stats->Attack_Power > lowestAB) + { + lowestAB = player->Stats->Attack_Power; + Target = player; + }else continue; + }else{ + if(player->Stats->Attack_Power < highestAB) + { + highestAB = player->Stats->Attack_Power; + Target = player; + }else continue; + } + break; + case 2: //defense + if(data->cMoreLess == 0) + { + if(player->Stats->Defense> lowestAB) + { + lowestAB = player->Stats->Defense; + Target = player; + }else continue; + }else{ + if(player->Stats->Defense < highestAB) + { + highestAB = player->Stats->Defense; + Target = player; + }else continue; + } + break; + case 3: //Magic Def + if(data->cMoreLess == 0) + { + if(player->Stats->Magic_Defense> lowestAB) + { + lowestAB = player->Stats->Magic_Defense; + Target = player; + }else continue; + }else{ + if(player->Stats->Magic_Defense < highestAB) + { + highestAB = player->Stats->Magic_Defense; + Target = player; + }else continue; + } + break; + case 4: // HP + if(data->cMoreLess == 0) + { + if(player->Stats->HP> lowestAB) + { + lowestAB = player->Stats->HP; + Target = player; + }else continue; + }else{ + if(player->Stats->HP < highestAB) + { + highestAB = player->Stats->HP; + Target = player; + }else continue; + } + break; + case 5: // Charm + if(data->cMoreLess == 0) + { + if(player->Attr->Cha > lowestAB) + { + lowestAB = player->Attr->Cha; + Target = player; + }else continue; + }else{ + if(player->Attr->Cha < highestAB) + { + highestAB = player->Attr->Cha; + Target = player; + }else continue; + } + break; + default: + break; + } + } + } + // OK we found the new Target player. now attack him. + monster->Stats->stance = 1; + monster->SetStats(); + monster->StartAction( Target, NORMAL_ATTACK, 0 ); + + // int searchDistance = data->iDistance * 100; + + // CWorldEntity** entityList = entity->thisZone->GetEntityList(); + // dword entityCount = entity->thisZone->GetEntityCount(); + // for(dword i = 1; i < MAX_ZONE_CLIENTID; i++){ + // CWorldEntity* other = entityList[i]; + // if(eCount >= entityCount) break; + // if(other == NULL) continue; + // eCount++; + // if(other == entity) continue; + // if(other->_EntityType == ENTITY_NO_TYPE) continue; + // if(other->_EntityType == ENTITY_DROP) continue; + + // int dX = (int)abs((int)other->curBlock.x - (int)entity->curBlock.x); + // if(dX > 1) continue; + // int dY = (int)abs((int)other->curBlock.y - (int)entity->curBlock.y); + // if(dY > 1) continue; + + // int iDistance = other->basic.pos.distance(entity->basic.pos); + // if(iDistance > searchDistance) continue; + + // int value = AI_GetAbility(other, data->cAbType); + + // if(value < lowestAB){ + // lowestAB = value; + // lowestEntity = other; + // } + // if(value > highestAB){ + // highestAB = value; + // highestEntity = other; + // } + // } + // if(data->cMoreLess){ + // if(lowestEntity){ + //SetCmdRUN_nATTACK + // entity->Attack(lowestEntity); + // } + // }else{ + // if(highestEntity){ + //SetCmdRUN_nATTACK + // entity->Attack(highestEntity); + // } + // } + //Log(MSG_DEBUG, "move (?)"); + return AI_SUCCESS; +} + +//Unknown +AIACT(007) +{ + //CObjCHAR::Special_ATTACK + //Log(MSG_DEBUG, "AIACT(007)"); + return AI_SUCCESS; +} + +//Move (4) +AIACT(008) +{ + //dword iDistance; //Pos: 0x00 + //byte cSpeed; //Pos: 0x04 + //Some weird shit going on here + //move within some sort of distance of target (i think possibly % iDistance of distance between target)(run away?) + //Log(MSG_DEBUG, "AIACT(008)"); + GETAIACTDATA(008); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + int iDist = data->iDistance;//Get it to our coord system! + float xDist = monster->Position->current.x - monster->Position->source.x; + float yDist = monster->Position->current.y - monster->Position->source.y; + float nX = monster->Position->source.x + (xDist * iDist / 100); + float nY = monster->Position->source.y + (yDist * iDist / 100); + monster->Position->destiny.x = nX; + monster->Position->destiny.y = nY; + monster->Stats->stance = data->cSpeed; + monster->SetStats(); + monster->Position->lastMoveTime = clock(); + ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + //entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, monster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} + +//Convert Monster to new monster (1) +AIACT(009) +{ + //word wMonster; //Pos: 0x00 + //Transform into wMonster + GETAIACTDATA(009); + CMap* map= GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); + int oldmon = monster->montype; + map->ConverToMonster(monster,data->wMonster,1); + //Log(MSG_DEBUG, "AIACT(009)convert monster type %i to %i",oldmon,data->wMonster); + return AI_SUCCESS; +} + +//Spawn Monster (2) +AIACT(010) +{ + //word wMonster; //Pos: 0x00 + //Spawn wMonster at current X,Y + GETAIACTDATA(010); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + fPoint position; + //CMonster* newmonster; + position = GServer->RandInCircle( entity->Position->current, 1 ); + map->AddMonster( data->wMonster, position, 0, 0, 1 ); + // entity->thisZone->SpawnMonster(data->wMonster, entity->basic.pos, entity->basic.map); + //Log(MSG_DEBUG, "AIACT(010)spawn %i",data->wMonster); + return AI_SUCCESS; +} + +//Call for backup +AIACT(011) +{ + //dword iDistance; //Pos: 0x00 + //dword iNumOfMonster; //Pos: 0x04 + //force iNumOfMonster of same team within iDistance to attack my target + //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + //if(target == NULL) return AI_FAILURE; + //Log(MSG_DEBUG, "AIACT(011)"); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + GETAIACTDATA(011); + + int chrCount = 0; + dword eCount = 0; + int searchDistance = data->iDistance; + + CMap* map = GServer->MapList.Index[entity->Position->Map]; + dword entityCount = map->MonsterList.size(); + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + if(other->CharType != TMONSTER) continue; + + // CWorldEntity** entityList = entity->thisZone->GetEntityList(); + // dword entityCount = entity->thisZone->GetEntityCount(); + // for(dword i = 1; i < MAX_ZONE_CLIENTID; i++){ + // CWorldEntity* other = entityList[i]; + // if(eCount >= entityCount) break; + // if(other == NULL) continue; + // eCount++; + // if(other == entity) continue; + // if(other->_EntityType == ENTITY_MONSTER) continue; + // if(other->team != entity->team) continue; + // int dX = (int)abs((int)other->curBlock.x - (int)entity->curBlock.x); + // if(dX > 1) continue; + // int dY = (int)abs((int)other->curBlock.y - (int)entity->curBlock.y); + // if(dY > 1) continue; + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + // int iDistance = other->basic.pos.distance(entity->basic.pos); + if(iDistance > searchDistance) continue; + chrCount++; + other->Battle->target = entity->Battle->target; + other->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); + + // other->Attack(target); + + if(chrCount >= data->iNumOfMonster) return AI_SUCCESS; + } + + return AI_SUCCESS; +} + +//start attack +AIACT(012) +{ + //Log(MSG_DEBUG, "AIACT(012)"); + //Run and attack "m_pNearCHAR" Nearest Character + // entity->Attack(entity->nearChar); + // Log(MSG_DEBUG, "AIACT(012)"); + if(entity->nearChar == NULL) + { + Log(MSG_DEBUG,"No target found in entity->nearchar. Now why is that?"); + return AI_FAILURE; + } + //entity->Battle->target=entity->nearChar->clientid; + CMonster* monster = reinterpret_cast(entity); + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + monster->Stats->stance = 1; + monster->SetStats(); + monster->StartAction( (CCharacter*) entity->nearChar, NORMAL_ATTACK, 0 ); + // Log(MSG_DEBUG, "AIACT(012)%i",entity->nearChar->clientid); + return AI_SUCCESS; +} + +//start attack +AIACT(013) +{ + //Log(MSG_DEBUG, "AIACT(013)"); + //Run and attack "m_pFindCHAR" Character found from aiobj loop things + // entity->Attack(entity->findChar); + if(entity->findChar == NULL) + { + Log(MSG_DEBUG,"No target found in entity->findchar. Now why is that?"); + return AI_FAILURE; + } + //entity->Battle->target=entity->findChar->clientid; + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + return AI_FAILURE; //monster has no agression value so cannot attack a player + int aggro = GServer->RandNumber(1,20); + if(aggro > monster->thisnpc->aggresive) + return AI_FAILURE; // failed aggresiveness check + monster->Stats->stance = 1; + monster->SetStats(); + monster->Battle->target = entity->findChar->clientid; + monster->StartAction( (CCharacter*) entity->findChar, NORMAL_ATTACK, 0 ); + //Log(MSG_DEBUG, "AIACT(013) Monster type %i initiated combat with player %i",monster->montype, entity->findChar->clientid); + return AI_SUCCESS; +} + +//get help from other monsters +AIACT(014) +{ + //dword iDistance; //Pos: 0x00 + //find aiobj within iDistance and force them to attack my target + // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + // if(target == NULL) return AI_FAILURE; + GETAIACTDATA(014); + CMonster* monster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + return AI_FAILURE; + int monstercount = 0; + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* othermon = map->MonsterList.at(j); + if(othermon == NULL) + continue; + if(othermon == monster) + continue; + if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) + { + othermon->StartAction( target, NORMAL_ATTACK, 0); + } + } + // dword eCount = 0; + // int searchDistance = data->iDistance * 100; + + // CWorldEntity** entityList = entity->thisZone->GetEntityList(); + // dword entityCount = entity->thisZone->GetEntityCount(); + // for(dword i = 1; i < MAX_ZONE_CLIENTID; i++){ + // CWorldEntity* other = entityList[i]; + // if(eCount >= entityCount) break; + // if(other == NULL) continue; + // eCount++; + // if(other == entity) continue; + // if(other->_EntityType == ENTITY_NO_TYPE) continue; + // if(other->_EntityType == ENTITY_DROP) continue; + // if(other->team != entity->team) continue; + + // int dX = (int)abs((int)other->curBlock.x - (int)entity->curBlock.x); + // if(dX > 1) continue; + // int dY = (int)abs((int)other->curBlock.y - (int)entity->curBlock.y); + // if(dY > 1) continue; + + // int iDistance = other->basic.pos.distance(entity->basic.pos); + // if(iDistance > searchDistance) continue; + + // other->Attack(target); + // return AI_SUCCESS; + // } + //Log(MSG_DEBUG, "AIACT(014)"); + return AI_SUCCESS; +} + +//Retaliate against the last character that hit me +AIACT(015) +{ + //Run and attack "m_pDestCHAR" Blah? + //Log(MSG_DEBUG, "AIACT(015)"); + CMonster* monster = reinterpret_cast(entity); + if(monster->thisnpc->aggresive == 0) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as monster has no aggression"); + return AI_FAILURE; //monster has no agression value so cannot attack a player + } + if(monster->IsOnBattle()) + { + //already in battle so we don't want it switching all the time. maybe 75% of the time it will do nothing? + if(GServer->RandNumber(0,100) > 25) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as we got a really bad random number %i"); + return AI_FAILURE; //denied!!! + } + if(monster->Battle->hitby == monster->Battle->target) + { + //Log(MSG_DEBUG, "AIACT(015) returned false as monster is already in combat with this player %i",monster->Battle->target); + return AI_FAILURE; //no need to initiate combat. I am already fighting this player + } + } + // otherwise change target to the person who just hit me + monster->Stats->stance = 1; + monster->SetStats(); + monster->Battle->target = monster->Battle->hitby; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + { + //Log(MSG_DEBUG, "I seem to have lost my target so I can't attack any more"); + return AI_FAILURE; + } + //Log(MSG_DEBUG, "Starting normal attack against opponent %i",target->clientid); + monster->StartAction( target, NORMAL_ATTACK, 0 ); + return AI_SUCCESS; +} + +//Run away +AIACT(016) +{ + //dword iDistance; + //Run Away! + //Log(MSG_DEBUG, "AIACT(016)"); + GETAIACTDATA(016); + CMonster* monster = reinterpret_cast(entity); + monster->UpdatePosition( ); + + float radius = data->iDistance; + float angle = rand()%(360); + // float angle = rg.IRandom(0,360); + // entity->UpdatePosition(); + // entity->MoveTo(entity->basic.pos.x + (radius * cos(angle)), entity->basic.pos.y + (radius * sin(angle))); + float nX = (entity->Position->source.x + (radius * cos(angle))); + entity->Position->destiny.x=nX; + float nY = (entity->Position->source.y + (radius * cos(angle))); + entity->Position->destiny.y=nY; + + monster->Stats->stance = 1; + monster->SetStats(); + + entity->Position->lastMoveTime = clock(); + //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? + entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, entity->Position->destiny.x*100 ); + ADDFLOAT ( pak, entity->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, 0x01 ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} + +//Drop Item +AIACT(017) +{ + //word item0; Pos: 0x00 + //word item1; Pos: 0x02 + //word item2; Pos: 0x04 + //word item3; Pos: 0x06 + //word item4; Pos: 0x08 + //word item5; pos: 0x0a dummy as a spacer + //dword btToOwner; //Pos: 0x0c + //Random drop one of item 1-5 + GETAIACTDATA(017); + //srand(time(NULL)); + int itemRand = rand()%(5); + //int itemRand = rg.IRandom(0,360); + int nItem = data->items[itemRand]; + if (nItem == 0)return AI_FAILURE; + CMonster* monster = reinterpret_cast(entity); + //CItem dropItem; + CDrop* newdrop = new (nothrow) CDrop; + newdrop->clientid = GServer->GetNewClientID( ); + newdrop->posMap = entity->Position->Map; + newdrop->pos = GServer->RandInCircle( entity->Position->current, 3 ); + newdrop->droptime = time(NULL); + if(data->btToOwner == 1) + { + newdrop->owner = monster->MonsterDrop->firsthit; + newdrop->thisparty = monster->thisparty; + } + else + { + newdrop->owner = 0; + newdrop->thisparty = 0; + } + ClearItem(newdrop->item); + newdrop->amount = 1; + newdrop->type = 2; + newdrop->item.itemnum = nItem % 1000; + newdrop->item.itemtype = nItem / 1000; + if(newdrop->item.IsStackable()) + { + newdrop->item.count = 1; + } + else + { + newdrop->item.stats = 0; + newdrop->item.durability = 40; + newdrop->item.lifespan = 1000; + newdrop->item.socketed = 0; + newdrop->item.appraised = 1; + newdrop->item.refine = 0; + } + //Log(MSG_DEBUG, "item type= %i item num = %i", newdrop->item.itemtype, newdrop->item.itemnum); + newdrop->item.count = 1; + + + CMap* map = GServer->MapList.Index[newdrop->posMap]; + map->AddDrop( newdrop ); + // entity->DropEntityItem(&dropItem); + + return AI_SUCCESS; +} + +//Make nearby monsters attack my target +AIACT(018) +{ + //word cMonster; //Pos: 0x00 + //word wHowMany; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + //make wHowMany monsters of type cMonster within iDistance attack my target + // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + // if(target == NULL) return AI_FAILURE; + GETAIACTDATA(018); + CMonster* monster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + return AI_FAILURE; + int monstercount = 0; + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* othermon = map->MonsterList.at(j); + if(othermon == NULL) + continue; + if(othermon == monster) + continue; + if(othermon->montype != data->cMonster) + continue; + if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) + { + othermon->StartAction( target, NORMAL_ATTACK, 0); + monstercount++; + if(monstercount >= data->wHowMany) + return AI_SUCCESS; + } + } + // int chrCount = 0; + // dword eCount = 0; + // int nearestDistance = 9999999; + // int searchDistance = data->iDistance * 100; + + // CWorldEntity** entityList = entity->thisZone->GetEntityList(); + // dword entityCount = entity->thisZone->GetEntityCount(); + // for(dword i = 1; i < MAX_ZONE_CLIENTID; i++){ + // CWorldEntity* other = entityList[i]; + // if(eCount >= entityCount) break; + // if(other == NULL) continue; + // eCount++; + // if(other == entity) continue; + // if(other->_EntityType != ENTITY_MONSTER) continue; + // if(other->team != entity->team) continue; + // if(reinterpret_cast(other)->MonID != data->cMonster) continue; + + // int dX = (int)abs((int)other->curBlock.x - (int)entity->curBlock.x); + // if(dX > 1) continue; + // int dY = (int)abs((int)other->curBlock.y - (int)entity->curBlock.y); + // if(dY > 1) continue; + + // int iDistance = other->basic.pos.distance(entity->basic.pos); + // if(iDistance > searchDistance) continue; + // chrCount++; + + //Run and Attack target + // other->Attack(target); + + // if(chrCount >= data->wHowMany) return AI_SUCCESS; + // } + //Log(MSG_DEBUG, "AIACT(018)"); + return AI_SUCCESS; +} + +//Start attack +AIACT(019) +{ + //Run and attack "m_pNearCHAR" //Identical to 012 + return F_AI_ACT_012(server, entity, raw); +} + +//Spawn Monster (5) +AIACT(020) +{ + //word cMonster; //Pos: 0x00 + //byte btPos; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + + //Spawn cMonster within iDistance of btPos + + //btPos 0 = my pos + //btPos 1 = "m_pDestCHAR" pos + //btPos 2 = targets pos + GETAIACTDATA(020); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + if(monster == NULL) return AI_FAILURE; + + fPoint position; + + if(data->btPos == 0) + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + } + else if(data->btPos == 1) + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + //dest char + } + else if(data->btPos == 2) + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + position = GServer->RandInCircle( target->Position->current, data->iDistance ); + map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); + } + //Log(MSG_DEBUG, "AIACT(020)option spawn %i distance %i monstertype %i",data->btPos,data->iDistance,data->cMonster); + return AI_SUCCESS; +} + +//Unknown +AIACT(021) +{ + //Does absolutely nothing + return AI_SUCCESS; +} + +//Unknown +AIACT(022) +{ + //Does absolutely nothing + return AI_SUCCESS; +} + +//ghostseeds and ghosts and player summons commit suicide +AIACT(023) +{ + //Commit suicide + entity->Stats->HP = -1; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, entity->clientid ); + ADDDWORD ( pak, 0x8005 ); + GServer->SendToVisible( &pak, entity ); + //Log(MSG_DEBUG, "AIACT(023) suicide monstertype %i",thisMonster->montype); + //thisMonster->DoAi(thisMonster->thisnpc->AI, 5); + //now release the id in the monsterlist + + //map->DeleteMonster( thisMonster); + + // something weird going on here. Client ids not being relinquished. Deletemonster does not help. it crashes the server. + + return AI_SUCCESS; +} + +//Do Skill +AIACT(024) +{ + //byte btTarget; //Pos: 0x00 + //word nSkill; //Pos: 0x02 + //word nMotion; //Pos: 0x04 //8 = SKILL_ATTACK 6 = magic + + //Use nSkill & nMotion? + //btTarget 0 = m_pFindCHAR + //btTarget 1 = target + //btTarget 2 = self skill + GETAIACTDATA(024); + // Log(MSG_DEBUG, "Do Skill btTarget %i nSkill %i nMotion %i",data->btTarget,data->nSkill,data->nMotion); + // Log(MSG_DEBUG, "entity %i",entity->clientid); + // Log(MSG_DEBUG, "entity->findChar %i",entity->findChar->clientid); + // Log(MSG_DEBUG, "entity->nearChar %i", entity->nearChar->clientid); + CSkills* thisskill = GServer->GetSkillByID( data->nSkill ); + if(thisskill == NULL)return AI_FAILURE; + CCharacter* monster = entity; + //if(monster->Battle->atktype != 0) + //{ + // Log(MSG_DEBUG,"Cannot apply battle attacktype. Monster already has one"); + // return AI_FAILURE; + //} + //Log(MSG_DEBUG,"AIACT(024)skill attack id: %i target: %i type: %i btTarget: %i nMotion: %i", data->nSkill, thisskill->target, thisskill->skilltype, data->btTarget, data->nMotion); + switch (thisskill->skilltype) + { + case 3: //damage action + case 6: //magic attack + { + CCharacter* target = entity->GetCharTarget( ); + //Log(MSG_DEBUG,"SKILL_ATTACK or DAMAGE_ACTION selected"); + if(target==NULL) + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + //Log(MSG_DEBUG,"SKILL_ATTACK selected. target: %i",target->clientid); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + break; + case 7: //AOE magic attack + { + CCharacter* target = entity->GetCharTarget( ); + //Log(MSG_DEBUG,"AOE_TARGET selected"); + if(target == NULL) + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + //Log(MSG_DEBUG,"AOE_TARGET selected. target: %i",target->clientid); + monster->StartAction( (CCharacter*) target, AOE_TARGET, data->nSkill ); + } + break; + case 8: //self buff + { + //Log(MSG_DEBUG,"BUFF_SELF selected."); + monster->StartAction( NULL, BUFF_SELF, data->nSkill ); + } + break; + case 9: //skill buff + case 13://skill buff + case 11: //heal + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) //some skills always return a NULL target here + { + if(entity->nearChar == NULL) // maybe there really is no possible target. + { + //Log(MSG_DEBUG,"No target can be found for this skill"); + return AI_FAILURE; + } + else //so we are going to have them select a NEW target (if possible) rather than return AI_FAILURE + { + //Log(MSG_DEBUG,"SKILL_BUFF type 13 selected. NEW target: %i",entity->nearChar->clientid); + CCharacter* newtarget = entity->nearChar; + if(newtarget == NULL) + return AI_FAILURE; + monster->StartAction( (CCharacter*) newtarget, SKILL_BUFF, data->nSkill ); + } + } + else + { + //Log(MSG_DEBUG,"SKILL_BUFF type %i selected. target: %i",thisskill->skilltype, target->clientid); + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + break; + case 10: //heal AOE centered on self + //Log(MSG_DEBUG,"BUFF_AOE selected. Skill id %i", data->nSkill); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + break; + case 17: //skill AOE centered on self + { + //Log(MSG_DEBUG,"SKILL_AOE selected. Skill id %i", data->nSkill); + monster->StartAction( NULL, SKILL_AOE, data->nSkill ); + } + break; + default: + { + //Log(MSG_WARNING,"Skill type not recognized. type: %i",thisskill->skilltype); + } + break; + } + /* + if (data->btTarget == 0) + { + //CCharacter* target = entity->GetCharTarget(); + //if(target==NULL)return AI_FAILURE; + //CCharacter* monster = entity; //defined twice???? + Log(MSG_DEBUG,"BUFF_AOE selected"); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + } + else if (data->btTarget == 1) + { + CCharacter* target = entity->GetCharTarget( ); + if(target==NULL)return AI_FAILURE; + if(data->nMotion == 8) + { + Log(MSG_DEBUG,"SKILL_ATTACK selected"); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + else + { + Log(MSG_DEBUG,"SKILL_BUFF selected"); + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + else if (data->btTarget == 2) + { + if(thisskill->target == tYourself) + { + Log(MSG_DEBUG,"BUFF_SELF selected"); + monster->StartAction( entity, BUFF_SELF, data->nSkill ); + } + else if (thisskill->target == tAlly) + { + Log(MSG_DEBUG,"BUFF_AOE selected"); + monster->StartAction( NULL, BUFF_AOE, data->nSkill ); + } + else if (thisskill->target == tHostileCharacter) + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL)return AI_FAILURE; + if(data->nMotion == 8) + { + Log(MSG_DEBUG,"SKILL_ATTACK selected"); + monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); + } + else + { + monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); + } + } + }*/ + return AI_SUCCESS; +} + +//Set Variable (1) +AIACT(025) +{ + // if(entity->_EntityType != ENTITY_NPC) return AI_FAILURE; + //byte btVarIDX Pos: 0x00 + //dword iValue Pos: 0x04 + //byte btOp Pos: 0x08 + GETAIACTDATA(025); + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return AI_FAILURE; + if(data->btVarIDX > 19) return AI_FAILURE; + short tempval = GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX]; + switch(data->btOp) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + break; + case 9: + tempval++; + break; + default: + return AI_FAILURE; + break; + } + if(tempval < 0)tempval = 0; + GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX] = tempval; + if(data->btVarIDX == 0) //changing index 0 changes event variable at client too. Possible others too but for now just 0 + { + //Log(MSG_DEBUG,"AIACT(025) eventID has changed from %i to %i",monster->thisnpc->eventid,tempval); + monster->thisnpc->eventid = tempval; + } + return AI_SUCCESS; +} + +//Set Variable (2) +AIACT(026) +{ + //Set WorldVAR + //word nVarNo; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + GETAIACTDATA(026); + switch(data->nVarNo) + { + case 1: + GServer->Config.EXP_RATE = data->iValue; + return AI_SUCCESS; + break; + default: + return AI_SUCCESS; + break; + } +} + +//Set Variable (3) +AIACT(027) +{ + //Set EconomyVAR + //word nVarNo; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + return AI_SUCCESS; +} + +//Shout/Ann LTB String +AIACT(028) +{ + GETAIACTDATA(028); + //byte btMsgType Pos: 0x00 + //dword iStrID Pos: 0x04 + dword npcId = 0; + //if(entity->_EntityType == ENTITY_NPC) + CMonster* thisMonster = reinterpret_cast(entity); + switch(data->btMsgType) + { + case 0: //whisper to client + // don't think this is possible. No way to get a character to send it to?? + break; + case 1: //shout to map + //Log(MSG_INFO,"AI -- NPC Shouts to map %i",entity->Position->Map); + GServer->NPCShout(entity->Position->Map,GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); + break; + case 2: //announce to server. + GServer->NPCAnnounce(GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); + break; + default: + break; + } + + //npcId = reinterpret_cast(entity)->NpcID; + // }else if(entity->_EntityType == ENTITY_MONSTER){ + // npcId = reinterpret_cast(entity)->MonID; + // }else{ + // return AI_FAILURE; + // } + // std::map::iterator triggerITR = server->NpcNames.find(npcId); + // if(triggerITR == server->NpcNames.end()) return AI_FAILURE; + + // char* npcName = triggerITR->second; + // char* sayStr = server->lngAi->Data(data->iStrID, 1); + // if(sayStr == NULL) return AI_FAILURE; + // if(data->btMsgType == 0){ + //Chat + // CPacket pakout(0x783); + // pakout.Add(entity->_ClientID); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // }else if(data->btMsgType == 1){ + //Shout + // CPacket pakout(0x785); + // pakout.Add(npcName); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // }else if(data->btMsgType == 2){ + //Announce + // CPacket pakout(0x702); + // pakout.AddBytes((byte*)npcName, strlen(npcName)); + // pakout.Add('>'); + // pakout.Add(sayStr); + // server->SendPacketToZone(entity, &pakout); + // } + // delete [] sayStr; + //Log(MSG_DEBUG, "AIACT(028)"); + return AI_SUCCESS; +} + +//Move to my owner's location +AIACT(029) +{ + //Move within 20% of the distance of my "CALLER" + GETAIACTDATA(029); + // if(entity->_EntityType != ENTITY_MONSTER) return AI_FAILURE; + // CMonster* thisMonster = reinterpret_cast(entity); + // CWorldEntity* caller = thisMonster->thisZone->GetEntity(thisMonster->_CallerID); + // if(caller == NULL) return AI_FAILURE; + // caller->UpdatePosition(); + // thisMonster->UpdatePosition(); + if(!entity->IsMonster( )) return AI_FAILURE; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL) return AI_FAILURE; + caller->UpdatePosition(); + thisMonster->UpdatePosition(); + thisMonster->Stats->stance = 1; + thisMonster->SetStats(); + thisMonster->Position->source = caller->Position->current; + int iDist = GServer->RandNumber(5, 20); //move to between 5% and 20% of owners position + float xDist = thisMonster->Position->current.x - thisMonster->Position->source.x; + float yDist = thisMonster->Position->current.y - thisMonster->Position->source.y; + float nX = thisMonster->Position->source.x + (xDist * iDist / 100); + float nY = thisMonster->Position->source.y + (yDist * iDist / 100); + thisMonster->Position->destiny.x = nX; + thisMonster->Position->destiny.y = nY; + thisMonster->Position->lastMoveTime = clock(); + ClearBattle( thisMonster->Battle );// this also seems to clear the attackers battle?? + //entity->Battle->atktype = 0; + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, thisMonster->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, thisMonster->Position->destiny.x*100 ); + ADDFLOAT ( pak, thisMonster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, thisMonster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + + return AI_SUCCESS; +} + +//Do Trigger +AIACT(030) +{ + //word lenszTrigger; //Pos: 0x00 + //char* szTrigger; //Pos: 0x02 + GETAIACTDATA(030); + char* tempName = reinterpret_cast(&data->szTrigger) - 2; + dword hash = MakeStrHash(tempName); + //Log(MSG_INFO, "Execute Quest Trigger %s[%d] [%08x]", tempName, data->lenszTrigger, hash); + //make a dummy player from this monster + CClientSocket* thisclient = new CClientSocket; + CPlayer* player = new CPlayer( thisclient ); + if(player == NULL) + { + Log(MSG_INFO, "Player not found in AIACT 30"); + return AI_FAILURE; + } + thisclient->player = (void*) player; + player->Position = entity->Position; + + int quest = player->ExecuteQuestTrigger(hash); + if(quest == QUEST_SUCCESS) + return AI_SUCCESS; + return AI_FAILURE; +} + +//set monster's attack target to the same as the owner +AIACT(031) +{ + //Log(MSG_DEBUG, "AIACT(031)"); + //Attack my "CALLER"(s target) if he is not an ally (must be caller's target...) + if(!entity->IsMonster( )) return AI_FAILURE; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL) return AI_FAILURE; + thisMonster->Position->source = caller->Position->current; + CCharacter* target = map->GetCharInMap(caller->Battle->target); + if(target == NULL) return AI_FAILURE; + // need to prevent the summon from attacking other summons of the same player + CMonster* otherMonster = reinterpret_cast(target); + if(otherMonster->owner == thisMonster->owner) return AI_FAILURE; + thisMonster->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); + //Log(MSG_DEBUG, "AIACT(031) summon attacking player's target"); + + return AI_SUCCESS; +} + +//Zone (1) +AIACT(032) +{ + //word nZoneNo; //Pos: 0x00 + //byte btOnOff; //Pos: 0x02 + //Set PK Flag (btOnOff) in nZoneNo + GETAIACTDATA(032); + //Log(MSG_DEBUG, "AIACT(032) zone (1)"); + return AI_SUCCESS; +} + +//Zone (2) +AIACT(033) +{ + //word nZoneNo //if 0, current map + //word nNewValue + //if nNewValue == 2 -> toggle + //else set regen system nNewValue (0 = off, 1 = on) + GETAIACTDATA(033); + //Log(MSG_DEBUG, "AIACT(033) zone (2)"); + return AI_SUCCESS; +} + +//Item (?) +AIACT(034) +{ + //word nItemNum; //Pos: 0x00 + //word nCount; //Pos: 0x02 + //Give item to "CALLER" + GETAIACTDATA(034); + if(!entity->IsMonster( )) return AI_FAILURE; + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CPlayer* caller = map->GetPlayerInMap( thisMonster->owner); + if(caller == NULL) return AI_FAILURE; + int nItem = data->nItemNum; + if (nItem == 0)return AI_FAILURE; + char itemtypes[15] = {4,0,0,0,0,0,0,0,0,0,1,2,2,4,3}; + CItem thisitem; + thisitem.Clear(); + thisitem.itemtype = nItem / 1000; + thisitem.itemnum = nItem % 1000; + thisitem.count = data->nCount; + if( itemtypes[thisitem.itemtype] == 0 ) + { + thisitem.appraised = true; + thisitem.lifespan = 100; + thisitem.gem = 0; + thisitem.durability = GServer->STB_ITEM[thisitem.itemtype-1].rows[thisitem.itemnum][29]; + } + int thisslot = caller->AddItem( thisitem ); + //Log(MSG_DEBUG,"Item type = %i item ID = %i Count = %i Slot = %i",thisitem.itemtype,thisitem.itemnum,thisitem.count,thisslot); + if (thisslot == 0xffff) // Fail + { + BEGINPACKET( pak, 0x7a7); + ADDWORD(pak, 0x00); + ADDBYTE(pak, 0x03); + ADDBYTE(pak, 0x00); + caller->client->SendPacket(&pak); + return AI_FAILURE; + } + else // Success + { + BEGINPACKET( pak, 0x71f); + ADDBYTE(pak, 0x01); + ADDBYTE(pak, thisslot); + ADDWORD(pak, caller->items[thisslot].GetPakHeader( ) ); + ADDDWORD(pak, caller->items[thisslot].GetPakData( ) ); + caller->client->SendPacket(&pak); + } + //Log(MSG_DEBUG, "AIACT(034) Item (?)"); + return AI_SUCCESS; +} + +//Set Variable (4) +AIACT(035) +{ + //Set AiVAR + //word nVarIDX; Pos: 0x00 + //dword iValue; Pos: 0x04 + //byte btOp; Pos: 0x08 + GETAIACTDATA(035); + CMonster* thisMonster = reinterpret_cast(entity); + if(data->nVarIDX >19)return AI_FAILURE; + int tempval = thisMonster->AIVar[data->nVarIDX]; + switch(data->btOp) + { + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + if(tempval <= 0) + { + thisMonster->AIVar[data->nVarIDX] = 0; + return AI_FAILURE; + } + break; + case 9: + tempval++; + break; + default: + return AI_FAILURE; + break; + } + thisMonster->AIVar[data->nVarIDX] = tempval; + //thisMonster->thisnpc->Objvar->SetVar(data->btOp, data->iValue); //code not working + return AI_SUCCESS; +} + +//Monster (1) +AIACT(036) +{ + //word nMonster; //Pos: 0x00 + //byte btMaster; //Pos: 0x02 + //Spawn nMonster at my position (has btMaster)? + GETAIACTDATA(036); + // CWorldEntity* master = NULL; + // if(data->btMaster) master = entity; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + CMap* map = GServer->MapList.Index[entity->Position->Map]; + int owner = 0; + if(data->btMaster == 1) + owner = monster->clientid; + map->AddMonster( data->nMonster, entity->Position->current, 0, monster->Status->spawnid, 10 ); + //Log(MSG_DEBUG, "AIACT(36) monster (1) no code here"); + return AI_SUCCESS; +} + +//Monster (2) +AIACT(037) +{ + //word nMonster; //Pos: 0x00 + //word nPos; //Pos: 0x02 + //dword iDistance; //Pos: 0x04 + //byte btMaster; //Pos: 0x08 + + //Spawn nMonster at nPos within iDistance (has btMaster)? + //nPos = 0 my pos + //nPos = 1 m_pDestCHAR pos + //nPos = 2 target pos + GETAIACTDATA(037); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity + if(monster == NULL) return AI_FAILURE; + int owner = 0; + if(data->btMaster == 1) + owner = monster->clientid; + fPoint position; + + if(data->nPos == 0) //spawn monster in a circle with radius iDistance around my current position + { + position = GServer->RandInCircle( entity->Position->current, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + } + else if(data->nPos == 1) //spawn monster in a circle with radius iDistance around my destiny position + { + position = GServer->RandInCircle( entity->Position->destiny, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + //dest char + } + else if(data->nPos == 2) //spawn monster in a circle with radius iDistance around my target's current position + { + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + position = GServer->RandInCircle( target->Position->current, data->iDistance ); + map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); + } + return AI_SUCCESS; +} + +//TDEF code. move to next waypoint +AIACT(038) +{ + //byte cSpeed pos 0x00 + GETAIACTDATA(038); + CMonster* monster = reinterpret_cast(entity); + monster->NextWayPoint ++; + monster->Position->destiny.x = GServer->WPList[monster->Position->Map][monster->NextWayPoint].X; + monster->Position->destiny.y = GServer->WPList[monster->Position->Map][monster->NextWayPoint].Y; + monster->Stats->stance = data->cSpeed; + entity->Position->lastMoveTime = clock(); + BEGINPACKET( pak, 0x797 ); + ADDWORD ( pak, entity->clientid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, entity->Stats->Move_Speed ); //speed + ADDFLOAT ( pak, monster->Position->destiny.x*100 ); + ADDFLOAT ( pak, monster->Position->destiny.y*100 ); + ADDWORD ( pak, 0xcdcd ); + ADDBYTE ( pak, monster->Stats->stance ); + GServer->SendToVisible(&pak, entity); + return AI_SUCCESS; +} diff --git a/Src/World Server/aip/AiActions.h b/Src/World Server/aip/AiActions.h new file mode 100644 index 0000000..fde258a --- /dev/null +++ b/Src/World Server/aip/AiActions.h @@ -0,0 +1,247 @@ +// Props to ExJam for this code :D +#define AIACT(act) int F_AI_ACT_ ## act (class CWorldServer* server, class CCharacter* entity, byte* raw) +typedef int (*fpAiAct)(class CWorldServer*, class CCharacter*, byte*); +#define GETAIACTDATA(act) STR_AI_ACT_ ## act * data = (STR_AI_ACT_ ## act *)raw; + + +AIACT(000);AIACT(001);AIACT(002); +AIACT(003);AIACT(004);AIACT(005); +AIACT(006);AIACT(007);AIACT(008); +AIACT(009);AIACT(010);AIACT(011); +AIACT(012);AIACT(013);AIACT(014); +AIACT(015);AIACT(016);AIACT(017); +AIACT(018);AIACT(019);AIACT(020); +AIACT(021);AIACT(022);AIACT(023); +AIACT(024);AIACT(025);AIACT(026); +AIACT(027);AIACT(028);AIACT(029); +AIACT(030);AIACT(031);AIACT(032); +AIACT(033);AIACT(034);AIACT(035); +AIACT(036);AIACT(037);AIACT(038); + +#define AI_ACT_COUNT 38 + +struct STR_AI_ACT_000 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_001 { + //Do Action (0x04 bytes) + byte cAction; //Pos: 0x00 +}; + +struct STR_AI_ACT_002 { + //Say LTB String (0x04 bytes) + dword iStrID; //Pos: 0x00 +}; + +struct STR_AI_ACT_003 { + //Move (1) (0x08 bytes) + dword iDistance; //Pos: 0x00 + byte cSpeed; //Pos: 0x04 +}; + +struct STR_AI_ACT_004 { + //Move (2) (0x08 bytes) + dword iDistance; //Pos: 0x00 + byte cSpeed; //Pos: 0x04 +}; + +struct STR_AI_ACT_005 { + //Move (3) (0x04 bytes) + byte cSpeed; //Pos: 0x00 +}; + +struct STR_AI_ACT_006 { + //Move (?) (0x08 bytes) + dword iDistance; //Pos: 0x00 + byte cAbType; //Pos: 0x04 + byte cMoreLess; //Pos: 0x05 +}; + +struct STR_AI_ACT_007 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_008 { + //Move (4) (0x08 bytes) + dword iDistance; //Pos: 0x00 + byte cSpeed; //Pos: 0x04 +}; + +struct STR_AI_ACT_009 { + //Spawn Monster (1) (0x04 bytes) + word wMonster; //Pos: 0x00 +}; + +struct STR_AI_ACT_010 { + //Spawn Monster (2) (0x04 bytes) + word wMonster; //Pos: 0x00 +}; + +struct STR_AI_ACT_011 { + //Spawn Monster (3) (0x08 bytes) + int iDistance; //Pos: 0x00 + int iNumOfMonster; //Pos: 0x04 +}; + +struct STR_AI_ACT_012 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_013 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_014 { + //Unknown (0x04 bytes) + int iDistance; //Pos: 0x00 +}; + +struct STR_AI_ACT_015 { + //Retaliate ? (0x00 bytes) +}; + +struct STR_AI_ACT_016 { + //Unknown (0x04 bytes) + int iDistance; //Pos: 0x00 +}; + +struct STR_AI_ACT_017 { + //Drop Item (0x10 bytes) + word items[6]; + byte btToOwner; //Pos: 0x0c +}; + +struct STR_AI_ACT_018 { + //Spawn Monster (4) (0x08 bytes) + short cMonster; //Pos: 0x00 + short wHowMany; //Pos: 0x02 + int iDistance; //Pos: 0x04 +}; + +struct STR_AI_ACT_019 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_020 { + //Spawn Monster (5) (0x08 bytes) + word cMonster; //Pos: 0x00 + byte btPos; //Pos: 0x02 + int iDistance; //Pos: 0x04 +}; + +struct STR_AI_ACT_021 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_022 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_023 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_024 { + //Do Skill (0x08 bytes) + byte btTarget; //Pos: 0x00 + word nSkill; //Pos: 0x02 + word nMotion; //Pos: 0x04 +}; + +struct STR_AI_ACT_025 +{ + //Set Variable (1) (0x0c bytes) + union + { + byte btVarIDX; //Pos: 0x00 + char cVarIdx[4]; + }; + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_ACT_026 { + //Set Variable (2) (0x00 bytes) + union{word nVarNo; //Pos: 0x00 + char cVarNo[4];}; + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_ACT_027 { + //Set Variable (3) (0x00 bytes) + union{word nVarNo; //Pos: 0x00 + char cVarNo[4];}; + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_ACT_028 +{ + //Shout/Ann LTB String (0x08 bytes) + union + { + byte btMsgType; + int iMsgType; + }; + dword iStrID; //Pos: 0x04 +}; + +struct STR_AI_ACT_029 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_030 { + //Do Trigger (0x0d bytes) + word lenszTrigger; //Pos: 0x00 + char* szTrigger; //Pos: 0x02 +}; + +struct STR_AI_ACT_031 { + //Unknown (0x00 bytes) +}; + +struct STR_AI_ACT_032 { + //Zone (1) (0x04 bytes) + word nZoneNo; //Pos: 0x00 + byte btOnOff; //Pos: 0x02 +}; + +struct STR_AI_ACT_033 { + //Zone (2) (0x00 bytes) +}; + +struct STR_AI_ACT_034 { + //Item (?) (0x04 bytes) + word nItemNum; //Pos: 0x00 + word nCount; //Pos: 0x02 +}; + +struct STR_AI_ACT_035 { + //Set Variable (4) (0x0c bytes) + union{word nVarIDX; //Pos: 0x00 + char cVarIdx[4];}; + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_ACT_036 { + //Monster (1) (0x04 bytes) + word nMonster; //Pos: 0x00 + byte btMaster; //Pos: 0x02 +}; + +struct STR_AI_ACT_037 { + //Monster (2) (0x0c bytes) + word nMonster; //Pos: 0x00 + byte nPos; //Pos: 0x02 + dword iDistance; //Pos: 0x04 + byte btMaster; //Pos: 0x08 +}; + +struct STR_AI_ACT_038 +{ + //TDEF set destiny position + byte cSpeed; +}; diff --git a/Src/World Server/aip/AiConditions.cpp b/Src/World Server/aip/AiConditions.cpp new file mode 100644 index 0000000..55889c8 --- /dev/null +++ b/Src/World Server/aip/AiConditions.cpp @@ -0,0 +1,1078 @@ +// Props to ExJam for this code :D +#include "../WorldServer.h" + +//extern CRandomMersenne rg; + +int AI_GetAbility(CCharacter* entity, byte btAbType) +{ + switch(btAbType) + { + case 0: + return entity->Stats->Level; + break; + case 1: + return entity->Stats->Attack_Power; + break; + case 2: + return entity->Stats->Defense; + break; + case 3: + return entity->Stats->Magic_Defense; + break; + case 4: + return entity->Stats->HP; + break; + case 5: + return 0; + //Log(MSG_WARNING, "Don't have monsters charm yet..."); + //return -1; + break; + default: + //Log(MSG_ERROR, "Unknown cAbType [%d]", btAbType); + return -1; + break; + } +} + +//Hold or Attack +AICOND(000) +{ + //Always False + //Log(MSG_DEBUG, "AICOND(000) called"); + return AI_FAILURE; +} + +//(Damage > iDamage) == true iDamage cRecvOrGive +AICOND(001) +{ + //Log(MSG_DEBUG, "AICOND(001) called"); + //dword iDamage //Pos: 0x00 + //byte cRecvOrGive //Pos: 0x04 + GETAICONDDATA(001); + //Log(MSG_DEBUG, "AIcond(001) %i %i %i",data->iDamage,data->cRecvOrGive,entity->damagecounter); + if(data->cRecvOrGive == 0) + { + if(entity->damagecounter > data->iDamage) + { + entity->damagecounter = 0; + //Log(MSG_DEBUG, "AIcond(001) Success "); + return AI_SUCCESS; + } + } + else + { + //PY this is wrong. if data->cRecvOrGive is 1 then it should return FALSE + /*if(entity->damagecounter < data->iDamage) + { + entity->damagecounter = 0; + //Log(MSG_DEBUG, "AIcond(001) Success "); + return AI_SUCCESS; + }*/ + return AI_FAILURE; + } + return AI_FAILURE; + +} + +//Check Near (1) +AICOND(002) +{ + /* + dword iDistance; //Pos: 0x00 + byte btIsAllied; //Pos: 0x04 + word nLevelDiff; //Pos: 0x06 + word nLevelDiff2; //Pos: 0x08 + word wChrNum; //Pos: 0x0a + */ + //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance + //must be >= wChrNum + + ////Log(MSG_DEBUG, "AICOND(002) called"); + GETAICONDDATA(002); + dword chrCount = 0; + dword eCount = 0; + int nearestDistance = 9999999; + int searchDistance = data->iDistance; + CMap* map = GServer->MapList.Index[entity->Position->Map]; + ////Log(MSG_DEBUG, "Check Near (1)iDistance %i btIsAllied %i nLevelDiff %i nLevelDiff2 %i wChrNum %i", data->iDistance, data->btIsAllied, data->nLevelDiff, data->nLevelDiff2, data->wChrNum); + if(data->btIsAllied == 1) + { + dword entityCount = map->MonsterList.size(); + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + + int levelDiff = (entity->Stats->Level - other->Stats->Level); + if(levelDiff < data->nLevelDiff) continue; + if(levelDiff > data->nLevelDiff2) continue; + int iDistance = (int) GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + + chrCount++; + if(iDistance < nearestDistance) + { + nearestDistance = iDistance; + entity->nearChar = other; + } + if(chrCount >= data->wChrNum) + { + entity->findChar = other; + return AI_SUCCESS; + } + } + } + else + { + dword entityCount = map->PlayerList.size(); + ////Log(MSG_DEBUG, "entityCount %i", entityCount); + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* other = map->PlayerList.at(j); + ////Log(MSG_DEBUG, "player %i", other->clientid); + if(eCount >= entityCount) break; + ////Log(MSG_INFO, "1"); + if(other == NULL) continue; + ////Log(MSG_INFO, "2"); + eCount++; + + int levelDiff = (entity->Stats->Level - other->Stats->Level); + ////Log(MSG_DEBUG, "levelDiff %i < %i", levelDiff,data->nLevelDiff); + if(levelDiff < data->nLevelDiff) continue; + ////Log(MSG_INFO, "3"); + ////Log(MSG_DEBUG, "levelDiff %i > %i", levelDiff,data->nLevelDiff2); + if(levelDiff > data->nLevelDiff2) continue; + ////Log(MSG_INFO, "4"); + int iDistance = (int) GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + ////Log(MSG_INFO, "5"); + + chrCount++; + if(iDistance < nearestDistance) + { + nearestDistance = iDistance; + entity->nearChar = other; + ////Log(MSG_INFO, "6"); + } + if(chrCount >= data->wChrNum) + { + entity->findChar = other; + ////Log(MSG_INFO, "7"); + return AI_SUCCESS; + } + } + } + return AI_FAILURE; + + //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance + //must be >= wChrNum + //doesn't seem to work that way though + //it would seem that LevelDiff is a maximum level and LevelDiff2 is a minimum level. + //just subtract each from 65535 to get the level range. No difference involved at all. PY + //leveldiff2 often uses only the first byte so it is taken at face value. + + //Log(MSG_DEBUG, "AICOND(002) called"); + /* + GETAICONDDATA(002); + int maxlevel = 0; + int minlevel = 0; + CMonster* monster = reinterpret_cast(entity); + //Log(MSG_INFO,"Minlevel: %i Maxlevel: %i",data->nLevelDiff2,data->nLevelDiff); + + if(data->nLevelDiff2 > 255) + minlevel = 65535 - data->nLevelDiff2; + else + minlevel = data->nLevelDiff2; + if(data->nLevelDiff > 255) + maxlevel = 65535 - data->nLevelDiff; + else + maxlevel = data->nLevelDiff; + + if(minlevel > maxlevel) //Sometimes min and max are reversed. After all we just need to know if someting is between the two + { + int tmpmax = maxlevel; + int tmpmin = minlevel; + minlevel = tmpmax; + maxlevel = tmpmin; + } + //Log(MSG_INFO,"Minlevel: %i Maxlevel: %i",minlevel,maxlevel); + UINT chrCount = 0; + UINT eCount = 0; + int nearestDistance = 9999999; + int searchDistance = data->iDistance; + CMap* map = GServer->MapList.Index[entity->Position->Map]; + //Log(MSG_DEBUG, "COND(002) Check Near (1)iDistance %i btIsAllied %i level %i to %i wChrNum %i Monid %i", data->iDistance, data->btIsAllied, minlevel, maxlevel, data->wChrNum, monster->montype); + if(data->btIsAllied == 1) + { + dword entityCount = map->MonsterList.size(); + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + + if(other->Stats->Level > maxlevel) continue; + if(other->Stats->Level < minlevel) continue; + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + + chrCount++; + if(iDistance < nearestDistance) + { + nearestDistance = iDistance; + entity->nearChar = other; + } + if(chrCount >= data->wChrNum) + { + entity->findChar = other; + return AI_SUCCESS; + } + } + } + else + { + dword entityCount = map->PlayerList.size(); + //Log(MSG_DEBUG, "entityCount %i", entityCount); + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* other = map->PlayerList.at(j); + //Log(MSG_DEBUG, "player %i", other->clientid); + if(eCount >= entityCount) break; + //Log(MSG_INFO, "1"); + if(other == NULL) continue; + //Log(MSG_INFO, "2"); + eCount++; + + if(other->Stats->Level > maxlevel) continue; + if(other->Stats->Level < minlevel) continue; + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + //Log(MSG_INFO, "5"); + + chrCount++; + if(iDistance < nearestDistance) + { + nearestDistance = iDistance; + entity->nearChar = other; + //Log(MSG_INFO, "6"); + } + if(chrCount >= data->wChrNum) + { + entity->findChar = other; + //Log(MSG_INFO, "7"); + return AI_SUCCESS; + } + } + } + return AI_FAILURE;*/ +} + +//Check Distance (1) +AICOND(003) +{ + //dword iDistance; //Pos: 0x00 + //CObjCHAR::Get_MoveDISTANCE > iDistance == true + //distance from spawn point? + //Log(MSG_DEBUG, "AICOND(003) called"); + GETAICONDDATA(003); + + entity->UpdatePosition(); + int moveDistance = (int)GServer->distance( entity->Position->current, entity->Position->source ); + if(moveDistance > (data->iDistance)) + { + return AI_SUCCESS; + } + return AI_FAILURE; +} + +//Check Distance (2) +AICOND(004) +{ + //dword iDistance; //Pos: 0x00 + //byte cMoreLess; //Pos: 0x04 + //distance to target (cMoreLess == 0 then >= else <=) iDistance + //Log(MSG_DEBUG, "AICOND(004) called"); + GETAICONDDATA(004); + //Log(MSG_DEBUG, "AICOND(004) retrieved data"); + CCharacter* target = entity->GetCharTarget( ); + //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + if(target == NULL) + { + //Log(MSG_DEBUG, "AICOND(004) found no valid target"); + return AI_FAILURE; + } + target->UpdatePosition(); + entity->UpdatePosition(); + int distance = (int)GServer->distance( entity->Position->current, target->Position->current ); + //int distance = entity->basic.pos.distance(target->basic.pos); + if(data->cMoreLess == 0) + { + if(distance >= data->iDistance) + { + //Log(MSG_DEBUG, "Check Distance (2) This distance: %i > %i", distance, data->iDistance); + return AI_SUCCESS; + } + } + else + { + if(distance <= data->iDistance) + { + //Log(MSG_DEBUG, "Check Distance (2) This distance: %i < %i", distance, data->iDistance); + return AI_SUCCESS; + } + } + return AI_FAILURE; +} + +//Check Ability of my Target +AICOND(005) +{ + /* + byte cAbType; //Pos: 0x00 + dword iDiff; //Pos: 0x04 + byte cMoreLess; //Pos: 0x08 + */ + //Log(MSG_DEBUG, "AICOND(005) called"); + GETAICONDDATA(005); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + { + //Log(MSG_DEBUG, "AICOND(004) found no valid target"); + return AI_FAILURE; + } + //cAbType + //0 = Level + //1 = Attack + //2 = Defense + //3 = "Res"? Magic Resistance? + //4 = HP + //5 = Charm + + //Check if "ability" (cMoreLess == 0 then >= else <=) iDiff + //Log(MSG_DEBUG, "Check AB (1)"); + int value = AI_GetAbility(target, data->cAbType); //PY This should be checking my target's ability NOT MY OWN. Corrected now + //Log(MSG_DEBUG, "Check AB (1) type %i diff %i", data->cAbType, data->iDiff); + if(value < 0) return AI_FAILURE; + if(data->cMoreLess == 0) + { + if(value > data->iDiff) + { + //Log(MSG_DEBUG, "Check AB (1) > %i", data->iDiff); + return AI_SUCCESS; + } + } + else + { + if(value < data->iDiff) + { + //Log(MSG_DEBUG, "Check AB (1) < %i", data->iDiff); + return AI_SUCCESS; + } + } + return AI_FAILURE; +} + +//Check HP +AICOND(006) +{ + //dword wHP; //Pos: 0x00 + //byte cMoreLess; //Pos: 0x04 + //Check if hp% (cMoreLess == 0 then >= else <=) wHP + //Log(MSG_DEBUG, "AICOND(000) called"); + GETAICONDDATA(006); + dword value = (dword)(((float)entity->Stats->HP/ (float)entity->Stats->MaxHP) * 100.0f); + //Log(MSG_DEBUG, "Check HP %i %i", data->cMoreLess, value); + if(data->cMoreLess == 0) + { + if(value >= data->wHP) return AI_SUCCESS; + } + else + { + if(value <= data->wHP) return AI_SUCCESS; + } + return AI_FAILURE; +} + +//Random Chance +AICOND(007) +{ + //byte cPercent; //Pos: 0x00 + //Random number 0->100 if random number < cPercent then return true + //Log(MSG_DEBUG, "AICOND(007) called"); + GETAICONDDATA(007); + //byte rand = rg.IRandom(0, 100) & 0xFF; + //srand(time(NULL)); + byte brand = rand()%100; + //Log(MSG_DEBUG, "brand = %i chance = %i", brand, data->cPercent); + if(brand <= data->cPercent) return AI_SUCCESS; + return AI_FAILURE; +} + +//Check Near (2) +AICOND(008) +{ + //dword iDistance; //Pos: 0x00 + //word nLevelDiff; //Pos: 0x04 + //word nLevelDiff2; //Pos: 0x06 + //byte btIsAllied; //Pos: 0x08 + + //find atleast 1 aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance + + //Log(MSG_DEBUG, "AICOND(008) called"); + GETAICONDDATA(008); + + dword eCount = 0; + int searchDistance = data->iDistance;// * 10; + CMap* map = GServer->MapList.Index[entity->Position->Map]; + + + if(data->btIsAllied == 1) + { + dword entityCount = map->MonsterList.size(); + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + if(other->CharType != TMONSTER) continue; + + int levelDiff = abs(entity->Stats->Level - other->Stats->Level); + if(levelDiff < data->nLevelDiff) continue; + if(levelDiff > data->nLevelDiff2) continue; + + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + + entity->findChar = other; + entity->nearChar = other; + return AI_SUCCESS; + } + } + else + { + dword entityCount = map->PlayerList.size(); + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* other = map->PlayerList.at(j); + + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + if(other->CharType != TPLAYER) continue; + + int levelDiff = abs(entity->Stats->Level - other->Stats->Level); + if(levelDiff < data->nLevelDiff) continue; + if(levelDiff > data->nLevelDiff2) continue; + + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + + entity->findChar = other; + entity->nearChar = other; + return AI_SUCCESS; + } + } + return AI_FAILURE; +} + +//check target +//PY This is actually incorrect. Should be comparing my target with my destination character. TRUE if they are different +//To be corrected!!! +AICOND(009) +{ + //Do i have a target? + //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + //if(target == NULL){entity->_TargetID = 0; return AI_FAILURE;} + //return AI_SUCCESS; + + //TO BE RECODED + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + return AI_SUCCESS; +} + +//Check AB (2) +AICOND(010) +{ + //byte cAbType; //Pos: 0x00 + //byte cMoreLess; //Pos: 0x01 + + //Is my ability >= or <= than my targets + //PY This is wrong too. Should be comparing ability of my target against the one that just hit me + //NOT against MY ability + + //TO BE RECODED + GETAICONDDATA(010); + //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) return AI_FAILURE; + //Log(MSG_DEBUG, "Check AB (2) type %i ", data->cAbType); + int myValue = AI_GetAbility(entity, data->cAbType); + if(myValue < 0) return AI_FAILURE; + + int targetValue = AI_GetAbility(target, data->cAbType); + if(targetValue < 0) return AI_FAILURE; + + if(data->cMoreLess == 0) + { + if(myValue > targetValue) return AI_SUCCESS; + } + else + { + if(myValue < targetValue) return AI_SUCCESS; + } + return AI_FAILURE; +} + +//Check AB (3) +//PY Again this has to be my Attacker's ability. NOT my own or even my Target's +AICOND(011) +{ + //byte cAbType; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte cMoreLess; //Pos: 0x08 + //check ability of "m_pDestCHAR" <= or >= iValue + GETAICONDDATA(011); + dword tValue = data->iValue; + //if(tValue == 0)tValue = AI_GetAbility(entity, data->cAbType); + CCharacter* target = entity->GetCharTarget( ); + if(target == NULL) + { + //Log(MSG_DEBUG, "AICOND(004) found no valid target"); + return AI_FAILURE; + } + int myValue = AI_GetAbility(target, data->cAbType); + //CCharacter* target = entity->GetCharTarget( ); + //if(target == NULL) + //{ + // Log(MSG_DEBUG,"AICOND(011) could not find a valid target"); + // return AI_FAILURE; + //} + //myValue = AI_GetAbility(target, data->cAbType); + //Log(MSG_DEBUG, "AICOND(011) myValue: %i <> iValue: %i moreless: %i type: %i", myValue, data->iValue, data->cMoreLess, data->cAbType); + if(data->cMoreLess == 0) //0 = more + { + if(myValue >= data->iValue) + { + //Log(MSG_DEBUG, "AICOND(011) myValue: %i <= tValue: %i ",myValue,tValue); + return AI_SUCCESS; + } + } + else // 1 = less + { + if(myValue <= data->iValue) + { + //Log(MSG_DEBUG, "AICOND(011) myValue: %i <= tValue: %i ",myValue,tValue); + return AI_SUCCESS; + } + } + return AI_FAILURE; +} + +//Check Time (1) +AICOND(012) +{ + //byte cWhen; //Pos: 0x00 + //Is it night or day? cWhen: ?1 = night 0 = day? + GETAICONDDATA(012); + CMap* map = GServer->MapList.Index[entity->Position->Map]; + if (data->cWhen == 1) + { + if (map->CurrentTime > 2) // night + return AI_SUCCESS; + } + else + { + if (map->CurrentTime < 3) // day + return AI_SUCCESS; + } + return AI_FAILURE; +} + +//Check Target for buffs(1) +AICOND(013) +{ + //byte btCheckTarget; //Pos: 0x00 //0 = self 1 = target + //byte btStatusType; //Pos: 0x01 // + //byte btHave; //Pos: 0x02 + + //Does my target btHave MagicSTATUS (btStatusType) + //btCheckTarget does Get_MagicSTATUS but uses differ AND masks + //btCheckTarget = 0 AND EAX,1EAAA84 + //btCheckTarget = 1 AND EAX,1E155570 + //btCheckTarget = 2 normal + CCharacter* target = NULL; + UINT buffs=0; + GETAICONDDATA(013); + if(data->btCheckTarget == 1) //check enemy + { + target = entity->GetCharTarget( ); + if(target == NULL)return AI_FAILURE; + } + else if(data->btCheckTarget == 0) //check self + { + target = entity; + } + else //should never be anything other than 0 or 1 + { + //Log(MSG_DEBUG, "AICOND(013) Check Target (1) btCheckTarget %i ", data->btCheckTarget); + return AI_FAILURE; + } + if (target == NULL) //target not found + { + //Log(MSG_DEBUG, "AICOND(013) Check Target (1) not found btCheckTarget %i ", data->btCheckTarget); + return AI_FAILURE; + } + + switch(data->btStatusType) + { + case 0: //debuff + buffs = GServer->BuildDeBuffs(target); + break; + case 1: //buff + buffs = GServer->BuildUpBuffs(target); + break; + case 2: //effect such as burning + //not sure how to handle this so i will come back to it. PY + break; + } + //Log(MSG_DEBUG, "Target: %i btStutusType: %i buffs: %i btHave: %i", target->clientid, data->btStatusType, buffs, data->btHave); + if (data->btHave == 1) //True if HAS buffs + { + if(buffs != 0)return AI_SUCCESS; + return AI_FAILURE; + } + else // true if NOT HAS buffs + { + if(buffs != 0)return AI_FAILURE; + return AI_SUCCESS; + } + //CCharacter* target = entity->GetCharTarget( ); + //Log(MSG_DEBUG, "Check Target (1) shouldn't be able to get this far"); + return AI_SUCCESS; + //return AI_FAILURE; +} + +//Check Variable (1) (ObjVar) +AICOND(014) +{ + GETAICONDDATA(014); + //Log(MSG_DEBUG,"Checking Object Variable using NPC ref number AICOND(015)"); + //Log(MSG_DEBUG,"btVarIDX = %i iValue = %i btOp = %i",data->btVarIDX, data->iValue,data->btOp); + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return AI_FAILURE; + if(data->btVarIDX > 19) return AI_FAILURE; + int refNPC = monster->thisnpc->refNPC; + int ObjvarIndex = data->btVarIDX; + + int tempval = GServer->ObjVar[refNPC][ObjvarIndex]; + //Log(MSG_DEBUG,"ObjVar value [%i][%i] = %i", refNPC,ObjvarIndex,tempval); + switch(data->btOp) + { + case 0: + if(tempval == data->iValue) + return AI_SUCCESS; + break; + case 1: + if(tempval > data->iValue) + return AI_SUCCESS; + break; + case 2: + if(tempval >= data->iValue) + return AI_SUCCESS; + break; + case 3: + if(tempval < data->iValue) + return AI_SUCCESS; + break; + case 4: + if(tempval <= data->iValue) + return AI_SUCCESS; + break; + case 10: + if(tempval != data->iValue) + return AI_SUCCESS; + break; + default: + return AI_FAILURE; + break; + } + + + //if(entity->_EntityType != ENTITY_NPC) return AI_FAILURE; + //CNpc* thisNpc = reinterpret_cast(entity); + //thisNpc = thisNpc->SelectedNpc; + //if(thisNpc == NULL) return AI_FAILURE; + //if(OperateValues(data->btOp, &VarValue, data->iValue & 0xFFFF)) return AI_SUCCESS; + return AI_FAILURE; +} + +//Check Variable (2) (WorldVAR) //PY should work just like NPC ObjVar. To Be Coded later +AICOND(015) +{ + //word nVarIDX; //Pos: 0x00 + //byte btOp; //Pos: 0x08 + //dword iValue; //Pos: 0x04 + + //Check WorldVAR + return AI_FAILURE; +} + +//Check Variable (3) (EconomyVar) +AICOND(016) +{ + //word nVarIDX; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + + //Check EconomyVar + return AI_FAILURE; +} + +//Select ObjVar reference +AICOND(017) +{ + GETAICONDDATA(017); + //Log(MSG_DEBUG,"Setting NPC ref number AICOND(017)"); + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return AI_FAILURE; + monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar + //Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC); + return AI_SUCCESS; +} + +//Check Distance (3) +AICOND(018) +{ + //dword iDistance; //Pos: 0x00 + //byte btOp; //Pos: 0x04 + + //Distance to "CALLER" ??Possibly the one who summoned the monster?? + GETAICONDDATA(018); + if(!entity->IsMonster( )) return AI_FAILURE; + //Log(MSG_DEBUG, "AICOND(018 1)"); + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL ) return AI_FAILURE; // I think this is wrong. It should return true if there is no owner. Otherwise orphaned bonfires will live forever + if(thisMonster->owner == 0) return AI_FAILURE; //Monster has no owner + //Log(MSG_DEBUG, "AICOND(018 2)"); + //if(entity->_EntityType != ENTITY_MONSTER) return AI_FAILURE; + //CMonster* thisMonster = reinterpret_cast(entity); + //CWorldEntity* caller = thisMonster->thisZone->GetEntity(thisMonster->_CallerID); + //if(caller == NULL) return AI_FAILURE; + caller->UpdatePosition(); + thisMonster->UpdatePosition(); + int distance = (int)GServer->distance( caller->Position->current, thisMonster->Position->current ); + //int distance = thisMonster->basic.pos.distance(caller->basic.pos); + //Log(MSG_DEBUG, "AICOND(018 3)"); + if(OperateValues(data->btOp, &distance, data->iDistance)) return AI_SUCCESS; + //Log(MSG_DEBUG, "AICOND(018 4)"); + return AI_FAILURE; +} + +//Check Time (2) +AICOND(019) +{ + //dword ulTime; //Pos: 0x00 + //dword ulEndTime; //Pos: 0x04 + + //Get_ZoneTIME between ulTime and ulEndTime + GETAICONDDATA(019); + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + int StartTime = int(data->ulTime); + int EndTime = int(data->ulEndTime); + if(map->ZoneTime >= StartTime && map->ZoneTime <= EndTime) + return AI_SUCCESS; + return AI_FAILURE; +} + +//Check My AB (4) NOT target's +AICOND(020) +{ + //byte btAbType; //Pos: 0x00 + //dword iValue; //Pos: 0x04 + //byte btOp; //Pos: 0x08 + + + //cAbType + //0 = Level + //1 = Attack + //2 = Defense + //3 = "Res"? Magic Resistance? + //4 = HP + //5 = Charm + GETAICONDDATA(020); + //Log(MSG_DEBUG, "Check AB (4) type %i iValue %i", data->btAbType, data->iValue); + int value = AI_GetAbility(entity, data->btAbType); + if(value < 0) return AI_FAILURE; + if(OperateValues(data->btOp, &value, data->iValue)) return AI_SUCCESS; + //Log(MSG_INFO,"check ab (4) successfull"); + return AI_FAILURE; +} + +//Do I have a caller +//PY. Needs to return AI_SUCCESS if I have no owner +AICOND(021) +{ + //Do i have a "CALLER" ??Possibly the one who summoned the monster?? + // more like Am I an orphan? + GETAICONDDATA(021); + + if(!entity->IsMonster())return AI_SUCCESS; + CMonster* thisMonster = reinterpret_cast(entity); + if(thisMonster->owner == 0) return AI_SUCCESS; + //Log(MSG_DEBUG, "AICOND(021) Yes I Have no owner"); + return AI_FAILURE; +} + +//Does my caller have a target +AICOND(022) +{ + //Does my "CALLER" have a target? + //Log(MSG_DEBUG, "AICOND(022) Does my `CALLER` have a target?"); + GETAICONDDATA(022); + + if(!entity->IsMonster( )) return AI_FAILURE; + //Log(MSG_DEBUG, "AICOND(022 1)"); + CMonster* thisMonster = reinterpret_cast(entity); + CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; + CCharacter* caller = map->GetCharInMap( thisMonster->owner ); + if(caller == NULL) return AI_FAILURE; + //Log(MSG_DEBUG, "AICOND(022 2)"); + CCharacter* target = map->GetCharInMap(caller->Battle->target); + if(target == entity) return AI_FAILURE; + if(target != NULL) return AI_SUCCESS; + //Log(MSG_DEBUG, "AICOND(022 3)"); + return AI_FAILURE; +} + +//Check Time (3) (World time?) +AICOND(023) +{ + //dword ulTime; //Pos: 0x00 + //dword ulEndTime; //Pos: 0x04 + + //Get_WorldTIME between ulTime and ulEndTime + return AI_FAILURE; +} + +//Check Date Time (4) +AICOND(024) +{ + //Log(MSG_INFO,"AIP Cdt 024 for aipId %i",AipId); + GETAICONDDATA(024); + //byte btDate Pos: 0x00 + //byte btHour1 Pos: 0x01 + //byte btMin1 Pos: 0x02 + //byte btHour2 Pos: 0x03 + //byte btMin2 Pos: 0x04 + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + + if(data->btDate != 0) + { + if(sTIME.wDay != data->btDate) + return AI_FAILURE; + } + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin >= wFrom && wMin <= wTo) + return AI_SUCCESS; + return AI_FAILURE; +} + +//Check Weekday Time (5) +AICOND(025) +{ + GETAICONDDATA(025); + //byte btWweekDay Pos: 0x00 + //byte btHour1 Pos: 0x01 + //byte btMin1 Pos: 0x02 + //byte btHour2 Pos: 0x03 + //byte btMin2 Pos: 0x04 + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + + if(data->btWeekDay != sTIME.wDayOfWeek) + return AI_FAILURE; + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin > wFrom && wMin < wTo) + return AI_SUCCESS; + return AI_FAILURE; +} + +//Check Server/Channel +AICOND(026) +{ + GETAICONDDATA(026); + //channel >= min && channel <= max + //return AI_SUCCESS; + return AI_FAILURE; +} + +//Check Near Character +AICOND(027 +){ + //dword iDistance; //Pos: 0x00 + //byte btIsAllied; //Pos: 0x04 + //word nLevelDiff; //Pos: 0x06 + //word nLevelDiff2; //Pos: 0x08 + //word wChrNum; //Pos: 0x0a + //byte btOp; //Pos: 0x0c + + //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance + //must be (btOp) wChrNum + GETAICONDDATA(027); + + short chrCount = 0; + dword eCount = 0; + int nearestDistance = 9999999; + int searchDistance = data->iDistance;// * 10; + + CMap* map = GServer->MapList.Index[entity->Position->Map]; + dword entityCount = map->MonsterList.size(); + + if(data->btIsAllied == 1) + { + for(UINT j=0;jMonsterList.size();j++) + { + CMonster* other = map->MonsterList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + int levelDiff = (entity->Stats->Level - other->Stats->Level); + if(levelDiff < data->nLevelDiff) continue; + if(levelDiff > data->nLevelDiff2) continue; + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + chrCount++; + if(iDistance < nearestDistance) + { + nearestDistance = iDistance; + entity->nearChar = other; + } + } + entity->findChar = entity->nearChar; + if(OperateValues(data->btOp, &chrCount, data->wChrNum)) return AI_SUCCESS; + } + else + { + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* other = map->PlayerList.at(j); + if(eCount >= entityCount) break; + if(other == NULL) continue; + eCount++; + if(other == entity) continue; + int levelDiff = (entity->Stats->Level - other->Stats->Level); + if(levelDiff < data->nLevelDiff) continue; + if(levelDiff > data->nLevelDiff2) continue; + int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); + if(iDistance > searchDistance) continue; + chrCount++; + if(iDistance < nearestDistance){ + nearestDistance = iDistance; + entity->nearChar = other; + } + } + entity->findChar = entity->nearChar; + if(OperateValues(data->btOp, &chrCount, data->wChrNum)) return AI_SUCCESS; + } + + ////Log(MSG_INFO,"AICOND(027) failure"); + return AI_FAILURE; +} + +//Check Variable (4) +AICOND(028) +{ + //word nVarIDX; //Pos: 0x00 + //byte btOp; //Pos: 0x08 + //dword iValue; //Pos: 0x04 + + //Get_AiVAR + GETAICONDDATA(028); + CMonster* thisMonster = reinterpret_cast(entity); + if(data->nVarIDX >19)return AI_FAILURE; + UINT tempval = thisMonster->AIVar[data->nVarIDX]; + switch(data->btOp) + { + case 0: + if(tempval == data->iValue) + return AI_SUCCESS; + break; + case 1: + if(tempval > data->iValue) + return AI_SUCCESS; + break; + case 2: + if(tempval >= data->iValue) + return AI_SUCCESS; + break; + case 3: + if(tempval < data->iValue) + return AI_SUCCESS; + break; + case 4: + if(tempval <= data->iValue) + return AI_SUCCESS; + break; + case 10: + if(tempval != data->iValue) + return AI_SUCCESS; + break; + default: + return AI_FAILURE; + break; + } + return AI_FAILURE; +} + +//Check Target (2) +AICOND(029) +{ + //byte btTargetType; //Pos: 0x00 + //UNKNOWN :@ + GETAICONDDATA(029); + //Log(MSG_DEBUG,"aicon 029 check target type %i",data->btTargetType); + return AI_FAILURE; +} + +//Timer since spawn (seconds). +AICOND(030) +{ + GETAICONDDATA(030); + //dword Timer; //Pos: 0x00 timer. + //Timer is stored in seconds so we need to multiply it by 1000 + CMonster* thisMonster = reinterpret_cast(entity); + clock_t etime = clock() - thisMonster->SpawnTime; + if(etime > (data->Timer * 1000)) + return AI_SUCCESS; + return AI_FAILURE; +} + +//from here on this is 100% server sided. AIP scripts with these elements in should not be uploaded to the client + +// TDEF code. Check current position against Destination +AICOND(031) +{ + + return AI_FAILURE; +} + + diff --git a/Src/World Server/aip/AiConditions.h b/Src/World Server/aip/AiConditions.h new file mode 100644 index 0000000..7086469 --- /dev/null +++ b/Src/World Server/aip/AiConditions.h @@ -0,0 +1,266 @@ +// Props to ExJam for this code :D +#define AICOND(cond) int F_AI_COND_ ## cond (class CWorldServer* server, class CCharacter* entity, byte* raw) +typedef int (*fpAiCond)(class CWorldServer*, class CCharacter*, byte*); +#define GETAICONDDATA(cond) STR_AI_COND_ ## cond * data = (STR_AI_COND_ ## cond *)raw; + +AICOND(000);AICOND(001);AICOND(002); +AICOND(003);AICOND(004);AICOND(005); +AICOND(006);AICOND(007);AICOND(008); +AICOND(009);AICOND(010);AICOND(011); +AICOND(012);AICOND(013);AICOND(014); +AICOND(015);AICOND(016);AICOND(017); +AICOND(018);AICOND(019);AICOND(020); +AICOND(021);AICOND(022);AICOND(023); +AICOND(024);AICOND(025);AICOND(026); +AICOND(027);AICOND(028);AICOND(029); +AICOND(030);AICOND(031);AICOND(032); + +#define AI_COND_COUNT 33 + +struct STR_AI_COND_000 +{ + //Hold or Attack (0x04 bytes) + byte cNotfightOrDelay; //Pos: 0x00 +}; + +struct STR_AI_COND_001 +{ + //Damage (0x08 bytes) + dword iDamage; //Pos: 0x00 + byte cRecvOrGive; //Pos: 0x04 +}; + +struct STR_AI_COND_002 +{ + //Check Near (1) (0x0c bytes) + int iDistance; //Pos: 0x00 + union + { + byte btIsAllied; //Pos: 0x04 + word nIsAllied; + }; + short nLevelDiff; //Pos: 0x06 + short nLevelDiff2; //Pos: 0x08 + word wChrNum; //Pos: 0x0a +}; + +struct STR_AI_COND_003 +{ + //Check Distance (1) (0x04 bytes) + int iDistance; //Pos: 0x00 +}; + +struct STR_AI_COND_004 +{ + //Check Distance (2) (0x08 bytes) + int iDistance; //Pos: 0x00 + byte cMoreLess; //Pos: 0x04 +}; + +struct STR_AI_COND_005 +{ + //Check AB (1) (0x0c bytes) + byte cAbType; //Pos: 0x00 + int iDiff; //Pos: 0x04 + byte cMoreLess; //Pos: 0x08 +}; + +struct STR_AI_COND_006 +{ + //Check HP (0x08 bytes) + dword wHP; //Pos: 0x00 + byte cMoreLess; //Pos: 0x04 +}; + +struct STR_AI_COND_007 +{ + //Random Chance (0x04 bytes) + byte cPercent; //Pos: 0x00 +}; + +struct STR_AI_COND_008 +{ + //Check Near (2) (0x0c bytes) + dword iDistance; //Pos: 0x00 + short nLevelDiff; //Pos: 0x04 + short nLevelDiff2; //Pos: 0x06 + byte btIsAllied; //Pos: 0x08 +}; + +struct STR_AI_COND_009 +{ + //Unknown (0x00 bytes) +}; + +struct STR_AI_COND_010 +{ + //Check AB (2) (0x04 bytes) + byte cAbType; //Pos: 0x00 + byte cMoreLess; //Pos: 0x01 +}; + +struct STR_AI_COND_011 { + //Check AB (3) (0x0c bytes) + byte cAbType; //Pos: 0x00 + word tBuffer; //Pos: 0x02 + dword iValue; //Pos: 0x04 + byte cMoreLess; //Pos: 0x08 +}; + +struct STR_AI_COND_012 +{ + //Check Time (1) (0x04 bytes) + byte cWhen; //Pos: 0x00 +}; + +struct STR_AI_COND_013 +{ + //Check Target (1) (0x04 bytes) + byte btCheckTarget; //Pos: 0x00 + byte btStatusType; //Pos: 0x01 + byte btHave; //Pos: 0x02 +}; + +struct STR_AI_COND_014 +{ + //Check Variable (1) (0x0c bytes) + byte btVarIDX; //Pos: 0x00 + word tBuffer; //Pos: 0x02 + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_COND_015 +{ + //Check Variable (2) (0x0c bytes) + word nVarIDX; //Pos: 0x00 + word tBuffer; //Pos: 0x02 + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_COND_016 +{ + //Check Variable (3) (0x00 bytes) + word nVarIDX; //Pos: 0x00 + word tBuffer; //Pos: 0x02 + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_COND_017 +{ + //Select NPC (0x04 bytes) + dword iNpcNo; //Pos: 0x00 +}; + +struct STR_AI_COND_018 +{ + //Check Distance (3) (0x08 bytes) + dword iDistance; //Pos: 0x00 + byte btOp; //Pos: 0x04 +}; + +struct STR_AI_COND_019 +{ + //Check Time (2) (0x08 bytes) + dword ulTime; //Pos: 0x00 + dword ulEndTime; //Pos: 0x04 +}; + +struct STR_AI_COND_020 +{ + //Check AB (4) (0x0c bytes) + union + { + byte btAbType; //Pos: 0x00 + dword iAbType; + }; + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_COND_021 +{ + //Unknown (0x00 bytes) +}; + +struct STR_AI_COND_022 +{ + //Unknown (0x00 bytes) +}; + +struct STR_AI_COND_023 +{ + //Check Time (3) (0x08 bytes) + dword ulTime; //Pos: 0x00 + dword ulEndTime; //Pos: 0x04 +}; + +struct STR_AI_COND_024 +{ + //Check Date Time (4) (0x08 bytes) + byte btDate; //Pos: 0x00 + byte btHour1; //Pos: 0x01 + byte btMin1; //Pos: 0x02 + byte btHour2; //Pos: 0x03 + byte btMin2; //Pos: 0x04 +}; + +struct STR_AI_COND_025 +{ + //Check Weekday Time (5) (0x08 bytes) + byte btWeekDay; //Pos: 0x00 + byte btHour1; //Pos: 0x01 + byte btMin1; //Pos: 0x02 + byte btHour2; //Pos: 0x03 + byte btMin2; //Pos: 0x04 +}; + +struct STR_AI_COND_026 +{ + //Check Position (0x04 bytes) + word nMin; //Pos: 0x00 + word nMax; //Pos: 0x02 +}; + +struct STR_AI_COND_027 +{ + //Check Near Character (0x10 bytes) + int iDistance; //Pos: 0x00 + byte btIsAllied; //Pos: 0x04 + short nLevelDiff; //Pos: 0x06 + short nLevelDiff2; //Pos: 0x08 + short wChrNum; //Pos: 0x0a + byte btOp; //Pos: 0x0c +}; + +struct STR_AI_COND_028 +{ + //Check Variable (4) (0x0c bytes) + word nVarIDX; //Pos: 0x00 + dword iValue; //Pos: 0x04 + byte btOp; //Pos: 0x08 +}; + +struct STR_AI_COND_029 +{ + //Check Target (2) (0x04 bytes) + byte btTargetType; //Pos: 0x00 +}; + +struct STR_AI_COND_030 +{ + //Unknown (0x04 bytes) + dword Timer; //Pos: 0x00 +}; + +struct STR_AI_COND_031 +{ + //no data needed here +}; + +struct STR_AI_COND_032 +{ + //no data needed here +}; diff --git a/Src/World Server/aip/Aip.cpp b/Src/World Server/aip/Aip.cpp new file mode 100644 index 0000000..9be2ded --- /dev/null +++ b/Src/World Server/aip/Aip.cpp @@ -0,0 +1,167 @@ +// Props to ExJam for this code :D +#include "../WorldServer.h" + +void CWorldServer::ReadAIP(strings path, dword index) +{ + CRoseFile* fh = new CRoseFile(path, FM_READ | FM_BINARY); + + if(fh->IsOpen()) + { // goto done; + //Log(MSG_LOAD, "Loading %s ", path); + dword BlockCount = fh->Get(); + dword mintime = fh->Get(); + dword mindamage = fh->Get(); + dword titlestringlength = fh->Get(); + strings title = new char[titlestringlength+1]; + strings title2 = new char[32+1]; + fh->Read(title, titlestringlength, 1); + for(dword i = 0; i < BlockCount; i++) + { + titlestringlength = 32; + fh->Read(title2, titlestringlength, 1);//on spawn-idle-attack-attacked-killed target-dead + dword RecordCount = fh->Get(); + + for(dword j = 0; j < RecordCount; j++) + { + CAip* script = new CAip(); + fh->Read(title2, titlestringlength, 1);// 00 ============================== + script->ConditionCount = fh->Get(); + if(script->ConditionCount > 0) + { + script->Conditions = new CAip::SAipDatum*[script->ConditionCount]; + for(dword k = 0; k < script->ConditionCount; k++) + { + CAip::SAipDatum* data = new CAip::SAipDatum(); + data->size = fh->Get(); + data->opcode = fh->Get() - 0x04000001; + data->data = new byte[data->size - 8]; + fh->Read(data->data, data->size - 8, 1); + script->Conditions[k] = data; + } + } + else + { + script->Conditions = NULL; + } + script->ActionCount = fh->Get(); + + if(script->ActionCount > 0) + { + script->Actions = new CAip::SAipDatum*[script->ActionCount]; + for(dword k = 0; k < script->ActionCount; k++) + { + CAip::SAipDatum* data = new CAip::SAipDatum(); + data->size = fh->Get(); + data->opcode = fh->Get() - 0x0B000001; + data->data = new byte[data->size - 8]; + fh->Read(data->data, data->size - 8, 1); + script->Actions[k] = data; + } + } + else + { + script->Actions = NULL; + } + script->AInumber = index; + script->AipID=((index*0x10000)+(i*0x100)+j); + script->minDamage = mindamage; + script->minTime = mintime; + script->recordcount[i] = RecordCount; + AipList.push_back( script ); + } + } + } + else + Log( MSG_ERROR, "AIP File: '%s' failed to load properly", path ); + fh->Close(); + delete fh; +} + +void CWorldServer::LoadAipData() +{ + CStrStb* stbAip = new CStrStb("3DDATA\\STB\\FILE_AI.STB"); + Log(MSG_INFO, "Loading AIP files"); + for(dword i = 1; i < stbAip->Rows(); i++) + { + if(stbAip->Data(i, 0)) + { + GServer->ReadAIP(stbAip->Data(i, 0),i); + } + } +//Log(MSG_INFO, "%i aip entrys",AipList.size()); + Log(MSG_INFO, "Finished loading aip data "); + + aiCondFunc[0] = &F_AI_COND_000; + aiCondFunc[1] = &F_AI_COND_001; + aiCondFunc[2] = &F_AI_COND_002; + aiCondFunc[3] = &F_AI_COND_003; + aiCondFunc[4] = &F_AI_COND_004; + aiCondFunc[5] = &F_AI_COND_005; + aiCondFunc[6] = &F_AI_COND_006; + aiCondFunc[7] = &F_AI_COND_007; + aiCondFunc[8] = &F_AI_COND_008; + aiCondFunc[9] = &F_AI_COND_009; + aiCondFunc[10] = &F_AI_COND_010; + aiCondFunc[11] = &F_AI_COND_011; + aiCondFunc[12] = &F_AI_COND_012; + aiCondFunc[13] = &F_AI_COND_013; + aiCondFunc[14] = &F_AI_COND_014; + aiCondFunc[15] = &F_AI_COND_015; + aiCondFunc[16] = &F_AI_COND_016; + aiCondFunc[17] = &F_AI_COND_017; + aiCondFunc[18] = &F_AI_COND_018; + aiCondFunc[19] = &F_AI_COND_019; + aiCondFunc[20] = &F_AI_COND_020; + aiCondFunc[21] = &F_AI_COND_021; + aiCondFunc[22] = &F_AI_COND_022; + aiCondFunc[23] = &F_AI_COND_023; + aiCondFunc[24] = &F_AI_COND_024; + aiCondFunc[25] = &F_AI_COND_025; + aiCondFunc[26] = &F_AI_COND_026; + aiCondFunc[27] = &F_AI_COND_027; + aiCondFunc[28] = &F_AI_COND_028; + aiCondFunc[29] = &F_AI_COND_029; + aiCondFunc[30] = &F_AI_COND_030; + + aiActFunc[0] = &F_AI_ACT_000; + aiActFunc[1] = &F_AI_ACT_001; + aiActFunc[2] = &F_AI_ACT_002; + aiActFunc[3] = &F_AI_ACT_003; + aiActFunc[4] = &F_AI_ACT_004; + aiActFunc[5] = &F_AI_ACT_005; + aiActFunc[6] = &F_AI_ACT_006; + aiActFunc[7] = &F_AI_ACT_007; + aiActFunc[8] = &F_AI_ACT_008; + aiActFunc[9] = &F_AI_ACT_009; + aiActFunc[10] = &F_AI_ACT_010; + aiActFunc[11] = &F_AI_ACT_011; + aiActFunc[12] = &F_AI_ACT_012; + aiActFunc[13] = &F_AI_ACT_013; + aiActFunc[14] = &F_AI_ACT_014; + aiActFunc[15] = &F_AI_ACT_015; + aiActFunc[16] = &F_AI_ACT_016; + aiActFunc[17] = &F_AI_ACT_017; + aiActFunc[18] = &F_AI_ACT_018; + aiActFunc[19] = &F_AI_ACT_019; + aiActFunc[20] = &F_AI_ACT_020; + aiActFunc[21] = &F_AI_ACT_021; + aiActFunc[22] = &F_AI_ACT_022; + aiActFunc[23] = &F_AI_ACT_023; + aiActFunc[24] = &F_AI_ACT_024; + aiActFunc[25] = &F_AI_ACT_025; + aiActFunc[26] = &F_AI_ACT_026; + aiActFunc[27] = &F_AI_ACT_027; + aiActFunc[28] = &F_AI_ACT_028; + aiActFunc[29] = &F_AI_ACT_029; + aiActFunc[30] = &F_AI_ACT_030; + aiActFunc[31] = &F_AI_ACT_031; + aiActFunc[32] = &F_AI_ACT_032; + aiActFunc[33] = &F_AI_ACT_033; + aiActFunc[34] = &F_AI_ACT_034; + aiActFunc[35] = &F_AI_ACT_035; + aiActFunc[36] = &F_AI_ACT_036; + aiActFunc[37] = &F_AI_ACT_037; + + delete stbAip; +} + diff --git a/Src/World Server/aip/Aip.h b/Src/World Server/aip/Aip.h new file mode 100644 index 0000000..48a23df --- /dev/null +++ b/Src/World Server/aip/Aip.h @@ -0,0 +1,29 @@ +// Props to ExJam for this code :] +class CAip +{ + public: + struct SAipDatum + { + int size; + int opcode; + byte* data; + }; + + dword AInumber; + dword AipID; + dword minDamage; + dword minTime; + byte recordcount[6]; + + dword ConditionCount; + SAipDatum** Conditions; + + dword ActionCount; + SAipDatum** Actions; + int AipVar; + private: +}; +#define AI_FAILURE 8 +#define AI_SUCCESS 7 +#include "AiConditions.h" +#include "AiActions.h" diff --git a/Src/World Server/battle.cpp b/Src/World Server/battle.cpp new file mode 100644 index 0000000..79d7db4 --- /dev/null +++ b/Src/World Server/battle.cpp @@ -0,0 +1,975 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +void CCharacter::DoAttack( ) +{ + //if(Status->Sleep <= 0xff) + //{ + // ClearBattle( Battle ); + // return; // can't take any action while we are asleep can we? + //} + //if(IsPlayer()) + //{ + // Log(MSG_INFO,"DoAttack Attack type %i selected. Chartype = %i casting = %i",Battle->atktype, CharType, Battle->iscasting); + //} + //if(CharType = 4) //it's an npc + //{ + // Log(MSG_DEBUG,"NPC battle sequence"); + //} + if(Battle->iscasting == 1) + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + //Log(MSG_DEBUG,"Iscasting detected as true."); + if(CanAttack()) + { + if(IsPlayer()) + { + //Log(MSG_DEBUG,"Should reach here once on normal attack. iscasting state = %i attack type = %i",Battle->iscasting, Battle->atktype); + + if(Battle->atktype == 3) //buff + { + + ClearBattle( Battle ); + return; + } + else // if not buffing then resume normal attack. + { + //ClearBattle( Battle ); + StartAction(Enemy, NORMAL_ATTACK, 0); + } + } + else //monsters should always resume normal attack + { + StartAction(Enemy, NORMAL_ATTACK, 0); + } + } + else + { + return; + } + return; + } + if(!CanAttack()) + { + return; + } + if(IsSummon()) + { + //Log(MSG_DEBUG,"Summon attacking in function DoAttack"); + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) // || (Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE)) + { + Log(MSG_DEBUG,"No Enemy found"); + ClearBattle( Battle ); + return; + } + if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen + { + //Log(MSG_INFO,"Clearing Battle for this summon"); + ClearBattle( Battle ); + return; + } + if(Enemy->IsSummon()) + { + CMonster* thisMonster = reinterpret_cast(this); + if(thisMonster == NULL) + { + ClearBattle( Battle ); + return; + } + CMonster* otherMonster = reinterpret_cast(Enemy); + if(otherMonster == NULL) + { + ClearBattle( Battle ); + return; + } + if(thisMonster->owner == otherMonster->owner) + { + //Log(MSG_INFO,"Clearing Battle for this summon"); + ClearBattle( Battle ); + return; + } + } + } + + CMap* map = GServer->MapList.Index[Position->Map]; + //if(IsPlayer()) + //{ + // Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype); + //} + switch(Battle->atktype) + { + case NORMAL_ATTACK://normal attack + { + //if(!Status->CanAttack) + // return; //can't attack right now. + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + if(Enemy == this) + { + //Log(MSG_INFO,"WTF?? I AM trying to attack myself"); + ClearBattle( Battle ); + } + if(IsTargetReached( Enemy )) + { + //if(IsMonster()) + // Log(MSG_INFO,"Monster is trying to hit the player"); + NormalAttack( Enemy ); + if (Enemy->IsMonster()) // do monster AI script when monster is attacked. + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + //monster->DoAi(monster->thisnpc->AI, 3); + monster->DoAi(monster->monAI, 3); + } + } + } + break; + case SKILL_ATTACK://skill attack + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + if(IsTargetReached( Enemy, skill )) + { + //Log(MSG_DEBUG,"Skill Attack reached target and started"); + SkillAttack( Enemy, skill ); + if (Enemy->IsMonster()) + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + monster->DoAi(monster->monAI, 3); + Battle->atktype = NORMAL_ATTACK; + } + } + //else + //{ + //Log(MSG_DEBUG,"Skill Attack failed to reach target"); + //} + } + break; + case SKILL_BUFF://buffs + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + //Log(MSG_DEBUG,"this target is NULL"); + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //Log(MSG_DEBUG,"this skill is NULL"); + return; + } + + if(IsTargetReached( Enemy, skill )) + { + //Log(MSG_DEBUG,"Skill successfully cast"); + BuffSkill( Enemy, skill ); + } + } + break; + case SKILL_AOE: + //case SKILL_SELF: //this is impossible. it can never be set + case AOE_TARGET: + { + + + //Log(MSG_DEBUG,"Called skill type %i",Battle->atktype); + CCharacter* Enemy = NULL; + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + ClearBattle( Battle ); + return; + } + if(Battle->atktype == AOE_TARGET) + { + float distance = GServer->distance( Position->current, Position->destiny ); + if(distance <= skill->range) + { + Position->destiny = Position->current; + AoeSkill( skill, Enemy ); + } + } + else + { + Position->skilltargetpos = Position->current; + AoeSkill( skill, NULL ); + } + } + break; + case BUFF_SELF: + { + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + BuffSkill( this, skill ); + } + break; + case BUFF_AOE: + { + //Log(MSG_INFO,"BUFF AOE selected"); + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + return; + } + AoeBuff( skill ); + } + break; + default: + return; + break; + } +} + + +// do normal attack +void CCharacter::NormalAttack( CCharacter* Enemy ) +{ + Enemy->OnBeAttacked( this ); + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(NormalAttack) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + + + // Even newer formula based on Jayce's idea + //dodge. gives a percentage chance + unsigned int hitpower = 0; + bool critical = false; + unsigned int totalchance = Stats->Accury + Enemy->Stats->Dodge; + double acdm = Stats->Accury * 100 / totalchance; + unsigned int hitchance = (unsigned int)floor(acdm); + if(GServer->RandNumber( 0, 100 ) > hitchance) + { + hitpower = 0; // dodged + //Log(MSG_DEBUG,"accuracy %i dodge %i",Stats->Accury,Enemy->Stats->Dodge); + //Log(MSG_DEBUG,"Dodged total %i mod %f hit chance %i",totalchance,acdm,hitchance); + } + else + { + //level adjustment. Not yet implemented + unsigned int leveltotal = Stats->Level + Enemy->Stats->Level; + double levelmod = Stats->Level * 200 / leveltotal; + + //calculate damage + if(Stats->magicattack == 1) //magic attacks such as wands and some monsters + { + unsigned int totalpower = Stats->Attack_Power + Enemy->Stats->Magic_Defense; + double hitmod = Stats->Attack_Power * 100 / totalpower + Stats->Level; //percentage of hitpower to use * 100 + hitpower = (unsigned int)floor((double)(hitmod * Stats->Attack_Power / 100)); + } + else + { + unsigned int totalpower = Stats->Attack_Power + Enemy->Stats->Defense; + double hitmod = Stats->Attack_Power * 100 / totalpower + Stats->Level; //percentage of hitpower to use * 100 + hitpower = (unsigned int)floor((double)(Stats->Attack_Power * hitmod / 100)); + //Log(MSG_DEBUG,"Normal Attack: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense); + //Log(MSG_DEBUG,"Normal Attack: hit total %i hitmod %f hit power %i",totalpower,hitmod,hitpower); + } + //add some randomness. + or - 5% of hitpower + int min = (int)floor((double)(hitpower * 0.85)); + int max = (int)floor((double)(hitpower * 1.15)); + int dif = max - min; + int mod = GServer->RandNumber( 0, dif ); + hitpower = min + mod; + //Log(MSG_DEBUG,"min %i max %i dif %i mod %i modified hit power %i", min, max, dif, mod, hitpower); + if(hitpower <= 5) + { + hitpower = 5; + } + else + { + if(GServer->RandNumber(0,300)Critical) + { + hitpower = (long int)floor(hitpower * 1.5); + critical = true; + //Log(MSG_DEBUG,"CRITICAL hit power %i", hitpower); + } + } + if (hitpower > 0x7ff)//2047 packet size limit. + hitpower = 0x7ff; + //Log(MSG_DEBUG,"Successful hit hit chance %i damage %i",hitchance, hitpower); + } + + + //end jayce formula + Battle->atktarget = Battle->target; + //if(IsMonster()) + //{ + //Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget); + //} + //Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack ); + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, hitpower ); + Enemy->damagecounter += hitpower;// is for AI + } + Enemy->Stats->HP -= hitpower; + //if(IsPlayer()) + // Log(MSG_DEBUG,"Player attack did damage %i",hitpower); + //if(IsMonster()) + // Log(MSG_DEBUG,"Monster attack did damage %i",hitpower); + + + + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->atktarget ); + + if(Enemy->IsDead()) + { + CDrop* thisdrop = NULL; + ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) )); + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + int thisdroprate = Stats->itemdroprate + GServer->Config.DROP_RATE; + //Log(MSG_DEBUG,"Monster killed. calling drops function %i times",thisdroprate); + for(int j=0;jGetDrop( ); + if(thisdrop != NULL) + { + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + } + GServer->SendToVisible( &pak, Enemy ); + ClearBattle(Battle); + //ClearBattle(Enemy->Battle); + if(Enemy->IsMonster()) + { + CMonster* monster = reinterpret_cast(Enemy); + if(monster == NULL) + { + //Log(MSG_DEBUG,"Monster killed. Failed to create monster object"); + return; + } + else + { + //Log(MSG_DEBUG,"Monster killed. Setting death delay timer"); + monster->DeathDelayTimer = clock(); + } + } + OnEnemyDie( Enemy ); + ClearBattle(Battle); + GServer->SendToVisible( &pak, Enemy ); + } + else + { + ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0))); + //ADDDWORD ( pak, (hitpower>0?(critical?12:0):0) ); + GServer->SendToVisible( &pak, Enemy ); + } + ReduceABC( ); + Battle->lastAtkTime = clock( ); + //Battle->lastAtkTime = Battle->NextAtkTime; +} + +// do skill attack +bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill ) +{ + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(SkillAttack) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + UseAtkSkill( Enemy, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) + Stats->MP = 0; + //Battle->atktype = NORMAL_ATTACK; + //Battle->skilltarget = 0; + //Battle->atktarget = Battle->target; + //Battle->skillid = 0; + GServer->DoSkillScript( this, skill ); + Battle->lastAtkTime = clock( ); + //Battle->lastAtkTime = Battle->NextAtkTime; + if(Enemy->IsDead()) + ClearBattle(Battle); + + + Battle->iscasting = 1; + //Log(MSG_DEBUG,"Cast a skill. Iscasting set to true"); + return true; +} + +// do buff skill +bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill ) +{ + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(BuffSkill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + //Log(MSG_DEBUG,"applied buff skill %i",skill->id); + UseBuffSkill( Target, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills? + if(!IsMonster()) + { + //ClearBattle( Battle ); + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + } + else //Monsters need to be reset to normal attack and clear skill attacks. + { + //Battle->atktarget = Battle->target; + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + //StartAction(Target, NORMAL_ATTACK); + } + //Battle->lastAtkTime = clock( ); + Battle->lastAtkTime = Battle->NextAtkTime; + Battle->iscasting = 1; + return true; +} +//arnold +// do AoE skill +bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy ) +{ + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(AoeSkill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + //Log(MSG_INFO,"Doing AOE Skill"); + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + CMap* map = GServer->MapList.Index[Position->Map]; + + //PY replacement code + if(IsPlayer() || IsSummon()) + { + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster == NULL) continue; + if(monster->clientid == clientid) continue; + if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill,1 ); + } + } + if(IsMonster() && !IsSummon()) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + if(player->clientid == clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos, player->Position->current, (float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill,1 ); + } + } + //PY end + + // this code appears to me to be completely wrong replaced with code above + /* + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster->clientid == clientid) continue; + if(IsSummon( ) || IsPlayer( )) + { + if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue; + } + else + { + if(!monster->IsSummon( )) continue; + } + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill ); + } + if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( ))) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player->clientid==clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill ); + } + }*/ + Battle->iscasting = 1; + //Log(MSG_DEBUG,"Cast a skill. Iscasting set to true"); + if(Enemy != NULL) + { + if(Enemy->IsDead( )) + ClearBattle( Battle ); + } + else + ClearBattle( Battle ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + Battle->lastAtkTime = clock( ); + //Battle->lastAtkTime = Battle->NextAtkTime; + //Send skill animation to the world + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + return true; +} + +bool CCharacter::AoeBuff( CSkills* skill ) +{ + //Log(MSG_INFO,"BUFF AOE called with skill target ",skill->target); + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(AoeBuff) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + CMap* map = GServer->MapList.Index[Position->Map]; + + if(CharType != 2) //player || Summon || NPC. Anything that is not a monster + { + //Log(MSG_INFO,"It's a Player, Summon or NPC"); + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player == NULL)continue; + switch(skill->target) + { + case 1: //tPartyMember + if(player->Party->party == GetParty()) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member"); + if (player == this) //it is me so I had better cast it anyway ^-^ + { + UseBuffSkill( (CCharacter*)player, skill ); + } + else if(player->Party->party != NULL) // Don't cast the skill on players who are not in any party at all. + { + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + } + break; + case 2: //tClanMember + if (player->Clan == GetClan( ) && (player->Clan != 0 || player == this)) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member"); + if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) ) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 5: //tHostileCharacter + { + if (CharType == 2) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + } + break; + default: + //Log(MSG_INFO,"No valid skill target found"); + break; + } + } + } + else if (CharType == 2) //monster + { + //Log(MSG_INFO,"It's a monster"); + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster == NULL)continue; + switch(skill->target) + { + case tPartyMember: // party + break; + case 2: //tClanMember + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster"); + if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)monster, skill ); + } + break; + case tHostileCharacter: + break; + default: + break; + } + } + } + + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + //Battle->atktarget = Battle->target; + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + ClearBattle( Battle ); + Battle->lastAtkTime = clock( ); + //Battle->lastAtkTime = Battle->NextAtkTime; + Battle->iscasting = 1; + //Send skill animation to the world + BEGINPACKET( pak1, 0x7b9); + ADDWORD ( pak1, clientid); + ADDWORD ( pak1, Battle->skillid); + ADDWORD ( pak1, 1); + GServer->SendToVisible( &pak1, this ); + return true; +} + + + +// use skill attack +void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff) +{ + + // Even newer formula based on Jayce's idea + //dodge. gives a percentage chance + Battle->iscasting = 1; + unsigned int hitpower = 0; + unsigned int Attack = 0; + // skill attacks now based on a percentage of base attack power so that they scale with level + Attack = (unsigned int)floor((double)skill->atkpower * Stats->Attack_Power / 100); + int success = GServer->RandNumber(0,100); + if(success <= skill->success) + { + //calculate damage + if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage + { + //unsigned int totalpower = skill->atkpower + Enemy->Stats->Magic_Defense; + unsigned int totalpower = Attack + Enemy->Stats->Magic_Defense; + double hitmod = Attack * 100 / totalpower; //percentage of hitpower to use * 100 + hitpower = (unsigned int)floor((double)(hitmod * Attack / 100)); + //Log(MSG_DEBUG,"Skill magic: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense); + //Log(MSG_DEBUG,"Skill magic: Skill power %i. Attack %i",skill->atkpower,Attack); + //Log(MSG_DEBUG,"Skill magic: hit total %i mod %f hit power %i",totalpower,hitmod,hitpower); + } + else + { + //unsigned int totalpower = skill->atkpower + Enemy->Stats->Defense; + unsigned int totalpower = Attack + Enemy->Stats->Defense; + double hitmod = Attack * 100 / totalpower; //percentage of hitpower to use * 100 + hitpower = (unsigned int)floor((double)(hitmod * Attack / 100)); + //Log(MSG_DEBUG,"Skill physical: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense); + //Log(MSG_DEBUG,"Skill physical: Skill power %i. Attack %i",skill->atkpower,Attack); + //Log(MSG_DEBUG,"skill physical: hit total %i mod %f hit power %i",totalpower,hitmod,hitpower); + } + //add some randomness. + or - 5% of hitpower + int min = (int)floor((double)(hitpower * 0.85)); + int max = (int)floor((double)(hitpower * 1.15)); + int dif = max - min; + int mod = GServer->RandNumber( 0, dif ); + hitpower = min + mod; + } + + bool bflag = false; + Enemy->OnBeAttacked( this ); + if(hitpower <= 0) hitpower = 0; + if(IsPlayer()) + { + hitpower += (int)(hitpower * (Stats->ExtraDamage))/100; + } + if (skill->atkpower == 0) + { + hitpower = 0; // If the skill has a 0 atk power, it isn't meant to damage + Log( MSG_INFO, "This skill does no damage" ); + //ClearBattle( Battle ); + return; // probably need to add some alternative code here for legitimate skills that have no attack power. + } + if(hitpower > 2046)hitpower = 2046; + //Log(MSG_DEBUG,"Cast a skill. damage is %i",skillpower); + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, hitpower ); + Enemy->damagecounter += hitpower;// is for AI + } + //Log( MSG_INFO, "modified skillpower = %i",hitpower ); + Enemy->Stats->HP -= hitpower; + //if(IsPlayer()) + // Log(MSG_DEBUG,"Player Spell did damage %i",hitpower); + //if(IsMonster()) + // Log(MSG_DEBUG,"Monster spell did damage %i",hitpower); + BEGINPACKET( pak, 0x7b6 ); + ADDWORD ( pak, Enemy->clientid ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, 0x000007f8 ); + ADDBYTE ( pak, 0x00 ); + if (Enemy->Stats->HP < 1) + { + ADDWORD ( pak, hitpower + 0x8000 ); + } + else + { + ADDWORD ( pak, hitpower + 0x2000 ); + } + if(Enemy->IsDead()) + { + //Log(MSG_DEBUG,"Enemy died"); + CDrop* thisdrop = NULL; + ADDDWORD ( pak, 16 ); + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + //Log(MSG_DEBUG,"Monster killed. calling drops function %i times",Stats->itemdroprate); + int thisdroprate = Stats->itemdroprate + GServer->Config.DROP_RATE; + for(int j=0;jGetDrop( ); + if(thisdrop != NULL) + { + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + } + + CMonster* monster = reinterpret_cast(Enemy); + if(Enemy->IsMonster()) + { + if(monster == NULL) + { + //Log(MSG_DEBUG,"Monster killed. Failed to create monster object"); + return; + } + else + { + //Log(MSG_DEBUG,"Monster killed. Setting death delay timer"); + monster->DeathDelayTimer = clock(); + } + } + OnEnemyDie( Enemy ); + ClearBattle(Battle); + GServer->SendToVisible( &pak, Enemy ); + } + else + { + ADDDWORD ( pak, 4 ); + GServer->SendToVisible( &pak, Enemy ); + bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) ); + + //Send (de)buff information to the whole world + if(skill->nbuffs > 0 && bflag) + { + RESETPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Battle->skilltarget ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( ) ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Enemy ); + } + } + if (deBuff) return; + //Send skill animation to the world + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + //Battle->lastAtkTime = clock( ); + Battle->lastAtkTime = Battle->NextAtkTime; //trying this to time skills better + //Battle->atktype = NORMAL_ATTACK; +} + +// use buff skill +void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill, bool deBuff ) +{ + bool bflag = false; + bflag = GServer->AddBuffs( skill, Target, GetInt( ) ); + //Log(MSG_INFO,"In UseBuffSkills, skill %i, nbuffs %i, bflag %i to %u",skill->id,skill->nbuffs,bflag,Target->clientid); + if(skill->nbuffs > 0 && bflag == true) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( )/4 ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Target ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 1"); + } + if (deBuff) return; + BEGINPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs); + //Log(MSG_INFO,"UseBuffSkill: Target HP = %i",Target->Stats->HP); +} + +// use buff skill from QSD +void CCharacter::UseBuffSkillQSD( CCharacter* Target, CSkills* skill, bool deBuff ) +{ + bool bflag = false; + bflag = GServer->AddBuffs( skill, Target, 1 ); + //Log(MSG_INFO,"In UseBuffSkillsQSD, skill %i, nbuffs %i, bflag %i to %u",skill->id,skill->nbuffs,bflag,Target->clientid); + if(skill->nbuffs > 0 && bflag == true) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, skill->id ); + ADDWORD ( pak, 1 ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Target ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 1"); + } + if (deBuff) return; + BEGINPACKET( pak, 0x7b9); + ADDWORD ( pak, Target->clientid); + ADDWORD ( pak, skill->id); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs); + //Log(MSG_INFO,"UseBuffSkillQSD: Target HP = %i",Target->Stats->HP); +} + +// Use a skill +bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target ) +{ + if (skill->atkpower > 0) + { + Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i", + ( skill->atkpower ), + ( (skill->range > 0) ? " AOE" : "" ), + ( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ), + ( skill->range ), + ( skill->target ) ); + } + for (int i = 0; i < 2; i++) { + Log(MSG_INFO, "Status ID: %i", skill->status[i]); + if (skill->status[i] == 0) continue; + CStatus* status = GServer->GetStatusByID(skill->status[i]); + if (status == NULL) continue; + /************************ + We'll probably need to use status->decrease (Figure out whether buf is up/down + status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat + (Recovery, continueing damage [1]) or Special (Status Effect [3]). + ************************/ + if (status->repeat == 1) // Continuous + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + } + else if (status->repeat == 2) // Once (Stat Boost) + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + + } else if (status->repeat == 3) // Status Effect (Poison,etc) + { + Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i], + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ( skill->duration ) ); + } + } + return true; +} + + diff --git a/Src/World Server/battle_old_version.cpp b/Src/World Server/battle_old_version.cpp new file mode 100644 index 0000000..164c132 --- /dev/null +++ b/Src/World Server/battle_old_version.cpp @@ -0,0 +1,802 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +void CCharacter::DoAttack( ) +{ + //if(Status->Sleep <= 0xff) + //{ + // ClearBattle( Battle ); + // return; // can't take any action while we are asleep can we? + //} + if(IsSummon()) + { + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL && Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE) + { + //Log(MSG_DEBUG,"No Enemy found"); + ClearBattle( Battle ); + return; + } + //Log(MSG_DEBUG,"Client id: %i Enemy client id: %i",clientid, Enemy->clientid); + if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen + { + //Log(MSG_INFO,"Clearing Battle for this summon"); + ClearBattle( Battle ); + return; + } + } + CMap* map = GServer->MapList.Index[Position->Map]; + //Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype); + switch(Battle->atktype) + { + case NORMAL_ATTACK://normal attack + { + //if(!Status->CanAttack) + // return; //can't attack right now. + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + if(Enemy == this) + { + //Log(MSG_INFO,"WTF?? I AM trying to attack myself"); + ClearBattle( Battle ); + } + if(IsTargetReached( Enemy ) && CanAttack( )) + { + NormalAttack( Enemy ); + if (Enemy->IsMonster()) // do monster AI script when monster is attacked. + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + monster->DoAi(monster->thisnpc->AI, 3); + } + + } + } + break; + case SKILL_ATTACK://skill attack + { + //if(!Status->CanCastSkill) + // return; // can't cast skills right now + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + if(IsTargetReached( Enemy, skill ) && CanAttack( )) + { + SkillAttack( Enemy, skill ); + if (Enemy->IsMonster()) + { + CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map); + if(monster == NULL)return; + monster->DoAi(monster->thisnpc->AI, 3); + } + } + } + break; + case SKILL_BUFF://buffs + { + //if(!Status->CanCastSkill) + // return; // can't cast skills right now + CCharacter* Enemy = GetCharTarget( ); + if(Enemy == NULL) + { + //Log(MSG_DEBUG,"this target is NULL"); + ClearBattle( Battle ); + return; + } + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //Log(MSG_DEBUG,"this skill is NULL"); + //ClearBattle( Battle ); + return; + } + //Log(MSG_DEBUG,"Called skill buff code for skill %i",skill->id); + if(IsTargetReached( Enemy, skill ) && CanAttack( )) + { + //Log(MSG_DEBUG,"Skill successfully cast"); + BuffSkill( Enemy, skill ); + } + } + break; + case SKILL_AOE: + //case SKILL_SELF: //this is impossible. it can never be set + case AOE_TARGET: + { + //if(!Status->CanCastSkill) + // return; // can't cast skills right now + //Log(MSG_DEBUG,"Called skill type %i",Battle->atktype); + CCharacter* Enemy = NULL; + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + ClearBattle( Battle ); + return; + } + if(Battle->atktype == AOE_TARGET) + { + float distance = GServer->distance( Position->current, Position->destiny ); + if(distance <= skill->range && CanAttack( )) + { + Position->destiny = Position->current; + AoeSkill( skill, Enemy ); + } + } + else + { + Position->skilltargetpos = Position->current; + AoeSkill( skill, NULL ); + } + } + break; + case BUFF_SELF: + { + //if(!Status->CanCastSkill) + // return; // can't cast skills right now + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + return; + } + BuffSkill( this, skill ); + } + break; + case BUFF_AOE: + { + //if(!Status->CanCastSkill) + // return; // can't cast skills right now + Log(MSG_INFO,"BUFF AOE selected"); + CSkills* skill = GServer->GetSkillByID( Battle->skillid ); + if(skill == NULL) + { + //ClearBattle( Battle ); + + return; + } + AoeBuff( skill ); + } + break; + default: + return; + break; + } +} + + +// do normal attack +void CCharacter::NormalAttack( CCharacter* Enemy ) +{ + Enemy->OnBeAttacked( this ); + Position->destiny = Position->current; + // PY code + float mod = 0; + if(IsPlayer()) + { + mod = 23 + (Stats->Level / 1.5); + } + else + { + mod = 23 + (Stats->Level / 4); + + } + float attack = 0; + if(Stats->magicattack == 1) + { + attack = (float) Stats->Attack_Power / Enemy->Stats->Magic_Defense; + attack *= mod; + } + else + { + attack = (float) Stats->Attack_Power / Enemy->Stats->Defense; + attack *= mod; + } + //PY end + if(attack < 5) attack = 5; + float d_attack = attack / 100; + mod = GServer->RandNumber( 0, 10 ) * d_attack; + attack += mod; + long int hitpower = (long int)floor(attack + GServer->RandNumber(0, 10)); + if(IsPlayer( )) //temp fix to find balance btw monster and player + { + hitpower = (long int)floor(attack * (GServer->Config.PlayerDmg/100.00)); + hitpower+=((hitpower*(Stats->ExtraDamage))/100); + } + if(IsMonster( )) //temp fix to find balance btw monster and player + hitpower = (long int)floor(attack * (GServer->Config.MonsterDmg/100.00)); + bool critical = false; + if(hitpower <= 5) + { + hitpower = 5; + } + else + { + if(GServer->RandNumber(0,300)Critical) + { + hitpower = (long int)floor(hitpower * 1.5); + critical = true; + } + } + // dodge + unsigned int hitvalue = (unsigned int)floor((double)(Stats->Accury * 100 / Enemy->Stats->Dodge)); + if(hitvalue>100) hitvalue = 100; + if(GServer->RandNumber( 0, 100 )>hitvalue) + hitpower = 0; // dodged + if (hitpower > 0x7ff)//2047 packet size limit. + { + hitpower=0x7ff; + } + + //maybe this little line here will fix my ghosty problem + //they are definitely attacking. But no animation so maybe atktarget is empty + Battle->atktarget = Battle->target; + //Well it does make the damage show up at teh client but the animation is still dodgey + + if(IsMonster()) + { + Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget); + } + //Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack ); + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, hitpower ); + Enemy->damagecounter += hitpower;// is for AI + } + Enemy->Stats->HP -= hitpower; + + + + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->atktarget ); +#ifdef USE124 + ADDDWORD ( pak, hitpower ); +#endif + + if(Enemy->IsDead()) + { + CDrop* thisdrop = NULL; +#ifndef USE124 + ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) )); +#else + ADDDWORD ( pak, critical?28:16 ); +#endif + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + thisdrop = Enemy->GetDrop( ); + if(thisdrop!=NULL) + { + ADDFLOAT ( pak, thisdrop->pos.x*100 ); + ADDFLOAT ( pak, thisdrop->pos.y*100 ); + if(thisdrop->type == 1) + { + ADDWORD( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + { + ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); + } + ADDWORD ( pak, thisdrop->clientid ); + ADDWORD ( pak, thisdrop->owner ); + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, Enemy, thisdrop ); + OnEnemyDie( Enemy ); + } + else + { +#ifndef USE124 + ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0))); +#else + ADDDWORD ( pak, (hitpower>0?(critical?12:0):0) ); +#endif + GServer->SendToVisible( &pak, Enemy ); + } + ReduceABC( ); + Battle->lastAtkTime = clock( ); +} + +// do skill attack +bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill ) +{ + Position->destiny = Position->current; + //if(Battle->castTime==0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etimecastTime = 0; + UseAtkSkill( Enemy, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP<0) Stats->MP=0; + if(Battle->contatk) + { + Battle->atktype = NORMAL_ATTACK; + Battle->skilltarget = 0; + Battle->atktarget = Battle->target; + Battle->skillid = 0; + } + else + { + //ClearBattle( Battle ); + } + GServer->DoSkillScript( this, skill ); + Battle->lastAtkTime = clock( ); + return true; +} + +// do buff skill +bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill ) +{ + Position->destiny = Position->current; + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + //Log(MSG_DEBUG,"applied buff skill %i",skill->id); + UseBuffSkill( Target, skill ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills? + if(!IsMonster()) + { + ClearBattle( Battle ); // clear battle for players when they use buff skills + //Battle->bufftarget = 0; + //Battle->skilltarget = 0; + //Battle->skillid = 0; + //Battle->atktype = NORMAL_ATTACK; + } + else //Monsters need to be reset to normal attack and clear skill attacks. + { + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + Battle->atktype = NORMAL_ATTACK; + } + Battle->lastAtkTime = clock( ); + return true; +} +//arnold +// do AoE skill +bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy ) +{ + Position->destiny = Position->current; + //Log(MSG_INFO,"Doing AOE Skill"); + //if(Battle->castTime == 0) + //{ + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + // Battle->castTime = clock(); + // return true; + //} + //else + //{ + // clock_t etime = clock() - Battle->castTime; + // if(etime < SKILL_DELAY) + // return true; + //} + //Battle->castTime = 0; + CMap* map = GServer->MapList.Index[Position->Map]; + + //PY replacement code + if(IsPlayer() || IsSummon()) + { + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster->clientid == clientid) continue; + if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill ); + } + } + if(IsMonster() && !IsSummon()) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player->clientid == clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill ); + } + } + //PY end + + // this code appears to me to be completely wrong replaced with code above + /* + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + if(monster->clientid == clientid) continue; + if(IsSummon( ) || IsPlayer( )) + { + if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue; + } + else + { + if(!monster->IsSummon( )) continue; + } + if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1)) + UseAtkSkill( (CCharacter*) monster, skill ); + } + if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( ))) + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + if(player->clientid==clientid) continue; + if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1)) + UseAtkSkill( (CCharacter*) player, skill ); + } + }*/ + + if(Enemy != NULL) + { + if(!Enemy->IsDead( )) + { + Battle->atktarget = Battle->target; + Battle->atktype = NORMAL_ATTACK; + Battle->skilltarget = 0; + Battle->skillid = 0; + } + else ClearBattle( Battle ); + } + else ClearBattle( Battle ); + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + Battle->lastAtkTime = clock( ); + return true; +} + +bool CCharacter::AoeBuff( CSkills* skill ) +{ + //Log(MSG_INFO,"BUFF AOE called"); + Position->destiny = Position->current; + BEGINPACKET( pak, 0x7bb ); + ADDWORD ( pak, clientid ); + GServer->SendToVisible( &pak, (CCharacter*)this ); + CMap* map = GServer->MapList.Index[Position->Map]; + + if(CharType == 1 || CharType == 3) //player || Summon + { + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* player = map->PlayerList.at(i); + switch(skill->target) + { + case 1: //tPartyMember + if(player->Party->party == GetParty( )) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 2: //tClanMember + if (player->Clan == GetClan( )) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member"); + if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) ) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + break; + case 5: //tHostileCharacter + { + if (CharType == 2) + { + //Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player"); + if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)player, skill ); + } + } + break; + } + } + } + else if (CharType == 2) //monster + { + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* monster = map->MonsterList.at(i); + switch(skill->target) + { + case tPartyMember: // party + break; + case 2: //tClanMember + break; + case 3: //tAlly + case 7: //tAllCharacters + case 8: //tAllMembers. all characters + { + Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster"); + if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1)) + UseBuffSkill( (CCharacter*)monster, skill ); + } + break; + case tHostileCharacter: + break; + } + } + } + Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100)); + if(Stats->MP < 0) Stats->MP = 0; + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + Battle->atktype = NORMAL_ATTACK; + //ClearBattle( Battle ); + Battle->lastAtkTime = clock( ); + return true; +} + + + +// use skill attack +void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff) +{ + long int skillpower = 0; + if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage + { + skillpower = skill->atkpower * 2; + } + else // physical damage + { + skillpower = skill->atkpower * 2; + } + UINT weapontype = 0; + + float mod = 0; + float attack = 0; + if(IsPlayer()) + { + mod = 35 + (Stats->Level / 2); + } + else + { + mod = 35 + (Stats->Level / 2); + } + if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage + { + attack = skillpower / Enemy->Stats->Magic_Defense; + } + else + { + attack = skillpower / Enemy->Stats->Defense; + } + attack *= mod; + attack += (Stats->Attack_Power / 2); // let's add our AP to the skillpower to make it worthwhile to cast skills. + + skillpower = (int)attack; + bool bflag = false; + Enemy->OnBeAttacked( this ); + if(skillpower <= 0) skillpower = 0; + if(IsPlayer()) + { + skillpower += (int)(skillpower * (Stats->ExtraDamage))/100; + } + if (skill->atkpower == 0) + { + skillpower = 0; // If the skill has a 0 atk power, it isn't meant to damage + Log( MSG_INFO, "This skill does no damage" ); + //ClearBattle( Battle ); + return; // probably need to add some alternative code here for legitimate skills that have no attack power. + } + if(skillpower >2046)skillpower = 2046; + if(!Enemy->IsSummon( ) && Enemy->IsMonster( )) + { + Enemy->AddDamage( this, skillpower ); + Enemy->damagecounter += skillpower;// is for AI + } + Enemy->Stats->HP -= skillpower; + BEGINPACKET( pak, 0x7b6 ); + ADDWORD ( pak, Enemy->clientid ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, 0x000007f8 ); + ADDBYTE ( pak, 0x00 ); + if (Enemy->Stats->HP<1) + { + ADDWORD ( pak, skillpower + 0x8000 ); + } + else + { + ADDWORD ( pak, skillpower + 0x2000 ); + } + if(Enemy->IsDead()) + { + CDrop* thisdrop = NULL; + ADDDWORD ( pak, 16 ); + if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( )) + { + thisdrop = Enemy->GetDrop( ); + if(thisdrop!=NULL) + { + ADDFLOAT ( pak, thisdrop->pos.x*100 ); + ADDFLOAT ( pak, thisdrop->pos.y*100 ); + if(thisdrop->type==1) + { + ADDWORD( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + { + ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); + } + ADDWORD ( pak, thisdrop->clientid ); + ADDWORD ( pak, thisdrop->owner ); + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, Enemy, thisdrop ); + OnEnemyDie( Enemy ); + } + else + { + ADDDWORD ( pak, 4 ); + GServer->SendToVisible( &pak, Enemy ); + bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) ); + + //Send (de)buff information to the whole world + if(skill->nbuffs>0 && bflag) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Battle->skilltarget ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( ) ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Enemy ); + } + } + if (deBuff) return; + //Send skill animation to the world + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + Battle->bufftarget = 0; + Battle->skilltarget = 0; + Battle->skillid = 0; + Battle->atktype = NORMAL_ATTACK; +} + +// use buff skill +void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill ) +{ + bool bflag = false; + bflag = GServer->AddBuffs( skill, Target, GetInt( ) ); + if(skill->nbuffs > 0 && bflag == true) + { + BEGINPACKET( pak, 0x7b5 ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Battle->skillid ); + ADDWORD ( pak, GetInt( )/4 ); + ADDBYTE ( pak, skill->nbuffs ); + GServer->SendToVisible( &pak, Target ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 1"); + } + + BEGINPACKET( pak, 0x7b9); + ADDWORD ( pak, clientid); + ADDWORD ( pak, Battle->skillid); + ADDWORD ( pak, 1); + GServer->SendToVisible( &pak, this ); + //Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs); + //Log(MSG_INFO,"Target HP = %i",Target->Stats->HP); +} + +// Use a skill +bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target ) +{ + if (skill->atkpower > 0) + { + Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i", + ( skill->atkpower ), + ( (skill->range > 0) ? " AOE" : "" ), + ( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ), + ( skill->range ), + ( skill->target ) ); + } + for (int i = 0; i < 2; i++) { + Log(MSG_INFO, "Status ID: %i", skill->status[i]); + if (skill->status[i] == 0) continue; + CStatus* status = GServer->GetStatusByID(skill->status[i]); + if (status == NULL) continue; + /************************ + We'll probably need to use status->decrease (Figure out whether buf is up/down + status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat + (Recovery, continueing damage [1]) or Special (Status Effect [3]). + ************************/ + if (status->repeat == 1) // Continuous + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + } + else if (status->repeat == 2) // Once (Stat Boost) + { + Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i], + ((status->decrease) ? "decrease" : "increase"), + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ((skill->value1[i] != 0) ? "" : "%"), skill->duration); + + } else if (status->repeat == 3) // Status Effect (Poison,etc) + { + Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i], + ((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]), + ( skill->duration ) ); + + } + } + return true; +} \ No newline at end of file diff --git a/Src/World Server/character.cpp b/Src/World Server/character.cpp new file mode 100644 index 0000000..72d2736 --- /dev/null +++ b/Src/World Server/character.cpp @@ -0,0 +1,205 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +//constructor +CCharacter::CCharacter( ) +{ + // STATS + Stats = new STATS; + //refnpc=NULL; + assert(Stats); + Stats->Level = 0; + Stats->HP = 0; + Stats->MP = 0; + Stats->MaxHP = 0; + Stats->MaxMP = 0; + Stats->Attack_Power = 0; + Stats->Defense = 0; + Stats->Critical = 0; + Stats->Dodge = 0; + Stats->Accury = 0; + Stats->Magic_Defense = 0; + Stats->Move_Speed = 0; + Stats->Attack_Speed = 0; + Stats->Attack_Distance = 0; + Stats->MaxWeight = 0; + Stats->MaxSummonGauge = 0; + Stats->MPReduction = 0; + Stats->ExtraDamage = 0; + // POSITION + Position = new POSITION; + assert(Position); + Position->Map = 0; + Position->lastMoveTime = 0; + Position->saved = 0; + Position->respawn = 0; + //BATLE + Battle = new BATTLE; + assert(Battle); + ClearBattle( Battle ); + Battle->castTime = 0; + //STATUS + Status = new STATUS; + assert(Status); + //Stats up + Status->Attack_up = 0xff; + Status->Defense_up = 0xff; + Status->Magic_Defense_up = 0xff; + Status->Accury_up = 0xff; + Status->Critical_up = 0xff; + Status->Dodge_up = 0xff; + Status->Haste_up = 0xff; + Status->Dash_up = 0xff; + Status->HP_up = 0xff; + Status->MP_up = 0xff; + //Stats down + Status->Attack_down = 0xff; + Status->Defense_down = 0xff; + Status->Magic_Defense_down = 0xff; + Status->Accury_down = 0xff; + Status->Critical_down = 0xff; + Status->Dodge_down = 0xff; + Status->Haste_down = 0xff; + Status->Dash_down = 0xff; + Status->HP_down = 0xff; + Status->MP_down = 0xff; + //Status + Status->Stuned = 0xff; + Status->Poisoned = 0xff; + Status->Muted = 0xff; + Status->ExtraDamage_up = 0xff; + Status->ExtraDamage_down = 0xff; + Status->Sleep = 0xff; + Status->CanAttack = true; + Status->CanRun = true; + Status->CanMove = true; + Status->CanCastSkill = true; + // Stance + Status->Stance = RUNNING; + for(int i=0;i<30;i++) + { + MagicStatus[i].Status = 0; + MagicStatus[i].Buff = 0; + MagicStatus[i].Value =0; + MagicStatus[i].Duration = 0; + MagicStatus[i].BuffTime = 0; + } + CharType = 0; + clientid = 0xffff; + nearChar = NULL; + findChar = NULL; +} + +//deconstructor +CCharacter::~CCharacter( ) +{ + delete Stats; + delete Position; + delete Battle; + delete Status; +} + +// Virtual +bool CCharacter::UpdateValues( ) +{ + return true; +} + +// update position +void CCharacter::UpdatePosition( ) +{ + float ntime; + clock_t etime = clock() - Position->lastMoveTime; + if(IsOnBattle( ) && Battle->target != 0) + { + CCharacter* Target = GetCharTarget( ); + if(Target == NULL) + ClearBattle( Battle ); + } + if(!IsMoving()) + { + if(IsPlayer())Position->lastMoveTime = clock(); + return; + } + float dx = Position->destiny.x - Position->current.x; + float dy = Position->destiny.y - Position->current.y; + float distance = sqrt( (dx*dx) + (dy*dy) ); + if(IsPlayer()) + { + + ntime = ( distance / (float(Stats->Move_Speed)/GServer->MOVE_SPEED_MOD)) * CLOCKS_PER_SEC; // calculates clock ticks to target position + //Log( MSG_DEBUG, "Dist: %f Speed: %i Mod: %i ntime: %f", distance, Stats->Move_Speed, GServer->MOVE_SPEED_MOD, ntime); + } + if(IsMonster()) + { + ntime = ( distance / (float(Stats->Move_Speed)/GServer->MOVE_SPEED_MOD)) * CLOCKS_PER_SEC; // calculates clock ticks to target position + Position->current.x = dx*(etime/ntime) + Position->current.x; + Position->current.y = dy*(etime/ntime) + Position->current.y; + + } + //float ntime = ( distance / Stats->Move_Speed * GServer->MOVE_SPEED_MOD ); // lmame method + /*if(IsPlayer()) + { + Log(MSG_DEBUG,"ntime = %f distance = %f ",ntime, distance ); + }*/ + + if(IsOnBattle() && distance <= Stats->Attack_Distance) //this will prevent the character from moving if in battle and in range + { + Position->destiny.x = Position->current.x; + Position->destiny.y = Position->current.y; + dx = 0; + dy = 0; + } + if (ntime <= etime || distance == 0) + { + /*if(IsPlayer()) + { + Log(MSG_DEBUG,"Movement timed out or target reached. Set current to destiny."); + }*/ + Position->current.x = Position->destiny.x; + Position->current.y = Position->destiny.y; + } + else + { + if(IsPlayer()) + { + /*{ + Log(MSG_DEBUG,"Still moving. Distance to target %f time to target %f", distance, ntime); + }*/ + Position->current.x = dx*(etime/ntime) + Position->current.x; + Position->current.y = dy*(etime/ntime) + Position->current.y; + } + } + /*if(IsPlayer()) + { + if(Position->current.x == Position->destiny.x) + { + Log(MSG_DEBUG,"Character is at destiny position in the X direction."); + } + else + { + Log(MSG_DEBUG,"Character is still moving. current x %f Destination x %f",Position->current.x,Position->destiny.x); + } + }*/ + Position->lastMoveTime = clock(); +} diff --git a/Src/World Server/character.h b/Src/World Server/character.h new file mode 100644 index 0000000..9ae3df4 --- /dev/null +++ b/Src/World Server/character.h @@ -0,0 +1,118 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef _ROSE_CHARACTER_ +#define _ROSE_CHARACTER_ +#include "chartype.h" +//#include "party.h" + +class CCharacter +{ + public: + CCharacter( ); + ~CCharacter( ); + + unsigned int clientid; + BYTE CharType; // 0 = undefined | 1 = player | 2 = monster | 3 = summon | 4 = NPC + CCharacter* nearChar; + CCharacter* findChar; + int damagecounter; + + STATS *Stats; + POSITION *Position; + BATTLE* Battle; + STATUS* Status; + MAGICSTATUS MagicStatus[30]; + int AIVar[20]; + + // Batle Functions + void DoAttack( ); + void NormalAttack( CCharacter* Enemy ); + bool SkillAttack( CCharacter* Enemy, CSkills* skill ); + bool BuffSkill( CCharacter* Target, CSkills* skill ); + bool AoeSkill( CSkills* skill, CCharacter* Enemy=NULL ); + bool AoeBuff( CSkills* skill ); + void UseBuffSkill( CCharacter* Target, CSkills* skill, bool deBuff = false ); + void UseBuffSkillQSD( CCharacter* Target, CSkills* skill, bool deBuff = false ); + + //void UseAtkSkill( CCharacter* Enemy, CSkills* skill ); + void UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff= false ); + int GetHitRate(CCharacter* pDEF); + int GetCritRate(); + int GetAtkDmg(CCharacter* pDEF, word wHitCNT, int iSuc); + int GetMgcDmg(CCharacter* pDEF, word wHitCNT, int iSuc); + + bool UseSkill( CSkills* skill, CCharacter *Target = NULL ); + //functions + bool IsMonster( ); + bool IsPlayer( ); + bool IsDead( ); + bool IsSummon( ); + bool IsOnBattle( ); + bool IsNPC( ); + void StartAction( CCharacter* Target, BYTE action, UINT skillid=0 ); + //void StartAction2( CCharacter* Target, BYTE action, UINT skillid=0, bool restart=false, CCharacter* receiver=NULL); + bool IsTargetReached( CCharacter* , CSkills* skill=NULL ); + bool CanAttack( ); + bool IsMoving( ); + CCharacter* GetCharTarget( ); + bool IsAttacking( ); + + // virtual functions + // Events + virtual bool OnBeAttacked( CCharacter* ); + virtual bool OnDie( ); + virtual bool OnEnemyDie( CCharacter* ); + virtual bool OnEnemyOnSight( CCharacter* ); + virtual bool OnSpawn( bool ); + virtual bool OnAlmostDie( ); + virtual bool OnFar( ); + + // Functions + virtual CDrop* GetDrop( ); + virtual void AddDamage( CCharacter* enemy, long int hitpower); + virtual void UpdatePosition( ); + virtual bool UpdateValues( ); + virtual void ReduceABC( ); + virtual int GetEVBuff( ); + void RefreshBuff( ); + virtual CParty* GetParty( ); + virtual CLAN* GetClan( ); + + // stats + virtual unsigned int GetAttackPower( ); + virtual unsigned int GetDefense( ); + virtual unsigned int GetXPRate( ); + virtual unsigned int GetItemDropRate( ); + virtual unsigned int GetItemDropCountRate( ); + virtual unsigned int GetDodge( ); + virtual unsigned int GetAccury( ); + virtual unsigned int GetMagicDefense( ); + virtual unsigned int GetMagicAttack( ); + virtual unsigned int GetCritical( ); + virtual unsigned int GetAttackSpeed( ); + virtual unsigned int GetMoveSpeed( ); + virtual unsigned int GetMaxHP( ); + virtual unsigned int GetMaxMP( ); + virtual float GetAttackDistance( ); + virtual unsigned int GetInt( ); +}; + +#endif diff --git a/Src/World Server/charevent.cpp b/Src/World Server/charevent.cpp new file mode 100644 index 0000000..be2b870 --- /dev/null +++ b/Src/World Server/charevent.cpp @@ -0,0 +1,63 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" + +//virtual [called when a enemy hit this character] +bool CCharacter::OnBeAttacked( CCharacter* Enemy ) +{ + return true; +} + +// virtual [called when this character die] +bool CCharacter::OnDie( ) +{ + return true; +} + +// virtual [called when enemy die] +bool CCharacter::OnEnemyDie( CCharacter* Enemy ) +{ + return true; +} + +// virtual [called when a enmy is on sigh] +bool CCharacter::OnEnemyOnSight( CCharacter* Enemy ) +{ + return true; +} + +// virtual [called when this character spawn] +bool CCharacter::OnSpawn( bool ) +{ + return true; +} + +// virtual [called when this character is almost dead] +bool CCharacter::OnAlmostDie( ) +{ + return true; +} + +// virtual [called when character is far] +bool CCharacter::OnFar( ) +{ + return true; +} diff --git a/Src/World Server/charfunctions.cpp b/Src/World Server/charfunctions.cpp new file mode 100644 index 0000000..3a5a8ee --- /dev/null +++ b/Src/World Server/charfunctions.cpp @@ -0,0 +1,606 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" +#include "worldserver.h" + +// return true if this character is monster +bool CCharacter::IsMonster( ) +{ + if(CharType == 2 || CharType == 3) + return true; + return false; +} + +// return true if this character is player +bool CCharacter::IsPlayer( ) +{ + if(CharType == 1) + return true; + return false; +} + +//return true if this character is an NPC +bool CCharacter::IsNPC( ) +{ + if(CharType == 4) + return true; + return false; +} + +// return true if HP is lower to 1 +bool CCharacter::IsDead( ) +{ + if(Stats->HP <= 0) + return true; + return false; +} + +// return true if this character is summon +bool CCharacter::IsSummon( ) +{ + if(this == NULL) return false; + if(CharType == 3)// this line is erroring. No idea why + return true; + return false; +} + +// Virtual function [return item drop] +CDrop* CCharacter::GetDrop( ) +{ + return NULL; +} + +// start action [attack] +void CCharacter::StartAction( CCharacter* Target, BYTE action, UINT skillid) +{ + + BEGINPACKET( pak, 0 ); + switch(action) + { + case NORMAL_ATTACK: + { + //Log( MSG_INFO, "case NORMAL_ATTACK"); + if(Target == NULL)return; + RESETPACKET( pak, 0x798 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, Stats->Move_Speed ); + ADDFLOAT ( pak, Target->Position->current.x * 100 ); + ADDFLOAT ( pak, Target->Position->current.y * 100 ); + Battle->target = Target->clientid; + Battle->atktarget = Target->clientid; + Battle->atktype = action; + Battle->skilltarget = 0; + Battle->bufftarget = 0; + Battle->skillid = 0; + Position->destiny = Target->Position->current; //move to intercept target + Position->lastMoveTime = clock(); + //Log(MSG_DEBUG,"Normal Attack set destiny X: %f Y: %f.",Position->current.x,Position->current.y); + } + break; + case SKILL_ATTACK: //Log( MSG_INFO, "case SKILL_ATTACK"); + case SKILL_BUFF: + { + if(Target == NULL)return; + RESETPACKET( pak, 0x7b3 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, Target->clientid ); + ADDWORD ( pak, skillid ); + ADDWORD ( pak, 0x0000 ); + ADDFLOAT ( pak, Target->Position->current.x * 100 ); + ADDFLOAT ( pak, Target->Position->current.y * 100 ); + Battle->target = Target->clientid; + + if(action == SKILL_ATTACK) + { + //Log( MSG_INFO, "case SKILL_ATTACK"); + Battle->skilltarget = Target->clientid; + Battle->bufftarget = 0; + } + else + { + //Log( MSG_INFO, "case SKILL_BUFF"); + Battle->bufftarget = Target->clientid; + Battle->skilltarget = 0; + } + + Battle->atktype = action; + Position->destiny = Target->Position->current; //move toward target + Battle->skillid = skillid; + Position->lastMoveTime = clock(); + //Log(MSG_DEBUG,"Skill Attack set destiny X: %f Y: %f.",Position->current.x,Position->current.y); + } + break; + case SKILL_AOE://Log( MSG_INFO, "case SKILL_AOE"); + case BUFF_SELF://Log( MSG_INFO, "case BUFF_SELF"); + case BUFF_AOE: + { + + //Log( MSG_INFO, "Battle set for skill %i Action = %i", skillid, action); + //Log( MSG_INFO, "case BUFF_AOE"); + RESETPACKET( pak, 0x7b2); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, skillid ); + Battle->atktype = action; + Battle->skillid = skillid; + Position->skilltargetpos.x = Position->current.x; + Position->skilltargetpos.y = Position->current.y; + //Log( MSG_INFO, "BUFF_AOE packet sent"); + } + break; + case AOE_TARGET: + { + //Log( MSG_INFO, "case AOE_TARGET"); + //Target is NULL for this function when cast by a player + //Target is NOT NULL when it comes from a monster + if(Target != NULL) //monster attack + { + Position->skilltargetpos.x = Target->Position->current.x; + Position->skilltargetpos.y = Target->Position->current.y; + } + RESETPACKET( pak, 0x7b4 ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, skillid ); + ADDFLOAT ( pak, Position->skilltargetpos.x * 100 ); + ADDFLOAT ( pak, Position->skilltargetpos.y * 100 ); + Battle->atktype = action; + Battle->skillid = skillid; + Battle->skilltarget = 0; + Battle->bufftarget = 0; + } + break; + default: + return; + break; + } + GServer->SendToVisible( &pak, this ); + Battle->contatk = true; + + //Log(MSG_INFO,"Battle Attacktype = %i",Battle->atktype); + return; +} + +bool CCharacter:: IsOnBattle( ) +{ + if(Battle->atktype == 0) + { + if(Battle->iscasting == 1) + { + clock_t etime = clock() - Battle->lastAtkTime; + if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed ) + { + //Log(MSG_DEBUG,"Iscasting is true while NOT in battle. Reset to false."); + Battle->iscasting = 0; + } + else + { + return false; + } + } + } + switch(Battle->atktype) + { + case 1: //NORMAL_ATTACK: + { + if(Battle->atktarget != 0) return true; + } + break; + case 2: //SKILL_ATTACK: + case 8: //AOE_TARGET: + { + if(Battle->skilltarget != 0 && Battle->skillid != 0) return true; + } + break; + case 3: //SKILL_BUFF: + { + if(Battle->bufftarget != 0 && Battle->skillid != 0) return true; + } + break; + case 4: //SKILL_AOE: + //case SKILL_SELF: //no such skill type + case 6: //BUFF_SELF: + case 7: //BUFF_AOE: + { + if(Battle->skillid != 0) return true; + } + break; + default: return false; + } +} + +bool CCharacter::CanAttack( ) +{ + + clock_t etime = clock() - Battle->lastAtkTime; + if(CharType == 4) //it's an npc + { + + //Log(MSG_DEBUG, "It's an NPC so set speed to 1"); + //always return true for an NPC + Stats->Attack_Speed = 1; + } + if (Stats->Attack_Speed < 1) + { + + Log( MSG_INFO, "weapon with no attackspeed."); + return false; + } + if(Battle->iscasting == 1) + { + if( etime < CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed ) //should make the delay 4 times longer + { + //Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime); + return false; + } + else + { + Log(MSG_INFO,"iscasting reset to 0 after time delay"); + Battle->iscasting = 0; + Battle->NextAtkTime = Battle->lastAtkTime + (CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed); + return true; + } + } + else //normal attack + { + if( etime < CLOCKS_PER_SEC * GServer->Config.AttkMult / Stats->Attack_Speed ) + { + //Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime); + return false; + } + } + Battle->NextAtkTime = Battle->lastAtkTime + (CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed); + return true; +} + +// return true if target is reached +bool CCharacter::IsTargetReached( CCharacter* Enemy, CSkills* skill ) +{ + CMap* map = GServer->MapList.Index[Position->Map]; + if(map == NULL) + return true; + float distance = GServer->distance( Position->current, Enemy->Position->current ); + + if(skill == NULL) //it's a normal attack + { + if(distance <= Stats->Attack_Distance) + { + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(istargetreached) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + return true; + } + else + { + Position->destiny = Enemy->Position->current; + //Log(MSG_DEBUG,"(istargetreached) Destiny set to Enemy position X: %f Y: %f.",Position->current.x,Position->current.y); + } + } + else // skill attack + { + float SkillRange = (float)skill->range; + if(SkillRange == 0) + { + SkillRange = Stats->Attack_Distance; + } + if(distance <= SkillRange) + { + Position->destiny = Position->current; + //Log(MSG_DEBUG,"(istargetreached skill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + return true; + } + else + { + Position->destiny = Enemy->Position->current; + //Log(MSG_DEBUG,"(istargetreached skill) Destiny set to Enemy position X: %f Y: %f.",Position->current.x,Position->current.y); + //Log(MSG_DEBUG,"(istargetreached skill) Distance: %f range: %i.", distance, SkillRange); + } + } + //Log(MSG_INFO,"(istargetreached) Cannot attack. Too far away"); + return false; +} + +// return true if the character is moving +bool CCharacter::IsMoving( ) +{ + if(Position->current.x == Position->destiny.x && Position->current.y == Position->destiny.y ) + return false; + return true; +} + +// VIRTUAL [add player damage] +void CCharacter::AddDamage( CCharacter* enemy, long int hitpower) +{ + +} + +// return character target +CCharacter* CCharacter::GetCharTarget( ) +{ + CCharacter* thistarget = GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->target ); + if (thistarget != NULL) + { + //Log(MSG_DEBUG, "getchartarget found target OK"); + return thistarget; + } + //Log(MSG_DEBUG, "getchartarget failed to find target"); + //if I don't currently have a target then return the character who just hit me + return GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->hitby ); +} + +// VIRTUAL [reduce ammon/bullets/cannons] +void CCharacter::ReduceABC( ) +{ + +} + +// VIRTUAL [get extra buff value] +int CCharacter::GetEVBuff( ) +{ + return 0; +} + +// VIRTUAL [return intelligence] +unsigned int CCharacter::GetInt( ) +{ + return 15; +} + + +// Check For Debuffs +void CCharacter::RefreshBuff( ) +{ + bool bflag = false; + for( UINT i=0;i<30;i++) + { + if(MagicStatus[i].Buff == 0) continue; + clock_t etime = clock() - MagicStatus[i].BuffTime; + if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC ) + { + switch(MagicStatus[i].Status) + { + case 18: // attack power up + case 19: // attack power down + case 48: // attack power up + if(i<15) + Status->Attack_up = 0xff; + else + Status->Attack_down = 0xff; + Stats->Attack_Power = GetAttackPower( ); + break; + case 20: // def up + case 21: // def down + case 49: // def up + if(i<15) + Status->Defense_up= 0xff; + else + Status->Defense_down = 0xff; + Stats->Defense = GetDefense( ); + break; + case 24: //accuracy up + case 25: //accuracy down + case 51: //attack accuracy up. + if(i<15) + Status->Accury_up= 0xff; + else + Status->Accury_down = 0xff; + Stats->Accury = GetAccury( ); + break; + case 22: // macic resistance up + case 23: // magic resistance down + case 50: // magic resistance up + if(i<15) + Status->Magic_Defense_up = 0xff; + else + Status->Magic_Defense_down = 0xff; + Stats->Magic_Defense = GetMagicDefense( ); + break; + case 28: //dodge up + case 29: //dodge down + case 53: //dodge rate up + if(i<15) + Status->Dodge_up = 0xff; + else + Status->Dodge_down = 0xff; + Stats->Dodge = GetDodge( ); + break; + case 14: //dash + case 15: //slow + case 46: //movement speed increased + if(i<15) + Status->Dash_up = 0xff; + else + Status->Dash_down = 0xff; + Stats->Move_Speed = GetMoveSpeed( ); + break; + case 16: // haste attack + case 17: // slow attack + case 47: // attack speed up + if(i<15) + Status->Haste_up = 0xff; + else + Status->Haste_down = 0xff; + Stats->Attack_Speed = GetAttackSpeed( ); + break; + case 26: // crit up + case 27: // crit down + case 52: // crit up + if(i<15) + Status->Critical_up = 0xff; + else + Status->Critical_down = 0xff; + Stats->Critical = GetCritical( ); + break; + case 12: // max HP up + case 44: // max HP up + if(i<15) + Status->HP_up = 0xff; + else + Status->HP_down = 0xff; + Stats->MaxHP = GetMaxHP( ); + break; + case 13: // max MP up + case 45: // max MP up + if(i<15) + Status->MP_up = 0xff; + else + Status->MP_down = 0xff; + Stats->MaxMP = GetMaxMP( ); + break; + case 32: // faint + Status->Stuned = 0xff; + Status->CanAttack = true; + //printf("removing stun\n"); + break; + case 7: case 8: case 9: case 10: case 11: //poisoned + Status->Poisoned = 0xff; + //printf("removing poison\n"); + break; + case 30: // muted + Status->Muted = 0xff; + Status->CanCastSkill = true; + break; + case 31: // sleep May need to be fixed later to accomodate multiple status effects. + Status->Sleep = 0xff; + Status->CanMove = true; + Status->CanCastSkill = true; + Status->CanAttack = true; + break; + case 54: //A_GMExtra_Damage: + case 36: //A_Extra_Damage: + if(i<15) + { + Status->ExtraDamage_up = 0xff; + Stats->ExtraDamage = 0xff; + } + else + { + Status->ExtraDamage_down = 0xff; + Stats->ExtraDamage = 0xff; + } + + break; + case 56: // Taunt + Status->Taunt = 0xff; + break; + default: + break; + } + + MagicStatus[i].Status = 0; + MagicStatus[i].Buff = 0; + MagicStatus[i].BuffTime = 0; + MagicStatus[i].Duration = 0; + MagicStatus[i].Value = 0; + bflag = true; + } + else if (MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11 && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds + { + Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself + MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC; + MagicStatus[i].Duration -= 1; + //printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); + + //A bunch of messy code to send dmg packet + BEGINPACKET( pak, 0x7b6 ); + ADDWORD ( pak, clientid ); // client taking damage + ADDWORD ( pak, 0 ); // client doing damage + ADDDWORD ( pak, 0x000007f8 ); + ADDBYTE ( pak, 0x00 ); + if (Stats->HP < 1) + { + ADDWORD ( pak, MagicStatus[i].Status + 0x8000 ); + } + else + { + ADDWORD ( pak, MagicStatus[i].Status + 0x2000 ); + } + //ADDDWORD ( pak, MagicStatus[i].Status ); //damage amount + + //If poisoned entity (player or monster) is killed + if( IsDead()) + { + //printf("char died\n"); + Log( MSG_INFO, "found dead monster"); + CDrop* thisdrop = NULL; + CMap* map = GServer->MapList.Index[thisdrop->posMap]; + ADDDWORD ( pak, 16 ); + if( !IsSummon( ) && !IsPlayer( )) + { + thisdrop = GetDrop( ); + if( thisdrop != NULL) + { + map->AddDrop( thisdrop ); + } + } + GServer->SendToVisible( &pak, this ); // removed thisdrop from the packet + //kill the monster if its HP is less than 0 + BEGINPACKET( pak1, 0x799 ); + ADDWORD ( pak1, clientid ); + ADDWORD ( pak1, clientid ); + ADDDWORD ( pak1, 0x8005 ); + GServer->SendToVisible( &pak1, this ); + } + + //If enemy is still alive + else + { + ADDDWORD ( pak, 4 ); + GServer->SendToVisible( &pak, this ); + } + } + } + if(bflag) + { + BEGINPACKET( pak,0x7b7 ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, GServer->BuildBuffs( this ) ); + GServer->SendToVisible( &pak, this ); + } +} + +// VIRTUAL [return party pointer] +CParty* CCharacter::GetParty( ) +{ + return NULL; +} + +// VIRTUAL [return clan pointer] +CLAN* CCharacter::GetClan( ) +{ + return NULL; +} + +bool CCharacter::IsAttacking( ) +{ + if(IsOnBattle( )) + { + switch(Battle->atktype) + { + case SKILL_BUFF: + case BUFF_SELF: + case BUFF_AOE: + return false; + default: + return false; + break; + } + } + else return false; +} diff --git a/Src/World Server/charstats.cpp b/Src/World Server/charstats.cpp new file mode 100644 index 0000000..0be8f9c --- /dev/null +++ b/Src/World Server/charstats.cpp @@ -0,0 +1,111 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "character.h" + +// virtual [return attackpower] +unsigned int CCharacter::GetAttackPower( ) +{ + return 0; +} + +// virtual [return defense] +unsigned int CCharacter::GetDefense( ) +{ + return 0; +} + +// virtual [return dodge] +unsigned int CCharacter::GetDodge( ) +{ + return 0; +} + +// virtual [return xprate] +unsigned int CCharacter::GetXPRate( ) +{ + return 0; +} + +// virtual [return item drop rate] +unsigned int CCharacter::GetItemDropRate( ) +{ + return 0; +} + +// virtual [return item drop rate] +unsigned int CCharacter::GetItemDropCountRate( ) +{ + return 0; +} + +// virtual [return accury] +unsigned int CCharacter::GetAccury( ) +{ + return 0; +} + +// virtual [return magic defense] +unsigned int CCharacter::GetMagicDefense( ) +{ + return 0; +} + +// virtual [return magic attack] +unsigned int CCharacter::GetMagicAttack( ) +{ + return 0; +} + +// virtual [return critical] +unsigned int CCharacter::GetCritical( ) +{ + return 0; +} + +// virtual [return attackspeed] +unsigned int CCharacter::GetAttackSpeed( ) +{ + return 0; +} + +// virtual [return movespeed] +unsigned int CCharacter::GetMoveSpeed( ) +{ + return 0; +} + +// virtual [return maxhp] +unsigned int CCharacter::GetMaxHP( ) +{ + return 0; +} + +// virtual [return maxmp] +unsigned int CCharacter::GetMaxMP( ) +{ + return 0; +} + +// virtual [return attackdistance] +float CCharacter::GetAttackDistance( ) +{ + return 0; +} diff --git a/Src/World Server/chartype.h b/Src/World Server/chartype.h new file mode 100644 index 0000000..16fe4e3 --- /dev/null +++ b/Src/World Server/chartype.h @@ -0,0 +1,303 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef _CHARACTER_TYPE_ +#define _CHARACTER_TYPE_ +#define MAX_STORAGE 160 +#define MAX_INVENTORY 140 +#define MAX_BASICSKILL 42 +#define MAX_QUICKBAR 32 +#define ClearBattle(i) { i->target=0;i->atktarget=0; i->bufftarget=0; i->skilltarget=0; i->skillid=0; i->atktype=0; i->hitby=0; i->iscasting=0; } +typedef unsigned char BYTE; +#include "datatypes.h" + +#define TPLAYER 1 +#define TMONSTER 2 +#define TSUMMON 3 +#define TNPC 4 + +//**************** SHARED ****************// +struct POSITION +{ + fPoint source; // source position + fPoint current; // current position + fPoint destiny; // destiny position + fPoint skilltargetpos; + unsigned int Map; // current map + unsigned int respawn; // respawn id [player = respawnid | monster = spawnid] + unsigned int saved; // saved id [player town | monster = 0] + clock_t lastMoveTime; +}; + +struct STATS +{ + int Level; + long int HP; + long int MP; + unsigned int MaxHP; + unsigned int MaxMP; + unsigned int stance; + + unsigned int Attack_Power; + unsigned int Defense; + unsigned int Critical; + unsigned int Dodge; + unsigned int Accury; + unsigned int Magic_Defense; + unsigned int Move_Speed; + unsigned int Mspd_base; + unsigned int Attack_Speed; + float Attack_Distance; + unsigned int MaxWeight; + unsigned int MaxSummonGauge; + unsigned int MPReduction; + unsigned int ExtraDamage; + unsigned int xprate; + unsigned int itemdroprate; + unsigned int itemdropcountrate; + unsigned int zulydroprate; + unsigned int greydrop; + unsigned int magicattack; + int ItemXPRate; + int ItemDropRate; + clock_t ItemXPTimer; + unsigned int ItemXPTime; + clock_t ItemDropRateTimer; + +}; + +struct BATTLE +{ + unsigned int target; + unsigned int atktarget; + unsigned int bufftarget; + unsigned int skilltarget; + unsigned int skillid; + unsigned int atktype; + unsigned int hitby; + unsigned int iscasting; + bool contatk; + clock_t lastAtkTime; + clock_t castTime; + clock_t NextAtkTime; +}; + +struct STATUS +{ + //Stats up + BYTE Dash_up; + BYTE Haste_up; + BYTE Attack_up; + BYTE Defense_up; + BYTE Magic_Defense_up; + BYTE Accury_up; + BYTE Critical_up; + BYTE Dodge_up; + + BYTE HP_up; + BYTE MP_up; + BYTE MaxHP_up; + BYTE MaxMP_up; + BYTE HPMP_up; + //Stats down + BYTE Dash_down; + BYTE Haste_down; + BYTE Attack_down; + BYTE Defense_down; + BYTE Magic_Defense_down; + BYTE Accury_down; + BYTE Critical_down; + BYTE Dodge_down; + BYTE HP_down; + BYTE MP_down; + BYTE MaxHP_down; + BYTE MaxMP_down; + BYTE HPMP_down; + + //Status Effects + BYTE Poisoned; + BYTE Muted; + BYTE Sleep; + BYTE Faint; + BYTE Stuned; + BYTE Camo; + BYTE Invis; + BYTE Shield; + BYTE Curse; + BYTE Recover; + BYTE Dispell; + BYTE Detect; + BYTE Invinc; + BYTE Flame; + BYTE ExtraDamage_up; + BYTE ExtraDamage_down; + BYTE Taunt; + bool CanAttack; + bool CanRun; + bool CanMove; + bool CanCastSkill; + bool IsTowerDef; + bool nightonly; + bool dayonly; + UINT spawnid; + + // Stance + BYTE Stance; +}; + +struct MAGICSTATUS +{ + unsigned int Status; // Store the skill Status + unsigned int Buff; // Store the buff type + unsigned int Value; // Buff Value + unsigned int Duration; // Skill Duration + clock_t BuffTime; // Buff Time +}; + +//**************************************************************/ +//***************Player only******************************************/ +struct ATTRIBUTES +{ + unsigned int Str; + unsigned int Dex; + unsigned int Int; + unsigned int Con; + unsigned int Cha; + unsigned int Sen; + + unsigned int Estr; + unsigned int Edex; + unsigned int Eint; + unsigned int Econ; + unsigned int Echa; + unsigned int Esen; + +}; + +struct USEDITEM +{ + clock_t lastRegTime; // Last Regeneration + UINT usevalue; // Max Heal Value + UINT usetype; // Consumible Type (MP/HP/STAMINA/...) + UINT userate; // consumible rate + UINT used; // Consumible Used +}; + +// Player information +struct INFO +{ + char charname[17]; + unsigned int charid; + BYTE Sex; + UINT Face; + UINT Hair; + UINT Exp; + UINT Pending_Exp; //temporary storage space for exp during calculations + UINT Job; + long int Zulies; + long int Storage_Zulies; + long int LastGlobal; + int StatPoints; + int SkillPoints; + unsigned int stamina; + UINT IsGM; + UINT IsDev; + //int xprate +}; + +struct SESSION +{ + unsigned int userid; + char username[17]; + char password[33]; + int accesslevel; + bool isLoggedIn; + bool inGame; + UINT Respawned; + BYTE RespawnChoice; + bool codedebug; + UINT KTPoints; + UINT NewPoints; + UINT logtime; // tracks total log points. 1 point = 1 hour. 5 points = 1KTPoint + clock_t LogTime; // time for the current session + UINT TotalLogTime; + UINT award; +}; + +struct RIDE +{ + bool Drive; + bool Ride; + UINT charid; +}; + +struct TRADE +{ + unsigned int short trade_itemid[0xa]; + unsigned int trade_count[0xb]; + unsigned int trade_status; + unsigned int trade_target; +}; + +struct PARTY +{ + class CParty* party; + bool IsMaster; +}; + +struct SHOP +{ + bool open; + char name[64]; + CBuying BuyingList[30]; + CSelling SellingList[30]; + UINT Buying; + UINT Selling; + UINT ShopType; +}; + +struct SKILLS +{ + unsigned id; + unsigned level; + CSkills* thisskill; +}; + +struct CLAN +{ + unsigned int clanid; + unsigned int clanrank; + unsigned int grade; + unsigned int CP; + unsigned int logo; + unsigned int back; + char clanname[17]; +}; + +struct UNION_s +{ + UINT unionvar[11]; +}; + + +//***************Monster only******************************************/ + +#endif + diff --git a/Src/World Server/clan.cpp b/Src/World Server/clan.cpp new file mode 100644 index 0000000..344af06 --- /dev/null +++ b/Src/World Server/clan.cpp @@ -0,0 +1,257 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Clan Manager +bool CWorldServer::pakClanManager ( CPlayer* thisclient, CPacket* P ) +{ + int action = GETBYTE((*P),0); + //Log(MSG_DEBUG, "Clan action %i", action ); + switch(action) + { + case 0xfa://new member added + { + MYSQL_ROW row; + int charid = GETWORD((*P),1); + int clanid = GETWORD((*P),3); + Log(MSG_INFO, "Adding member %d to clan %d", charid, clanid); + CPlayer* otherclient = GetClientByCID ( charid ); + if(otherclient==NULL) { + Log(MSG_ERROR, "Char with id %d doesn't exist", charid); + return true; + } + MYSQL_RES *result = DB->QStore("SELECT logo,back,name,grade FROM list_clan where id = %i", clanid); + if(result==NULL) + { + Log(MSG_INFO, "NULL result from query to list_clan"); + return true; + } + if(mysql_num_rows(result)!=1) + { + Log(MSG_WARNING, "Invalid clan %i", clanid ); + DB->QFree( ); + return true; + } + row = mysql_fetch_row(result); + otherclient->Clan->logo = atoi(row[0]); + otherclient->Clan->back = atoi(row[1]); + strcpy(otherclient->Clan->clanname,row[2]); + otherclient->Clan->grade = atoi(row[3]); + DB->QFree( ); + otherclient->Clan->clanid=clanid; + otherclient->Clan->clanrank=1; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 );//funcion + ADDWORD ( pak, otherclient->clientid );//client id + ADDWORD ( pak, clanid );//? + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, otherclient->Clan->back ); + ADDWORD ( pak, otherclient->Clan->logo ); + ADDBYTE ( pak, otherclient->Clan->grade ); + ADDBYTE ( pak, otherclient->Clan->clanrank ); + ADDSTRING ( pak, otherclient->Clan->clanname ); + ADDBYTE ( pak, 0x00 ); + SendToVisible( &pak, otherclient ); + } + break; + case 0xfb://Member Kicked + { + char nick[30]; + memcpy( nick, &P->Buffer[1], P->Size ); + CPlayer* otherclient = GetClientByCharName( nick ); + if(otherclient!=NULL) + { + otherclient->Clan->clanid=0; + otherclient->Clan->clanrank=1; + otherclient->Clan->back=0; + otherclient->Clan->logo=0; + otherclient->Clan->grade=0; + strcpy(otherclient->Clan->clanname,""); + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, otherclient->clientid ); + ADDQWORD ( pak, 0 ); + ADDWORD ( pak, 0x0001 ); + SendToVisible( &pak, otherclient ); + } + } + break; + case 0xfc://member change rank + { + char nick[30]; + int newrank = GETBYTE((*P),1); + memcpy( nick, &P->Buffer[2], P->Size ); + CPlayer* otherclient = GetClientByCharName( nick ); + if(otherclient!=NULL) + { + otherclient->Clan->clanrank = newrank; + } + } + break; + case 0xfd://disorg + { + unsigned int clanid = GETWORD((*P),1); + unsigned int charid = GETWORD((*P),3); + CPlayer* tclient = GetClientByCID( charid ); + if(tclient == NULL) + return true; + tclient->Clan->clanid = 0; + tclient->Clan->clanrank = 1; + tclient->Clan->grade = 0; + tclient->Clan->back = 0; + tclient->Clan->logo = 0; + memset( &tclient->Clan->clanname, '\0', 17 ); + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, tclient->clientid ); + ADDQWORD ( pak, 0 ); + ADDWORD ( pak, 0x0001 ); + SendToVisible( &pak, tclient ); + } + break; + case 0xfe://Member Leave + { + char nick[17]; + memcpy( nick, &P->Buffer[1], P->Size ); + CPlayer* otherclient = GetClientByCharName(nick); + if(otherclient!=NULL) + { + otherclient->Clan->clanid=0; + otherclient->Clan->clanrank=0; + otherclient->Clan->back=0; + otherclient->Clan->logo=0; + otherclient->Clan->grade=0; + strcpy(otherclient->Clan->clanname,""); + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, otherclient->clientid ); + ADDQWORD ( pak, 0 ); + ADDWORD ( pak, 0x0001 ); + SendToVisible( &pak, otherclient ); + } + } + break; + case 0xff: // update clan mark + { + unsigned int clanid = GETWORD((*P),1); + unsigned int clanlogo = GETDWORD((*P), 3 ); + for(unsigned int i=0;iplayer==NULL) continue; + CPlayer* player = (CPlayer*)ClientList.at(i)->player; + if(player->Clan->clanid==clanid) + { + player->Clan->back = 0; + player->Clan->logo = clanlogo; + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 );//funcion + ADDWORD ( pak, player->clientid );//cleint id + ADDWORD ( pak, clanid ); + ADDWORD ( pak, 0x0000 );//? + ADDWORD ( pak, player->Clan->back );//? + ADDWORD ( pak, player->Clan->logo );//? + ADDBYTE ( pak, player->Clan->grade ); + ADDBYTE ( pak, player->Clan->clanrank ); + ADDSTRING ( pak, player->Clan->clanname ); + ADDBYTE ( pak, 0x00 ); + SendToVisible( &pak, player ); + } + } + } + break; + default: + Log( MSG_INFO, "Clan manager unknow action %i", action ); + } + return true; +} + +// Create a new clan +bool CWorldServer::pakCreateClan ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->CharInfo->Zulies<1000000) + return true; + thisclient->CharInfo->Zulies -= 1000000; + MYSQL_ROW row; + int background = GETWORD((*P),1); + int icon = GETWORD((*P),3); + const char *name = ""; + const char *slogan = ""; + name=(char*)&P->Buffer[5]; + slogan=(char*)&P->Buffer[strlen(name)+6]; + + //Check if name already exists + MYSQL_RES *result = DB->QStore("SELECT name FROM list_clan WHERE name='%s'", name); + if(result==NULL) return true; + if ( mysql_num_rows( result ) > 0 ) + { + BEGINPACKET( pak, 0x07e0 ); + ADDWORD ( pak, 0x42 ); + thisclient->client->SendPacket( &pak ); + DB->QFree( ); + return true; + } + DB->QFree( ); + + //Check if user can create a clan + if( thisclient->Clan->clanid != 0 ) + { + BEGINPACKET( pak, 0x07e0 ); + ADDWORD ( pak, 0x44 ); + thisclient->client->SendPacket( &pak ); + return true; + } + + //Add clan to sql table + DB->QExecute( "INSERT into list_clan (logo,back,name,cp,grade,slogan,news) values (%i,%i,'%s',0,1,'%s','')", + icon,background,name,slogan); + thisclient->Clan->clanid = mysql_insert_id(DB->Mysql); + thisclient->Clan->clanrank = 6; + thisclient->Clan->logo = icon; + thisclient->Clan->back = background; + strncpy(thisclient->Clan->clanname,name,16); + thisclient->Clan->grade = 1; + + //Update user clan information + DB->QExecute( "UPDATE characters set clanid=%i,clan_rank=6 where id=%i", + thisclient->Clan->clanid,thisclient->CharInfo->charid); + //load clan info in char server + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0xfa ); //action to update clan informacion (charserver) + ADDWORD ( pak, thisclient->Clan->clanid ); + ADDWORD ( pak, thisclient->CharInfo->charid ); + ADDWORD ( pak, thisclient->clientid ); + SendISCPacket( &pak ); + + //Send to other players + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisclient->Clan->clanid); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, background ); + ADDWORD ( pak, icon ); + ADDBYTE ( pak, 0x01 );//clan grade + ADDBYTE ( pak, 0x06 );//clan rank + ADDSTRING ( pak, name ); + ADDBYTE ( pak, 0x00 ); + SendToVisible( &pak, thisclient ); + return true; +} diff --git a/Src/World Server/datatypes.h b/Src/World Server/datatypes.h new file mode 100644 index 0000000..5db59b1 --- /dev/null +++ b/Src/World Server/datatypes.h @@ -0,0 +1,1414 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef __ROSE_DATATYPES__ +#define __ROSE_DATATYPES__ + +//*upgraded status +//#define NULL 2 +#define STRENGTH 10 +#define DEXTERY 11 +#define INTELIGENCE 12 +#define CONCENTRATION 13 +#define CHARM 14 +#define SENSE 15 +#define C_HP 16 +#define C_MP 17 +#define ATTACK_POWER 18 +#define ACCURY 20 +#define MAGIC_RESISTENCE 21 +#define DODGE 22 +#define MOVE_SPEED 23 +#define ATTACK_SPEED 24 +#define INV_CAPACITY 25 +#define CRITICAL 26 +#define XP_RATE 30 +#define EXTRA_DAMAGE 36 +#define ONE_HAND_WEAP_AP 42 +#define TWO_HAND_WEAP_AP 43 +#define MOV_SPEED 52 +#define DEFENSE 53 +#define MAX_HP 54 +#define MAX_MP 55 +#define HP_REC_AMONT 56 +#define MP_REC_RATE 57 +#define BAGPACK_CAPACITY 58 +#define MP_COST_RED 61 +#define SUMMON_GAUGE 62 +#define ITEM_DROP_RATE 63 +#define ZULY_DROP_RATE 64 +#define ATK_ACCURACY 99 +#define SHIELD_DEFENSE 102 +#define ONE_HAND_SWORD_AP 104 +#define ONE_HAND_MELEE_AP 105 +#define TWO_HAND_SWORD_AP 106 +#define TWO_HAND_SPEAR_AP 107 +#define TWO_HAND_AXE_AP 108 +#define BOW_AP 109 +#define GUN_AP 110 +#define LAUNCHER_AP 111 +#define STAFF_POWER 113 +#define WAND_POWER 114 +#define KATAR_AP 115 +#define DUALSWORD_AP 116 +#define BOW_ATK_SPD 123 +#define GUN_ATK_SPD 124 +#define LAUNCHER_ATK_SPD 125 +#define KATAR_ATK_SPD 129 +#define DUALSWORD_ATK_SPD 130 + +//*status/condition +#define A_STR 10 +#define A_DEX 11 +#define A_INT 12 +#define A_CON 13 +#define A_CHA 14 +#define A_SEN 15 +#define A_HP 16 +#define A_MP 17 +#define A_ATTACK 18 +#define A_DEFENSE 19 +#define A_ACCUR 20 +#define A_MRESIST 21 +#define A_DODGE 22 +#define A_DASH 23 +#define A_HASTE 24 +#define A_INVENTORY_CAPACITY 25 +#define A_CRITICAL 26 +#define A_HP_REC_RATE 27 +#define A_MP_REC_RATE 28 +#define A_MP_CONSUMATION 29 +#define A_Extra_Damage 36 +#define A_MAX_HP 38 +#define A_MAX_MP 39 +#define A_GMExtra_Damage 54 +#define A_STUN 40 +#define A_MUTE 41 +#define A_POISON 42 + + +//*spawn values +//BYTE1 +#define UNK1 0x01 +#define MAX_HP_MP 0x02//revizar +#define POISONED 0x04 +#define UNK2 0x08 +#define MP_UP 0x10//revizar +#define HP_UP 0x20//revizar +#define DASH_UP 0x40 +#define DASH_DOWN 0x80 +//BYTE2 +#define HASTE_UP 0x01 +#define HASTE_DOWN 0x02 +#define ATTACK_UP 0x04 +#define ATTACK_DOWN 0x08 +#define DEFENSE_UP 0x10 +#define DEFENSE_DOWN 0x20 +#define MDEFENSE_UP 0x40 +#define MDEFENSE_DOWN 0x80 +//BYTE3 +#define HITRATE_UP 0x01 +#define HITRATE_DOWN 0x02 +#define CRITICAL_UP 0x04 +#define CRITICAL_DOWN 0x08 +#define DODGE_UP 0x10 +#define SUMMON 0x20 +#define MUTED 0x40 +#define SLEEPING 0x80 +//BYTE4 +#define STUN 0x01 +#define INVISIBLE_1 0x02 +#define INVISIBLE_2 0x04 +#define UNK3 0x08 +#define DAMAGE_UP 0x10 +#define SUMMON 0x20 +#define UNK4 0x40 +#define UNK5 0x80 + +//Stance +#define mWALKING 0 +#define mRUNNING 1 +#define ATTACKING 1 +#define WALKING 2 +#define RUNNING 3 +#define DRIVING 4 +#define SITTING 5 + +//Target Type +#define T_MONSTER 1 +#define T_PLAYER 2 +#define T_SELF 3 +#define T_PARTY 4 +#define T_OTHER 5 + +// attack type +#define NORMAL_ATTACK 1 +#define SKILL_ATTACK 2 +#define SKILL_BUFF 3 +#define SKILL_AOE 4 +#define SKILL_SELF 5 +#define BUFF_SELF 6 +#define BUFF_AOE 7 +#define AOE_TARGET 8 + +// other constants +#define MAX_SKILL 90 +#define SKILL_DELAY 500 //ms + +// weapon type +#define ONE_HAND_SWORD 211 +#define ONE_HAND_BUNT_SWORD 212 +#define TWO_HAND_SWORD 221 +#define SPEAR 222 +#define TWO_HAND_AXE 223 +#define BOW 231 +#define GUN 232 +#define LAUNCHER 233 +#define STAFF 241 +#define WAND 242 +#define KATAR 251 +#define DOUBLE_SWORD 252 +#define DUAL_GUN 253 +#define CROSSBOW 271 + +//equip type +#define MASK 1 +#define CAP 2 +#define BODY 3 +#define GLOVE 4 +#define SHOE 5 +#define BACK 6 +#define JEWEL 7 +#define WEAPON 8 +#define SUBWEAPON 9 +#define CONSUMIBLE 10 +#define JEM 11 +#define NATURAL 12 +#define QUEST 13 +#define PAT 14 + +//time +#define DAY 0 +#define MORNING 1 +#define EVENING 2 +#define NIGHT 3 + +// Quests + +#define sIsSummon 1 +#define sGender 2 +#define sBirthStone 3 +#define sJob 4 +#define sUnion 5 +#define sUnionPoints 83 +#define sRanking 6 +#define sReputation 7 +#define sFace 8 +#define sHair 9 + +#define sStrength 10 +#define sDexterity 11 +#define sIntelligence 12 +#define sConcentration 13 +#define sCharm 14 +#define sSensibility 15 + +#define sHP 16 +#define sMP 17 + +#define sAttackPower 18 +#define sDefense 19 +#define sAccuracy 20 +#define sMagicResistance 21 +#define sDodge 22 +#define sMoveSpeed 23 +#define sAttackSpeed 24 +#define sMaxWeight 25 +#define sCritical 26 + +#define sHPRate 27 +#define sMPRate 28 +#define sMPDecrease 29 + +#define sEXPRate 30 +#define sLevel 31 +#define sStatPoints 32 +#define sTendency 33 +#define sPvp 34 +#define sHeadSize 35 +#define sBodySize 36 +#define sSkillPoints 37 + +#define sMaxHP 38 +#define sMaxMP 39 + +#define sMoney 40 +#define sUnarmedAtkPow 41 +#define sOneHandAtkPow 42 +#define sTwoHandAtkPow 43 +#define sBowAtkPow 44 +#define sGunAtkPow 45 +#define sMagicAtkPow 46 +#define sCrossbowAtkPow 47 +#define sCombatAtkPow 48 + +#define sBowAtkSpd 49 +#define sGunAtkSpd 50 +#define sCombatAtkSpd 51 + +#define sMoveSpeed2 52 //Passive +#define sDefense2 53 //Passive + +#define sMaxHP2 54 //Passive +#define sMaxMP2 55 //Passive +#define sHPRecovery 56 +#define sMPRecovery 57 + +#define sMaxWeight2 58 +#define sBuyDiscount 59 +#define sSalesPremium 60 +#define sMPSpendDecrease 61 +#define sExpandSummonGauge 62 +#define sItemDropIncrease 63 +#define sIncreaseSummonGauge 63 + +#define sCurrentPlanet 75 +#define sStamina 76 + +#define sClanPoints 92 + + + +#define sNoStorageCharge 93 +#define sExpandStorage 95 +#define sRemodelShop 96 +#define sCartGauge 97 + +// Status Type +#define sNone 0 +#define sPoison1 7 +#define sPoison2 8 +#define sPoison3 9 +#define sPoison4 10 +#define sPoison5 11 +#define sMaxHPUp 12 +#define sMaxMPUp 13 +#define sDash 14 +#define sSlow 15 +#define sHasteAtk 16 +#define sSlowAtk 17 +#define sAtkUp 18 +#define sAtkDown 19 +#define sDefUp 20 +#define sDefDown 21 +#define sMResUp 22 +#define sMResDown 23 +#define sAccUp 24 +#define sAccDown 25 +#define sCritUp 26 +#define sCritDown 27 +#define sDodgeUp 28 +#define sDodgeDown 29 +#define sMute 30 +#define sSleep 31 +#define sFaint 32 +#define sCamo 33 +#define sInvis 34 +#define sShield 35 +#define sAddDmg 36 +#define sCurse 37 +#define sRecover 38 +#define sDispell 39 +#define sHPMPRest 40 +#define sHPMPRest2 41 +#define sHPMPRest3 42 + +#define sHPDown 43 +#define sMaxHPUp2 44 +#define sMaxMPUp2 45 +#define sDash2 46 +#define sHasteAtk2 47 +#define sAtkUp2 48 +#define sDefUp2 49 +#define sMResUp2 50 +#define sAccUp2 51 +#define sCritUp2 52 +#define sDodgeUp2 53 +#define sAddDmg2 54 +#define sDetect 55 +#define sTaunt 56 +#define sInvinc 57 +#define sFlame 58 +#define sDefDown2 59 +#define sSlow2 60 +#define sFlame2 61 +#define sHPRes100 62 +#define sHPRes200 63 +#define sHPRes300 64 +#define sHPRes40 65 +#define sHPRes80 66 +#define sHPRes120 67 +#define sMPRes40 68 +#define sMPRes80 69 +#define sMPRes120 70 +#define sMPRes10 71 +#define sMPRes15 72 +#define sMPRes20 73 +#define sFlame3 74 +#define sHPRes160 75 +#define sMPRes25 76 +#define sFlame4 77 +#define sFlame5 78 +#define sFlame6 79 +#define sFlame7 80 + + +// Skills - Classes +#define cVisitor 0 +#define cSoldier 1 +#define cMuse 2 +#define cHawker 3 +#define cDealer 4 +#define cKnight 11 +#define cChampion 12 +#define cMage 13 +#define cCleric 14 +#define cRaider 15 +#define cScout 16 +#define cBourgeois 17 +#define cArtisan 18 +#define cSoldierJob 41 +#define cMuseJob 42 +#define cHawkerJob 43 +#define cDealerJob 44 +#define cFirstJob 46 +#define cSecondJob 47 +#define cThirdJob 48 +#define cSoldier2nd 51 +#define cMuse2nd 52 +#define cHawker2nd 53 +#define cDealer2nd 54 +#define cSoldier3rd 56 +#define cMuse3rd 57 +#define cHawker3rd 58 +#define cDealer3rd 59 +#define cKnightJob 61 +#define cChampionJob 62 +#define cMageJob 63 +#define cClericJob 64 +#define cRaiderJob 65 +#define cScoutJob 66 +#define cBourgeoisJob 67 +#define cArtisanJob 68 +#define cSoldierHawker 71 + +// Skills - Types +#define tBasic 1 +#define tCraft 2 +#define tDamage 3 // Seen (Start) +#define tWeaponUp 4 +#define tWeaponUp2 5 +#define tSpell 6 // Seen (Start) (This one is odd, an AOE skill, that uses Start) + +#define tAOEDamage 7 // Seen (AOE) +#define tContinueSelf2 8 // Seen (Self) +#define tContinuing2 9 +#define tRecoverSelf 10 // Seen (Self) +#define tRecover 11 // Seen (Start) +#define tContinueSelf 12 // Seen (Self) +#define tContinue 13 +#define tSummon 14 +#define tPassive 15 +#define tEmotion 16 +#define tAOEAttackSelf 17 // Seen (Self) +#define tReservation 18 +#define tAbsorb 19 +#define tResurrect 20 + +// Skills - Targets +#define tYourself 0 +#define tPartyMember 1 +#define tClanMember 2 +#define tAlly 3 +#define tMonster 4 +#define tHostileCharacter 5 +#define tHostileTeamMember 6 +#define tAllCharacters 7 +#define tAllMembers 8 +#define tFaintedAlly 9 +#define tHostileMonster 10 + +// Stats calc - Jobs +#define jVisitor 0 +#define jSoldier 111 +#define jKnight 121 +#define jChampion 122 +#define jMuse 211 +#define jMage 221 +#define jCleric 222 +#define jHawker 311 +#define jRaider 321 +#define jScout 322 +#define jDealer 411 +#define jBourgeois 421 +#define jArtisan 422 + + + + +#include "../common/sockets.h" + +//LTB strings +struct CLTBstring +{ + char LTBstring[200]; + char NPCname[50]; + char QuestNpcName[50]; + char QuestLTB[200]; +}; + +// Hold party experience when kill a moster +struct CPartyExp +{ + class CParty* thisparty; + UINT exp; + UINT num; + UINT partymember[50]; + UINT maxlevel; + bool flag; +}; + +// Equip information +struct CEquip +{ + UINT id; + char name[50]; + UINT itemtype; + UINT equiptype; + UINT type; + UINT price; + UINT pricerate; + UINT weight; + UINT quality; + UINT level; + UINT material; + UINT occupation[4]; + UINT condition1[3]; + UINT condition2[3]; + UINT stat1[3]; + UINT stat2[3]; + UINT durability; + UINT defense; + UINT magicresistence; + UINT attackdistance; + UINT attackpower; + UINT attackspeed; + UINT magicattack; + UINT refinetype; + UINT itemgrade; + UINT movespeed; + UINT raretype; + UINT unionpoint; + UINT breakid; +}; + +// Jem Data +struct CJemData +{ + UINT id; + UINT itemtype; + UINT type; + UINT price; + UINT pricerate; + UINT weight; + UINT quality; + UINT material; + UINT stat1[2];/**/ + UINT stat2[2];/**/ + UINT unionpoint; +}; + +// natural data +struct CNaturalData +{ + UINT id; + UINT itemtype; + UINT type; + UINT price; + UINT pricerate; + UINT weight; + UINT quality; + UINT pricevalue; + UINT unionpoint; +}; + +// pat data +struct CPatData +{ + UINT id; + UINT itemtype; + UINT type; + UINT price; + UINT pricerate; + UINT weight; + UINT quality; + UINT material; + UINT partversion; + UINT level; + UINT condition[2]; + UINT modifier[2]; + UINT maxfuel; + UINT fuelcons; + UINT speed; + UINT attackdistance; + UINT attackpower; + UINT attackspeed; + UINT unionpoint; + UINT durability; +}; + +struct CProductData +{ + UINT id; + UINT matType; // used for material type in crafting + UINT item[5]; + UINT itemtype[5]; // item type + UINT itemid[5]; // item number + UINT amount[5]; +}; + +struct CBreakData +{ + UINT id; //id of the break + UINT ItemId; //item being broken + UINT Mat[20]; //material id + UINT Min[20]; //minimum count + UINT Max[20]; //maximum count + UINT Chance[20]; //chance for this item + UINT MinDis; //Minimum number of items from the list + UINT MaxDis; //Maximun number of items from the list + UINT ItemCount; //Count of items in the list +}; + +struct CCSellData +{ + UINT id; + UINT item[49]; +}; + +struct CUseData +{ + UINT id; + UINT itemtype; + UINT restriction; + UINT type; + UINT price; + UINT pricerate; + UINT weight; + UINT quality; + UINT usecondition[2]; + UINT useeffect[2]; + UINT pricevalue; + UINT unionpoint; +}; + +struct CVector2D +{ + CVector2D() : x(0), y(0) {} + CVector2D(dword nX, dword nY) : x(nX), y(nY) {} + dword x, y; + + bool operator == ( const CVector2D& otherpoint ){ + return (x == otherpoint.x) && (y == otherpoint.y); + } + + bool operator != ( const CVector2D& otherpoint ){ + return (x != otherpoint.x) || (y != otherpoint.y); + } +}; + +struct CVector2F +{ + CVector2F() : x(0), y(0) {} + CVector2F(float nX, float nY) : x(nX), y(nY) {} + float x, y; + + bool operator == ( const CVector2F& otherpoint ) + { + return (fabs(this->x-otherpoint.x)<0.001f && fabs(this->y-otherpoint.y)<0.001f); + } +}; + +struct SBasic +{ + word exp; + word map; + CVector2F pos; +}; + +// ----------------------------------------------------------------------------------------- +// A 2d point, for positions +// ----------------------------------------------------------------------------------------- +struct fPoint +{ + float x; + float y; + float z; // Unused? +}; + +// AIP data +struct CAIPData +{ + char* filename[200]; + +}; + +// List Mob Data +struct CNPCData +{ + UINT id; + char name[50]; + UINT life; + UINT wspeed; + UINT rspeed; + BYTE stance; + UINT dspeed; + UINT weapon; + UINT subweapon; + UINT level; + UINT hp; + UINT atkpower; + UINT hitrate; + UINT defense; + UINT magicdefense; + UINT dodge; + float atkspeed; + UINT magicattack; + UINT AI; + UINT exp; + UINT dropid; + UINT money; + UINT item; + UINT tab1; + UINT tab2; + UINT tab3; + UINT specialtab; + float atkdistance; + UINT aggresive; + char QTrigger[20]; + UINT shp; + UINT dialogid; + UINT eventid; + + UINT side; + UINT sidechance; + UINT zulydrop; + UINT mapdrop; + UINT mobdrop; + UINT leveldrop; + UINT dropchance; + + dword EventID; + SBasic basic; + bool IsMoving; + CVector2F newPos; + CVector2D lastBlock; + CVector2D curBlock; + dword _EntityType; + UINT refNPC; + + UINT die_quest; +}; + +// Store the damage for monster to give exp +struct MonsterDamage +{ + UINT charid; + long int damage; +}; + +struct CDropInfo +{ + BYTE type; + DWORD item; + QWORD prob; +}; + +// For store the drops info +struct CMDrops +{ + UINT id; + UINT zuly; + UINT zulyprob; + vector Drops; + UINT level_min;//for map drops + UINT level_max;//for map drops + UINT level_boss;//for map drops + UINT probmax; + + UINT prob; + UINT map; + UINT mob; + UINT itemtype; + UINT itemnum; + UINT alt[8]; + //experimental structure for New Drops + UINT Dropid; + UINT item[100]; + UINT chance[100]; + dword RunningTotal[100]; + dword TotalChance; + +}; + +// For store the Buff info +struct CBuff +{ + unsigned int Buff;//store the buff type + unsigned int value1;//buff value 1 (direct) + unsigned int value2;//buff value 2 (%) + unsigned int Evalue;//Extra value 1 (buffer int +) + unsigned int Duration; //Skill Duration + clock_t bufftime;//Buff Time +}; + +// ----------------------------------------------------------------------------------------- +// For store the Buff info +// ----------------------------------------------------------------------------------------- +struct CBuffValue +{ + int value1; + int value2; + int value3; +}; + +// ----------------------------------------------------------------------------------------- +// For store the Buff info +// ----------------------------------------------------------------------------------------- +struct CPSkill +{ + int buff[3];//Buff type + int value1[3]; + int value2[3]; +}; + +// ----------------------------------------------------------------------------------------- +// For store the clan info +// ----------------------------------------------------------------------------------------- +struct SClan +{ + int id; + int logo; + int back; + int grade; + int cp; + char name[50]; + char slogan[100]; + char news[200]; +}; + +// ----------------------------------------------------------------------------------------- +// GM Respawn Mobs (50 points max) +// ----------------------------------------------------------------------------------------- +struct CRespawnPoints +{ + int d;//flag to duplicate + int b;//flag to store + int n;//num points + int min;//mob min + int max;//mob max + int map;//map + int mobID;//mob id + int respawntime;//respawn time + fPoint points[50];//points +}; + +// ----------------------------------------------------------------------------------------- +// An item that a client owns +// ----------------------------------------------------------------------------------------- +class CItem +{ +public: + unsigned short GetPakHeader( ); + unsigned GetPakData( ); + unsigned GetVisInfo( ); + void Clear( ); + + inline bool IsStackable() + { + return (itemtype >= 10) && (itemtype <= 13); + }; + unsigned itemnum; + UINT itemtype; + UINT refine; + UINT lifespan; + UINT durability; + bool socketed; + bool appraised; + int count; + UINT stats; + UINT gem; + UINT quality; + UINT type; + UINT raretype; +}; + +// ----------------------------------------------------------------------------------------- +// Selling / buying items +// ----------------------------------------------------------------------------------------- +struct BSItem { + int slot; + int count; + long int price; + int head; + int data; +}; + +// ----------------------------------------------------------------------------------------- +// A typical npc +// ----------------------------------------------------------------------------------------- +struct CNPC +{ + unsigned short clientid; + unsigned char posMap; + unsigned npctype; + fPoint pos; + float dir; + CNPCData* thisnpc; + clock_t lastAiUpdate; +}; + +struct SWarp { + SWarp() : TargetPoint(0), TargetMap(0), SourceMap(0){pos.x = 0; pos.y = 0;} + ~SWarp(){} + + CVector2F pos; + dword SourceMap; + dword TargetMap; + strings TargetPoint; +}; + +// Zone Data +struct SZonPoint { + CVector2F pos; + strings name; +}; + +// Item data object +struct CItemData +{ + unsigned type; + unsigned id; +}; + +// Structure for holding loaded STB data + +struct CNPCTrigger +{ + unsigned hash; + unsigned char linkcount; + unsigned char conditioncount; + unsigned char rewardcount; + unsigned* links; + unsigned char** actions; +}; + +// ----------------------------------------------------------------------------------------- +// Skill Data +// ----------------------------------------------------------------------------------------- +struct CSkills +{ + UINT id; + UINT skillid; + UINT level; + UINT type; + UINT skilltype; + UINT range; + UINT target; + UINT status[2]; + UINT aoerange; + UINT successrate; + UINT costtype[2]; + UINT costamount[2]; // New + UINT duration; + UINT atkpower; + UINT mp; + UINT costtype2; // New + UINT costamount2; // New + UINT cooldown; // New + UINT weapon[5]; + UINT rskill[3]; + UINT lskill[3]; + UINT c_class[4]; + UINT req[2]; + UINT reqam[2]; + UINT zuly; // New + UINT clevel; + UINT success; + UINT sp; + UINT buff[3]; + UINT value1[3]; + UINT value2[3]; + UINT nbuffs; + + unsigned short int aoe; + UINT aoeradius; + UINT script; + UINT svalue1; + +}; + +//LMA: Grade structure. +struct CGrade +{ + UINT atk_percent; + UINT atk_addbonus; + UINT acc_percent; + UINT acc_addbonus; + UINT def_percent; + UINT def_addbonus; + UINT mdef_percent; + UINT mdef_addbonus; + UINT dodge_percent; + UINT dodge_addbonus; +}; + +// ----------------------------------------------------------------------------------------- +// Status Object +// ----------------------------------------------------------------------------------------- +struct CStatus +{ + UINT id; // + UINT type; // Unk [1] + UINT duplication; // Unk [2] + UINT dir; // 0: Up 1: Down 2: ? [3] + UINT repeat; // 1: Repeat 2: Once 3: Special [4] + UINT ability[2]; // Status # to call? [5][7] + UINT amount[2]; // # to increase/decrease by? [6][8] + UINT decrease; // 1: decrease 0: increase [17] +}; + +// ----------------------------------------------------------------------------------------- +// Sell data object +// ----------------------------------------------------------------------------------------- +struct CSellData +{ + unsigned id; + CItem items[48]; +}; + +// ----------------------------------------------------------------------------------------- +// User skill info +// ----------------------------------------------------------------------------------------- +struct CSkill +{ + unsigned id; + unsigned level; + CSkills* thisskill; +}; + +// ----------------------------------------------------------------------------------------- + +// An item or zuly dropped on the ground +// ----------------------------------------------------------------------------------------- +struct CDrop +{ + unsigned short clientid; + unsigned char posMap; + bool spawned; + fPoint pos; + char type; + unsigned amount; + CItem item; + time_t droptime; + unsigned short owner; + class CParty* thisparty; +}; + +// ----------------------------------------------------------------------------------------- +// pvp zones +// ----------------------------------------------------------------------------------------- +struct CPVPZones +{ + int map; +}; +// ----------------------------------------------------------------------------------------- +// A teleport gate object +// ----------------------------------------------------------------------------------------- +struct CTeleGate +{ + unsigned short id; + fPoint src; + unsigned char srcMap; + fPoint dest; + unsigned char destMap; +}; + +// ----------------------------------------------------------------------------------------- +// A respawn point object +// ----------------------------------------------------------------------------------------- +struct CRespawnPoint +{ + unsigned short id; + fPoint dest; + unsigned char destMap; + unsigned char radius; + bool masterdest; +}; + +// ----------------------------------------------------------------------------------------- +// A monster spawn zone +// ----------------------------------------------------------------------------------------- +struct CSpawnArea +{ + UINT id; + UINT type; + //0 = inactive + //1 = normal + //2 = cascade monster triggered by specific trigger defined in typeoftrigger + //3 = cascade to max. As 2 but all possible mobs are spawned up to the spawn maximum + //4 = minion spawns. i.e. scarabs around a Guardian Tree. don't use a spawn area but spawn around the boss + //5 = minion spawns to max. As 4 but all possible mobs are spawned to the spawn maximum. + UINT typeoftrigger; + //0 = disabled + //1 = triggered from any event in the map + //2 = triggered only from events in a specific spawn + UINT limit; + //0 = day or night spawns permitted + //1 = daytime spawns only + //2 = nighttime spawns only + UINT spawnkey; + //0 = no key defined + //1 = On spawn + //2 = On die + //3 = On being in a battle ( Isonbattle() ) + //4 = On target mob being alive + UINT map; + UINT min; + UINT max; + UINT respawntime; + UINT montype; + UINT pcount; + UINT triggertype; + UINT triggeramount; + UINT triggercount; + UINT aggressive; + UINT amon; + fPoint* points; + fPoint point; + UINT radius; + clock_t lastRespawnTime; + CNPCData* thisnpc; + CMDrops* mobdrop; + CMDrops* mapdrop; + UINT AI; + UINT RefVar; + UINT RefVal; +}; + +#ifdef USEIFO +struct CMob { + UINT id; + UINT mobId; + bool tactical; + UINT amount; + CNPCData* thisnpc; + CMDrops* mobdrop; + CMDrops* mapdrop; +}; + +struct CMobGroup { + UINT id; + UINT map; + UINT limit; + UINT active; + UINT tacticalpoints; + UINT respawntime; + UINT basicKills; + UINT curTac; + UINT curBasic; + fPoint point; + UINT range; + clock_t lastRespawnTime; + vector basicMobs; + vector tacMobs; +}; +#endif + +struct CUseInfo +{ + int itemnum; + int itemtype; + int usescript; + int usetype; + int usevalue; +}; + +struct CItemType +{ + int itemnum; + int itemtype; + int probability; +}; + +struct CItemSlot +{ + UINT newslot[2]; + UINT nslot; + bool flag; +}; + +// Buying List (Shop) +struct CBuying +{ + CItem item; + long int price; + UINT slot; + UINT count; +}; + +// Selling List (Shop) +struct CSelling +{ + UINT slot; + UINT count; + long int price; +}; + +// MONSTER <--------------------------- +struct MPosition // Monster position +{ + UINT Map; + UINT SpawnID; + fPoint Spos; // Start position + fPoint Cpos; // Current Position + fPoint Dpos; // destiny Position +}; + +struct MDrop // monster drops +{ + bool drop; + unsigned int firsthit; + unsigned int firstlevel; + CMDrops* mobdrop; + CMDrops* mapdrop; +}; + +struct CBValue +{ + UINT NewValue; + UINT Value; + UINT Position; +}; + + +// ITEMS +struct CEquipList +{ + CEquip *Index[5000]; + vector Data; + CEquip* nullequip; + UINT STBMax; +}; + +struct CJemList +{ + CJemData *Index[4000]; + vector Data; + CJemData* nulljem; +}; + +struct CNaturalList +{ + CNaturalData *Index[1000]; + vector Data; + CNaturalData* nullnatural; +}; + + +struct CPatList +{ + CPatData *Index[1000]; + vector Data; + CPatData* nullpat; +}; + +struct CProductList +{ + CProductData* Index[2000]; + vector Data; + CProductData* nullproduct; +}; + +struct CBreakList +{ + CBreakData* Index[6000]; + vector Data; + CBreakData* nullbreak; +}; + +struct CSellList +{ + CCSellData* Index[1000]; + vector Data; + CCSellData* nullsell; +}; + +struct CUseList +{ + CUseData* Index[2000]; + vector Data; + CUseData* nulluse; +}; + +struct CMapList +{ + class CMap* Index[300]; + vector Map; + class CMap* nullzone; +}; + +struct CItemStas +{ + UINT stat[2]; + UINT value[2]; +}; + +struct CFairy +{ + UINT ListIndex; + clock_t LastTime; + UINT WaitTime; + bool assigned; + bool scroll; +}; + +// Quests + +struct SQuest +{ + word QuestID; + dword StartTime; + word Variables[10]; + byte Switches[4]; + CItem Items[5]; + byte unknown[6]; + + bool GetSwitchBit( dword switchId ) + { + dword byteId = switchId / 8; + dword bitId = switchId % 8; + return ((Switches[byteId] >> bitId) & 1); + } + + void SetSwitchBit( byte switchId, byte value ) + { + dword byteId = switchId / 8; + dword bitId = switchId % 8; + Switches[byteId] = Switches[byteId] | ((value?1:0) << bitId); + } + + void AddItem(CItem* item, byte btOp = 2) + { + for(dword i = 0; i < 6; i++) + { + if(Items[i].GetPakHeader() == item->GetPakHeader()) + { + if(btOp == 1) + { + Items[i].count += item->count; + return; + } + else if(btOp == 0) + { + if(Items[i].count <= item->count) + Items[i].Clear(); + else + Items[i].count -= item->count; + return; + } + } + if(btOp == 0) continue; + if(Items[i].GetPakHeader() != 0) continue; + Items[i].itemnum = item->itemnum; + Items[i].itemtype = item->itemtype; + Items[i].count = item->count; + return; + } + } + + void Clear() + { + memset(this, 0, sizeof(SQuest)); + } + +}; + +struct SQuestData +{ + word EpisodeVar[5]; + word JobVar[3]; + word PlanetVar[7]; + word UnionVar[10]; + + SQuest quests[10]; + byte flags[0x40]; + byte Qflags[64][8]; //new quest flag structure for testing PY + + void SetFlag( dword flagid, bool value ) + { + dword byteid = flagid / 8; + dword bitid = flagid % 8; + //flags[byteid] = flags[byteid] | ((value?1:0) << bitid); + Qflags[byteid][bitid] = value?1:0; + } + + bool GetFlag( dword flagid ) + { + dword byteid = flagid / 8; + dword bitid = flagid % 8; + //return ((flags[byteid] >> bitid) & 1); + return (Qflags[byteid][bitid] == 1); + } +}; + +struct TowerDef +{ + fPoint pos; + float X; + float Y; + UINT WPType; +}; + +enum class QuestResult +{ + RESULT_QUEST_REPLY_NULL = 0, + RESULT_QUEST_REPLY_ADD_SUCCESS = 1, + RESULT_QUEST_REPLY_ADD_FAILED = 2, + RESULT_QUEST_REPLY_DEL_SUCCESS = 3, + RESULT_QUEST_REPLY_DEL_FAILED = 4, + RESULT_QUEST_REPLY_TRIGGER_SUCCESS = 5, // This packet is received, the client applies the compensation. + RESULT_QUEST_REPLY_TRIGGER_FAILED = 6 +}; + + + +#endif + diff --git a/Src/World Server/extrafunctions.cpp b/Src/World Server/extrafunctions.cpp new file mode 100644 index 0000000..f7ea349 --- /dev/null +++ b/Src/World Server/extrafunctions.cpp @@ -0,0 +1,1173 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Build Item Head +unsigned CWorldServer::BuildItemHead( CItem thisitem ) +{ + if (thisitem.count==0) return 0; + return ( ( thisitem.itemnum & 0x7ffffff ) << 5 ) | ( thisitem.itemtype & 0x1f ); +} + +// Build Item Data +unsigned CWorldServer::BuildItemData( CItem thisitem ) +{ + if (thisitem.count==0) return 0; + if ( thisitem.itemtype >= 10 && thisitem.itemtype <= 13 ) + { + return thisitem.count; + } + else + { + unsigned part1 = (thisitem.refine>>4) << 28; + unsigned part2 = (thisitem.appraised?1:0) << 27; + unsigned part3 = (thisitem.socketed?1:0) << 26; + unsigned part4 = (thisitem.lifespan*10) << 16; + unsigned part5 = thisitem.durability << 9; + unsigned part6 = thisitem.stats; + unsigned part7 = thisitem.gem; + if (!part7==0)part6=0; + return part1 | part2 | part3 | part4 | part5 | part6 | part7; + } +} + +// Get Item By Head and Data (Coded by Caali) +CItem CWorldServer::GetItemByHeadAndData( unsigned head, unsigned data ) +{ + CItem thisitem; + thisitem.Clear ( ); + + //Get info from ItemHead + thisitem.itemnum = (head >> 5) & 0x7ffffff; + head ^= (thisitem.itemtype & 0x7ffffff) << 5; + thisitem.itemtype = head & 0x1f; + + //Get info from ItemData + if( thisitem.itemtype >= 10 && thisitem.itemtype <= 13 ) //Stackable + { + thisitem.refine = 0; + thisitem.appraised = 0; + thisitem.socketed = 0; + thisitem.lifespan = 100; + thisitem.durability = 40; + thisitem.gem = 0; + thisitem.stats = 0; + thisitem.count = (int)data; + } + else //Non-stackable + { + thisitem.refine = (data >> 28) << 4; + data-=(thisitem.refine>>4) << 28; + thisitem.appraised = ((data >> 27)==1)?true:false; + data-=(thisitem.appraised?1:0) << 27; + thisitem.socketed = ((data >> 26)==1)?true:false; + data-=(thisitem.socketed?1:0) << 26; + thisitem.lifespan = (data >> 16) / 10; + data-=(thisitem.lifespan*10) << 16; + thisitem.durability = data >> 9; + data-=thisitem.durability << 9; + thisitem.gem = data; + data-=thisitem.gem; + thisitem.count = 1; + } + return thisitem; +} + +//----Build Item Show +unsigned CWorldServer::BuildItemShow(CItem thisitem) +{ + if (thisitem.gem == 0) + { + unsigned part1 = thisitem.refine*0x10000; + unsigned part2 = thisitem.itemnum; + return part2|part1; + } + else + { + unsigned part1 = thisitem.refine*0x10000; + unsigned part2 = thisitem.itemnum; + unsigned part3 = (0xd0000)+(((thisitem.gem-320)*0x400)); + return part3|part2|part1; + } +} + + +//----Build Item refine +unsigned CWorldServer::BuildItemRefine(CItem thisitem) +{ + if (thisitem.gem == 0) + return ((thisitem.refine)*256); + else + { + return (0xd00+(thisitem.gem-320)*4)+((thisitem.refine)*256); + } +} + +// Send a packet too all connected clients +void CWorldServer::SendToAll( CPacket* pak ) +{ + for(UINT i=0;iplayer; + if (otherclient->Session->inGame) + otherclient->client->SendPacketCpy( pak ); + } +} + +// Send a packet too all X who are visible +// -- CLIENT -- +void CWorldServer::SendToVisible( CPacket* pak, CPlayer* thisclient, bool dothisclient ) +{ + for(unsigned j=0; jVisiblePlayers.size(); j++) + { + thisclient->VisiblePlayers.at( j )->client->SendPacketCpy( pak ); + } + if(dothisclient) + thisclient->client->SendPacketCpy( pak ); +} + +void CWorldServer::SendToVisible( CPacket* pak, CPlayer* thisclient, CPlayer* xotherclient ) +{ + for(unsigned j=0; jVisiblePlayers.size(); j++) + { + CPlayer* otherclient = thisclient->VisiblePlayers.at( j ); + if(otherclient==xotherclient) + continue; + otherclient->client->SendPacketCpy( pak ); + } +} + +// -- MONSTER -- +void CWorldServer::SendToVisible( CPacket* pak, CMonster* thismon, CDrop* thisdrop ) +{ + CMap* map = MapList.Index[thismon->Position->Map]; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* otherclient = map->PlayerList.at(i); + if( IsVisible(otherclient, thismon) ) + otherclient->client->SendPacketCpy( pak ); + if(thisdrop!=NULL) + { + otherclient->VisibleDrops.push_back( thisdrop ); + } + } +} + + +// -- CHARACTER -- +void CWorldServer::SendToVisible( CPacket* pak, CCharacter* character, CDrop* thisdrop ) +{ + if(character->IsPlayer( )) + { + SendToVisible( pak, (CPlayer*)character ); + } + else + if(character->IsMonster( )) + { + SendToVisible( pak, (CMonster*)character, thisdrop ); + } +} + +// -- DROP -- +void CWorldServer::SendToVisible( CPacket* pak, CDrop* thisdrop ) +{ + CMap* map = MapList.Index[thisdrop->posMap]; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* otherclient = map->PlayerList.at(i); + if (otherclient->client == NULL) continue; + if (!otherclient->client->isActive) continue; + if( IsVisible(otherclient, thisdrop) ) + otherclient->client->SendPacketCpy( pak ); + } +} + +// Send a packet too all clients on the specified map +void CWorldServer::SendToMap( CPacket* pak, int mapid ) +{ + CMap* map = MapList.Index[mapid]; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* otherclient = map->PlayerList.at(i); + if( otherclient->Session->inGame ) + otherclient->client->SendPacketCpy( pak ); + } +} + +// ----------------------------------------------------------------------------------------- +// Check if an object is visible to a client +// ----------------------------------------------------------------------------------------- +// -- CLIENT -- +bool CWorldServer::IsVisible( CPlayer* thisclient, CPlayer* otherclient ) +{ + for(unsigned j=0; jVisiblePlayers.size(); j++) + { + if (otherclient==thisclient->VisiblePlayers.at(j)) + return true; + } + return false; +} + +// -- MOB -- +bool CWorldServer::IsVisible( CPlayer* thisclient, CMonster* thismon ) +{ + for(unsigned j=0; jVisibleMonsters.size(); j++) + { + if (thismon->clientid == thisclient->VisibleMonsters.at(j)) return true; + } + return false; +} + +// -- DROP -- +bool CWorldServer::IsVisible( CPlayer* thisclient, CDrop* thisdrop ) +{ + for(unsigned j=0; jVisibleDrops.size(); j++) + { + if (thisdrop == thisclient->VisibleDrops.at(j)) return true; + } + return false; +} + +// -- NPC -- +bool CWorldServer::IsVisible( CPlayer* thisclient, CNPC* thisnpc ) +{ + for(unsigned j=0; jVisibleNPCs.size(); j++){ + if (thisnpc==thisclient->VisibleNPCs.at(j)) return true; + } + return false; +} + +// This function gets a new clientID for a npc, monster or mob +unsigned CWorldServer::GetNewClientID( ) { + unsigned i = ClientIDIndex; // Set i (the index value) equal to the current Index Pointer value + for (unsigned j=1; j<0xffff; j++) { // Outer loop will limit the entire function to no more than 65535 iterations + if ( ClientIDList[i] == 0 ) { // Found a free ID at position i + ClientIDList[i] = 1; // Set the value at this position to 1 to indicate it is in use + ClientIDIndex = i + 1; // Set the pointer to the next element of the Array + if (ClientIDIndex >= 0xffff) // If ClientIDIndex is greater than or equal to 65535 then wrap it around to 1 + ClientIDIndex = 1; + return i; // Return the index value of the element that we just found + } + i++; // Increment i + if (i >= 0xffff) // If i is greater than or equal to 65535 then wrap it around to 1 + i = 1; + } + return 0; // After 65535 iterations we failed to find a free ID so return 0 +} + +// This function will free our clientID +void CWorldServer::ClearClientID( unsigned int id ) { + ClientIDList[id] = 0; // Clear the value at the position pointed at by the released ID +} + +// Search a drop by ID +CDrop* CWorldServer::GetDropByID( UINT id, UINT map ) +{ + if(map!=0) + return MapList.Index[map]->GetDropInMap( id ); + for(unsigned j=0; jGetDropInMap( id ); + if(thisdrop != NULL) + return thisdrop; + } + return NULL; +} + +// Search a Monster by ID +CMonster* CWorldServer::GetMonsterByID( UINT id, UINT map ) +{ + if(map!=0) + return MapList.Index[map]->GetMonsterInMap( id ); + for(UINT i=0;iGetMonsterInMap( id ); + if(thismon!=NULL) + return thismon; + } + return NULL; +} + +// Search a Client by Username +CPlayer* CWorldServer::GetClientByUserName( char *username ) +{ + for(UINT i=0;iplayer; + if (strcmp(thisclient->Session->username,username)==0) + return thisclient; + } + return NULL; +} + +// Get Client By ID +CPlayer* CWorldServer::GetClientByID( UINT id, UINT map ) +{ + if(map!=0) + return MapList.Index[map]->GetPlayerInMap( id ); + for(UINT i=0;iplayer; + if (thisclient->clientid==id) + return thisclient; + } + return NULL; +} + +// Search a Client by CharID +CPlayer* CWorldServer::GetClientByCID( UINT id, UINT map ) +{ + if(map!=0) + return MapList.Index[map]->GetCharIDInMap( id ); + for(UINT i=0;iplayer; + if (thisclient->CharInfo->charid==id) + return thisclient; + } + return NULL; +} + +// Seach a client by Charname +CPlayer* CWorldServer::GetClientByCharName( char* name ) +{ + for(UINT i=0;iplayer; + if (strncmp(thisclient->CharInfo->charname,name, 16)==0) + return thisclient; + } + return NULL; +} + +// Get Spawn Area by ID +CSpawnArea* CWorldServer::GetSpawnArea( UINT id, UINT map ) +{ + if(map!=0) + { + for(unsigned j=0; jMonsterSpawnList.size(); j++) + { + CSpawnArea* thisspawn = MapList.Index[map]->MonsterSpawnList.at(j); + if (thisspawn->id==id) + return thisspawn; + } + } + else + { + for(map=0;mapMonsterSpawnList.size(); j++) + { + CSpawnArea* thisspawn = MapList.Index[map]->MonsterSpawnList.at(j); + if (thisspawn->id==id) + return thisspawn; + } + } + } + return NULL; +} + +#ifdef USEIFO +CMobGroup* CWorldServer::GetMobGroup(UINT id, UINT map ) +{ + if (map != 0) + { + for (unsigned j = 0; j < MapList.Index[map]->MobGroupList.size(); j++) + { + CMobGroup* thisgroup = MapList.Index[map]->MobGroupList.at(j); + if (thisgroup->id == id) + return thisgroup; + } + } + else + { + for (map = 0; map < MapList.Map.size(); map++) + { + for (unsigned j = 0; j < MapList.Index[map]->MobGroupList.size(); j++) + { + CMobGroup* thisgroup = MapList.Index[map]->MobGroupList.at(j); + if (thisgroup->id == id) + return thisgroup; + } + } + } + return NULL; +} +#endif + +// delete a spawn +bool CWorldServer::DeleteSpawn( CSpawnArea* spawn ) +{ + if(spawn==NULL) + return false; + for(UINT i=0;imap]->MonsterSpawnList.size();i++) + { + if(MapList.Index[spawn->map]->MonsterSpawnList.at(i)==spawn) + { + MapList.Index[spawn->map]->MonsterSpawnList.erase( MapList.Index[spawn->map]->MonsterSpawnList.begin()+i ); + delete spawn; + return true; + } + } + return false; +} + +// Search NPC by ID +CNPC* CWorldServer::GetNPCByID( UINT id, UINT map ) +{ + if(map!=0) + return MapList.Index[map]->GetNPCInMap( id ); + for(unsigned j=0; jGetNPCInMap( id ); + if (thisnpc!=0) + return thisnpc; + } + return NULL; +} + +// Get Telegate by ID +CTeleGate* CWorldServer::GetTeleGateByID( unsigned int id ) +{ + for(unsigned j=0; jid == id) return thisgate; + } + // Hmm, shit, couldent find it + return NULL; +} + +// Get Triangle Area +float CWorldServer::AreaOfTriangle( fPoint p1, fPoint p2, fPoint p3 ) +{ + return abs((int)((p2.x * p1.y - p1.x * p2.y) + (p3.x * p2.y - p2.x * p3.y) + (p1.x * p3.y - p2.x * p1.y))) / 2; +} + +// Get a random point in triangle area +fPoint CWorldServer::RandInTriangle( fPoint p1, fPoint p2, fPoint p3 ) +{ + fPoint thispoint; + float a = 1.0; + float b = 1.0; + while (a+b>1) { a=(float)(rand()*1.0/RAND_MAX); b=(float)(rand()*1.0/RAND_MAX); } + float c = 1 - a - b; + thispoint.x = (p1.x*a)+(p2.x*b)+(p3.x*c); + thispoint.y = (p1.y*a)+(p2.y*b)+(p3.y*c); + return thispoint; +} + +// Get random point in poly +fPoint CWorldServer::RandInPoly( fPoint p[], int pcount ) +{ + int tnum = 0; + float tval = 0; + float totalarea = 0; + float* areas = new float[pcount-1]; + + for(int i=0; iareas[tnum-1]) break; } + + return RandInTriangle(p[0], p[tnum], p[tnum+1]); +} + +// Get Random point in circle +fPoint CWorldServer::RandInCircle( fPoint center, float radius ) +{ + fPoint thispoint; + float angle = (float)(rand() * ( pi * 2 ) / RAND_MAX); + float dist = (float)sqrt( rand() * 1.0 / RAND_MAX ) * radius; + thispoint.x = cos(angle) * dist + center.x; + thispoint.y = sin(angle) * dist + center.y; + return thispoint; +} + +// Check if a Monster is in a Circle around a Client with a specific range +bool CWorldServer::IsMonInCircle( CPlayer* thisclient, CMonster* thismon, float radius ) +{ + if ( thisclient->Position->Map!=thismon->Position->Map ) return false; + + float dx = ( thisclient->Position->current.x - thismon->Position->current.x ); + float dy = ( thisclient->Position->current.y - thismon->Position->current.y ); + + if ( sqrt( (dx * dx) + (dy * dy) ) <= radius ) + { + return true; + } + else + { + return false; + } +} + +// Check if a Monster is in a Circle around a point with a specific radius +bool CWorldServer::IsMonInCircle( fPoint center, fPoint position, float radius ) +{ + float dx = ( center.x - position.x ); + float dy = ( center.y - position.y ); + + if ( sqrt( (dx * dx) + (dy * dy) ) <= radius ) + { + return true; + } + else + { + return false; + } +} + +// Check if a Player is in a Circle around a Client with a specific range +bool CWorldServer::IsPlayerInCircle( CPlayer* thisclient, CPlayer* otherclient, float radius ) +{ + if ( thisclient->Position->Map!=otherclient->Position->Map ) return false; + + float dx = ( thisclient->Position->current.x - otherclient->Position->current.x ); + float dy = ( thisclient->Position->current.y - otherclient->Position->current.y ); + + if ( sqrt( (dx * dx) + (dy * dy) ) <= radius ) + { + return true; + } + else + { + return false; + } +} + +// Search Respawn by ID +CRespawnPoint* CWorldServer::GetRespawnByID( unsigned id ) +{ + for(UINT i=0;iRespawnList.size();j++) + { + CRespawnPoint* thisrespawn = thismap->RespawnList.at(j); + if(thisrespawn->id==id) + return thisrespawn; + } + } + return NULL; +} + +// Search Respawn by Map +CRespawnPoint* CWorldServer::GetRespawnByMap( int map ) +{ + for(UINT j=0;jRespawnList.size();j++) + { + if(MapList.Index[map]->RespawnList.at(j)->destMap==map) + return MapList.Index[map]->RespawnList.at(j); + } + return NULL; +} + +// Search Skill By ID +CSkills* CWorldServer::GetSkillByID( unsigned int id ) +{ + unsigned int A=0,B=0,C=0; + for(A=0,B=SkillList.size()-1;A<=B;) + { + if(A==B) + { + CSkills* thisskill = (CSkills*) SkillList.at( A ); + if( id = thisskill->id )// lol this function aint working correct + { + //Log(MSG_INFO,"skill returned id = %i", id ); + return thisskill; + } + else + { + Log(MSG_WARNING,"SKILL NOT FOUND! %i", id ); + return NULL; + } + } + C = (A+B)/2; + CSkills* thisskill = (CSkills*) SkillList.at( C ); + if(thisskill->id == id) + { + //Log(MSG_INFO,"skill returned id = %i. C = %i", id,C ); + return thisskill; + } + if(thisskill->id > id) + { + B=C-1; + } + else + { + A=C+1; + } + } + Log( MSG_WARNING,"SKILL NOT FOUNDED! %i", id ); + return NULL; +} + +// Search Status By ID +CStatus* CWorldServer::GetStatusByID( unsigned int id ) +{ + unsigned int A=0,B=0,C=0; + for(A=0,B=StatusList.size()-1;A<=B;) + { + if(A==B) + { + CStatus* thisstatus = (CStatus*) StatusList.at( A ); + if( id = thisstatus->id ) + return thisstatus; + else + { + Log(MSG_WARNING,"STATUS NOT FOUND! %i", id ); + return NULL; + } + } + C = (A+B)/2; + CStatus* thisstatus = (CStatus*) StatusList.at( C ); + if(thisstatus->id == id) + return thisstatus; + if(thisstatus->id > id) + B=C-1; + else + A=C+1; + } + Log( MSG_WARNING,"STATUS NOT FOUND! %i", id ); + return NULL; +} + +// Get Monster Drop By ID +CMDrops* CWorldServer::GetDropData( unsigned int id ) +{ + return NULL; + unsigned int A=0,B=0,C=0; + for(A=0,B=MDropList.size()-1;A<=B;) + { + if(A==B) + { + CMDrops* thismdrop = (CMDrops*) MDropList.at( A ); + if( id == thismdrop->id ){return thismdrop;} + else + { + Log(MSG_WARNING,"DROP NOT FOUNDED! %i", id ); + return 0; + } + } + C = (A+B)/2; + CMDrops* thismdrop = (CMDrops*) MDropList.at( C ); + if(thismdrop->id == id){return thismdrop;} + if(thismdrop->id > id){B=C-1;} + else{A=C+1;} + } + Log(MSG_WARNING,"DROP NOT FOUNDED! %i", id ); + return NULL; +} + +// Get NPC Data by ID +CNPCData* CWorldServer::GetNPCDataByID( unsigned int id ) +{ + unsigned int A=0,B=0,C=0; + for(A=0,B=NPCData.size()-1;A<=B;) + { + if(A==B) + { + CNPCData* thisnpc = (CNPCData*) NPCData.at( A ); + if( id == thisnpc->id ){return thisnpc;} + else + { + Log(MSG_WARNING,"NPC NOT FOUNDED! %i", id ); + return 0; + } + } + C = (A+B)/2; + CNPCData* thisnpc = (CNPCData*) NPCData.at( C ); + if(thisnpc->id == id){return thisnpc;} + if(thisnpc->id > id){B=C-1;} + else{A=C+1;} + } + Log(MSG_WARNING,"NPC NOT FOUNDED! %i", id ); + return NULL; +} + +// Get Consumible Item Info +CUseInfo* CWorldServer::GetUseItemInfo(CPlayer* thisclient, unsigned int slot ) +{ + if(thisclient->items[slot].count<1) + return NULL; + CUseInfo* useitem = new (nothrow) CUseInfo; + if(useitem==NULL) + { + Log(MSG_WARNING, "Error allocing memory GetUseItemInfo"); + return useitem; + } + useitem->itemnum = 0; + useitem->itemtype = 0; + useitem->usescript = 0; + useitem->usetype = 0; + useitem->usevalue = 0; + unsigned int type = 0; + useitem->itemnum = thisclient->items[slot].itemnum; + useitem->itemtype = thisclient->items[slot].itemtype; + type = UseList.Index[useitem->itemnum]->type; + switch(type) + { + case 311://Medicine + case 312://Food + { + useitem->usescript = 1; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + } + break; + case 313://Magic Item [scrolls/summons/fireworks/etc] + { + // scrolls [maxhp,attackpower up, ... ] + if((useitem->itemnum >= 301 && useitem->itemnum <= 323)|| (useitem->itemnum >= 961 && useitem->itemnum <= 964) ||(useitem->itemnum >= 331 && useitem->itemnum <= 340)) + { + useitem->usescript = 7; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + useitem->usevalue = 0; + } + else//Return Scrolls + if(useitem->itemnum > 349 && useitem->itemnum < 367) + { + if( thisclient->Stats->MP < 33 ){delete useitem;} + thisclient->Stats->MP -= 32; + useitem->usescript = 2; + switch( useitem->itemnum ) + { + //Adventure plain [credits to tomiz] + case 350: + useitem->usetype = 22; + useitem->usevalue = 51105310; + break; + //Zant + case 351: + useitem->usetype = 1; + useitem->usevalue = 52405192; + break; + //Junon + case 352: + useitem->usetype = 2; + useitem->usevalue = 55095283; + break; + //Eucar + case 353: + useitem->usetype = 51; + useitem->usevalue = 53665099; + break; + //Xita + case 354: + useitem->usetype = 61; + useitem->usevalue = 54344607; + break; + //Goblin cave + case 360: + useitem->usetype = 31; + useitem->usevalue = 55185444; + break; + //Desert of dead + case 361: + useitem->usetype = 29; + useitem->usevalue = 50825013; + break; + //El Verloon + case 362: + useitem->usetype = 24; + useitem->usevalue = 55275377; + break; + //George of Silence + case 363: + useitem->usetype = 28; + useitem->usevalue = 54674783; + break; + //Shady Jungle + case 364: + useitem->usetype = 62; + useitem->usevalue = 57515196; + break; + //Sunshine Beach + case 366: + useitem->usetype = 37; + useitem->usevalue = 50055382; + break; + default: + break; + } + } + else //Charm Scrolls + if( (useitem->itemnum>380 && useitem->itemnum<384) ) + { + useitem->usescript = 3; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + thisclient->CharInfo->stamina -= 5000; + } + else //Dance Scroll + if( (useitem->itemnum>370 && useitem->itemnum<377) || + (useitem->itemnum>389 && useitem->itemnum<398) ) + { + useitem->usescript = 12; // should not be treated the same as firecrackers. this is a skill + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + } + else // Firecrackers + if( useitem->itemnum == 913 || (useitem->itemnum >= 921 && useitem->itemnum <= 929) || + (useitem->itemnum >= 991 && useitem->itemnum <= 995) ) + { + useitem->usescript = 13; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + //useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + } + else // Emotions + if( (useitem->itemnum>970 && useitem->itemnum<979) ) + { + useitem->usescript = 4; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + } + else // Summons + if(useitem->itemnum > 400 && useitem->itemnum < 440) + { + if( thisclient->CharInfo->stamina < 100 ) + return NULL; + thisclient->CharInfo->stamina -= 100; + useitem->usescript = 5; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + CSkills* skill = GetSkillByID(useitem->usetype); + if(skill == NULL) + { + Log(MSG_INFO,"Useitem returnd a NULL skill"); + return NULL; + } + useitem->usevalue = skill->svalue1; + } + else // Summons 2. Special summons such as goodie bags + if(useitem->itemnum > 908 && useitem->itemnum < 918) + { + if( thisclient->CharInfo->stamina < 100 ) + return NULL; + thisclient->CharInfo->stamina -= 100; + useitem->usescript = 5; // testing at 11 for a bit. Gives no owner to the summons. + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1]; + CSkills* skill = GetSkillByID(useitem->usetype); + if(skill == NULL) + { + Log(MSG_INFO,"Useitem returnd a NULL skill"); + return NULL; + } + useitem->usevalue = skill->svalue1; + } + else // Snowball + if( useitem->itemnum == 326 ) + { + useitem->usescript = 6; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + } + else // fairy scroll + if( useitem->itemnum == 4860 ) // random number. please adjust to the correct id for your scroll + { + useitem->usescript = 14; // new category for fairy scroll + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + } + else + { + Log( MSG_WARNING, "Unknown Item %i - Type %i",useitem->itemnum,type); + delete useitem; + return NULL; + } + } + break; + case 314://Skill Book + useitem->usescript = 10; + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + break; + case 315://Repair Tool + delete useitem; + return NULL; + break; + case 316://Quest Scroll + delete useitem; + return NULL; + break; + case 317://Engine Fuel + delete useitem; + return NULL; + break; + case 320://Automatic Consumption + delete useitem; + return NULL; + break; + case 321://Time Coupon / shop coupon / storage expansion coupon + useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0]; + useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1]; + switch( useitem->usetype ) + { + case 96: // shop coupons + delete useitem; + thisclient->Shop->ShopType = UseList.Index[useitem->itemnum]->quality; + break; + case 94: // free storage coupon + break; + case 95: // storage expansion coupon + break; + case 133: // Exp medals + delete useitem; + thisclient->Stats->ItemXPRate = 2; + if (thisclient->Stats->ItemXPTime == 0) // don't reset timer if it already has time on it + { + thisclient->Stats->ItemXPTime = clock( ); // set start time for clock countdown + } + thisclient->Stats->ItemXPTimer += useitem->usevalue * 600000; //convert to seconds and add to time remaining + break; + case 138: // Exp nulifier + delete useitem; + thisclient->Stats->ItemXPRate = -1; // set to -1 for XP nulification + thisclient->Stats->ItemXPTime = clock( ); // set start time for clock countdown. This overwrites any other XP timer + thisclient->Stats->ItemXPTimer += useitem->usevalue * 600000; //convert to seconds and add to time remaining + break; + default: + break; + } + /*switch( useitem->itemnum ) + { + case 954: //shop coupon A + { + delete useitem; + thisclient->Shop->ShopType = 1; + } + case 955: //shop coupon B + { + delete useitem; + thisclient->Shop->ShopType = 2; + } + case 956: //shop coupon C + { + delete useitem; + thisclient->Shop->ShopType = 3; + } + } + */ + return NULL; + break; + case 322: // gift boxes and stuff + Log( MSG_WARNING, "Tried to use a gift box"); + break; + default: + Log( MSG_WARNING, "Unknown use item type: %i",type); + return NULL; + break; + } + useitem->itemtype -= 1; + return useitem; +} + +// Look if a skill target is friendly +bool CWorldServer::isSkillTargetFriendly( CSkills* thisskill ) +{ + switch(thisskill->target) + { //Non-Hostile Target + case tYourself://yourself + case tPartyMember://party member + case tClanMember://clan member + case tAlly://ally + case tAllMembers://all members + case tAllCharacters: // All characters + case tFaintedAlly://Fainted + return true; + break; //Hostile Target + case tMonster: //monster + case tHostileCharacter: //hostile character + case tHostileTeamMember: //hostile team member + case tHostileMonster: //hostile monster + return false; + break; + default: + Log(MSG_WARNING,"Unknown skill target %i. Skill id = %i", thisskill->target, thisskill->id); + break; + } + return false; +} + +// calc distance +float CWorldServer::distance( fPoint pos1, fPoint pos2 ) +{ + float dist = 0; + float dx = pos1.x - pos2.x; + float dy = pos1.y - pos2.y; + dist = sqrt( (dx*dx) + (dy*dy) ); + return dist; +} + +// Search client by userid +CPlayer* CWorldServer::GetClientByUserID( UINT userid ) +{ + for(UINT i=0;iplayer; + if(thisclient->Session->userid==userid) + return thisclient; + }; + return NULL; +} + +// Check if a slot is valid (to prevent crash) +bool CWorldServer::CheckInventorySlot( CPlayer* thisclient, int slot ) +{ + if(slot>=MAX_INVENTORY || slot<0) + { + Log(MSG_HACK, "Invalid Inventory slot %i from %s", slot, thisclient->Session->username ); + return false; + } + return true; +} + +// Return a pseudo random number +UINT CWorldServer::RandNumber( UINT init, UINT range, UINT seed ) +{ + if(seed!=0) + srand(seed); + if(range>RAND_MAX) + { + UINT max = (UINT)GServer->round(sqrt((double)range)); + UINT num1 = init+int(max*rand()/(RAND_MAX + 1.0)); + UINT num2 = init+int(max*rand()/(RAND_MAX + 1.0)); + UINT num3 = init+int(max*rand()/(RAND_MAX + 1.0)); + UINT res = (num1 * num2)+num3; + return res>range?range:res; + } + else + { + UINT ranum = init+int(range*rand()/(RAND_MAX + 1.0)); + return ranum; + } + +} + +// Check if is a valid item +bool CWorldServer::IsValidItem(UINT type, UINT id ) +{ + if(type==0 || id==0 ) + return false; + if(type<10) + { + if(id>4999) + return false; + if(EquipList[type].Index[id]->id==0) + return false; + } + else + { + switch(type) + { + case 10: + if(id>1999) + return false; + if(UseList.Index[id]->id==0) + return false; + break; + case 11: + if(id>3999) + return false; + if(JemList.Index[id]->id==0) + return false; + break; + case 12: + if(id>999) + return false; + if(NaturalList.Index[id]->id==00) + return false; + break; + case 14: + if(id>999) + return false; + if(PatList.Index[id]->id==00) + return false; + break; + default: + return false; + } + } + return true; +} + +int CWorldServer::GetIntValue( const char* s , void* var ) +{ + char *tmp = strtok( (char*)var, (const char*)s ); + return atoi(tmp==NULL?0:tmp); +} + +UINT CWorldServer::GetUIntValue( const char* s , void* var ) +{ + char *tmp = strtok( (char*)var, (const char*)s ); + return atoi(tmp==NULL?0:tmp); +} + +char* CWorldServer::GetStrValue( const char* s , void* var ) +{ + char *tmp = strtok( (char*)var, (const char*)s ); + return tmp; +} + +bool CWorldServer::AddParty( CParty* thisparty ) +{ + PartyList.push_back( thisparty ); + return true; +} + +bool CWorldServer::RemoveParty( CParty* thisparty ) +{ + for(UINT i=0;iNaturalList.Data.size();i++) + { + if(GServer->NaturalList.Index[i]->type == matType) + { + ThisCount++; + if(ThisCount == CraftLevel) + { + return GServer->NaturalList.Index[i]->id; + } + } + } + return 0; +} + +// return fairy range for waiting time random +UINT CWorldServer::GetFairyRange( UINT part ) +{ + UINT Range1[] = { 5, 5, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + UINT Range2[] = { 10, 7, 7, 6, 6, 6, 6, 5, 4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1 }; + if (part == 0) return Range1[GServer->FairyList.size()]; + if (part == 1) return Range2[GServer->FairyList.size()]; +} diff --git a/Src/World Server/gmcmds.cpp b/Src/World Server/gmcmds.cpp new file mode 100644 index 0000000..2a8d15b --- /dev/null +++ b/Src/World Server/gmcmds.cpp @@ -0,0 +1,3877 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + + +// Parse our commands to their appropriate function +bool CWorldServer::pakGMCommand( CPlayer* thisclient, CPacket* P ) +{ + + char* tmp; + char* command = strtok( (char*)&(*P).Buffer[1] , " "); + if (command==NULL) return true; + //non GM functions + if (strcmp(command, "b")==0) + { + if(Config.Command_Broadcast > thisclient->Session->accesslevel) + return true; + time_t seconds; + seconds = time (NULL); + if((thisclient->CharInfo->LastGlobal+Config.Command_GlobalTime) <= seconds || thisclient->Session->accesslevel > 100) + { + thisclient->CharInfo->LastGlobal = time (NULL); + char outputmsg[200]; + sprintf( outputmsg, "%s %s", Config.Command_GlobalPrefix, &P->Buffer[3] ); + Log( MSG_INFO, "%s> %s %s", thisclient->CharInfo->charname, Config.Command_GlobalPrefix, &P->Buffer[3]); + SendGlobalMSG(thisclient, outputmsg); + } + else + { + long int remaining = (Config.Command_GlobalTime-(seconds-thisclient->CharInfo->LastGlobal)); + char buffer2[200]; + sprintf ( buffer2, "Please wait %i seconds before sending another global message.", remaining ); + SendPM(thisclient, buffer2); + } + return true; + } + + // all commands beyond here require IsGM or IsDev to be active in the character record + if (thisclient->CharInfo->IsGM == 0) + { + SendPM(thisclient, "Sorry. you do not have GM priveleges with this character"); + return true; + } + //SendPM(thisclient, "Well it looks like you do have GM or DEV priveleges with this character"); + if (strcmp(command, "here")==0) + { + if(Config.Command_Here > thisclient->Session->accesslevel) + return true; + + return pakGMTele(thisclient, thisclient->Position->Map, thisclient->Position->current.x, thisclient->Position->current.y); + } + else if (strcmp(command, "tele")==0) // **** TELEPORT TO MAX AND X Y POINTS ***** + { + if(Config.Command_Tele > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned map=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; float x=(float)atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; float y=(float)atoi(tmp); + Log( MSG_GMACTION, " %s : /tele %i,%i,%i" , thisclient->CharInfo->charname, map, x, y); + return pakGMTele(thisclient, map, x, y); + } + else if (strcmp(command, "usebuff")==0) // apply a status effect. icon plus visual + { + //SendPM(thisclient, "effect used"); + //if(thisclient->Session->accesslevel < 300) + //return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + unsigned effect = atoi(tmp); + SendPM(thisclient, "Effect called for skill effect %i",effect); + return pakGMEffect(thisclient, effect); + } + else if (strcmp(command, "mute")==0) //==== Mute a player ==== [by Paul_T] + { + //SendPM(thisclient, "trying to use mute"); + if(Config.Command_Mute > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + char* id=tmp; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + int min=atoi(tmp); + Log( MSG_GMACTION, " %s : /mute %s %i" , thisclient->CharInfo->charname, id, min); + return pakGMMute(thisclient, id, min); + } + else if (strcmp(command, "configreset")==0) // *** REALOAD DATA FILES ****** + { + //SendPM(thisclient, "trying to use configreset"); + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + char* action; + if ((tmp = strtok(NULL, " ")) == NULL)return true; action = tmp; + if(strcmp(action, "maxlevel")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.MaxLevel = newval; + const char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET maxlevel=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "xprate")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.EXP_RATE = newval; + const char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET exp_rate=%i WHERE conf='%s'",newval,confname); + } + } + else if(strcmp(action, "cmd")==0) + { + LoadConfigurations( "commands.ini" ); + } + else + { + Log( MSG_INFO, "Unrecognized configreset command by GM %s" , thisclient->CharInfo->charname); + SendPM(thisclient, "/reload %s is not a valid command", tmp); + return true; + } + Log( MSG_GMACTION, " %s : /configreset %s" , thisclient->CharInfo->charname, action); + SendPM(thisclient, "%s data has been reloaded", action); + return true; + } + else if (strcmp(command,"readobjvar")==0) + { + //SendPM(thisclient, "trying to use readobjvar"); + if(thisclient->Session->accesslevel < 300 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + int objVar = atoi(tmp); + if (objVar > 1999)return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + int thisIndex = atoi(tmp); + if (thisIndex > 19)return true; + int thisValue = GServer->ObjVar[objVar][thisIndex]; + SendPM(thisclient, "Global Object Variable [%i][%i] current value is %i" , objVar,thisIndex,thisValue); + } + else if (strcmp(command,"setobjvar")==0) + { + //SendPM(thisclient, "trying to use setobjvar"); + if(thisclient->Session->accesslevel < 300 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + int objVar = atoi(tmp); + if (objVar > 1999)return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + int thisIndex = atoi(tmp); + if (thisIndex > 19)return true; + if ((tmp = strtok(NULL, " ")) == NULL) + return true; + int thisValue = atoi(tmp); + SendPM(thisclient, "Global Object Variable [%i][%i] set to value %i" , objVar,thisIndex,thisValue); + GServer->ObjVar[objVar][thisIndex] = thisValue; + } + /*else if (strcmp(command, "configreset")==0) // *** REALOAD DATA FILES ****** + { + if(thisclient->Session->accesslevel < 900) + return true; + char* action; + char* buffer; + if ((tmp = strtok(NULL, " ")) == NULL)return true; action = tmp; + if(strcmp(action, "maxlevel")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.MaxLevel = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET maxlevel=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "xprate")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.EXP_RATE = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET exp_rate=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "droprate")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.DROP_RATE = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET drop_rate=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "droptype")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.DROP_TYPE = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET drop_type=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "zulyrate")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.ZULY_RATE = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET zuly_rate=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "savetime")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.SAVETIME = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET savetime=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "partygap")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.PARTYGAP = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET partygap=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "maxstat")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.MAXSTAT = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET maxstat=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "playerdmg")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.PLAYERDMG = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET player_damage=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "monsterdmg")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.MONSTERDMG = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET monster_damage=%i WHERE conf='%s'",newval,confname); + } + } + if(strcmp(action, "cfmode")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT newval = atoi(tmp); + { + Config.CFMODE = newval; + char* confname = "default"; + GServer->DB->QExecute("UPDATE list_config SET cfmode=%i WHERE conf='%s'",newval,confname); + } + } + else if(strcmp(action, "cmd")==0) + { + LoadConfigurations( "commands.ini" ); + } + else + { + Log( MSG_INFO, "Unrecognized configreset command by GM %s" , thisclient->CharInfo->charname); + sprintf ( buffer, "/reload %s is not a valid command", tmp ); + SendPM(thisclient, buffer); + return true; + } + Log( MSG_GMACTION, " %s : /configreset %s" , thisclient->CharInfo->charname, action); + sprintf ( buffer, "%s data has been reloaded", action ); + SendPM(thisclient, buffer); + return true; + }*/ + else if (strcmp(command, "save")==0) // *** SAVE USER DATA ***** + { + if(Config.Command_Save > thisclient->Session->accesslevel) + return true; + thisclient->savedata(); + SendPM(thisclient, "Character data saved"); + return true; + } + else if (strcmp(command, "savequest")==0) // *** SAVE USER DATA ***** + { + if(Config.Command_Save > thisclient->Session->accesslevel) + return true; + thisclient->savequests(thisclient); + SendPM(thisclient, "Character Quest data saved"); + return true; + } + else if (strcmp(command, "aiwatch")==0) // set a skill for mobs to use. testing + { + //SendPM(thisclient, "trying to use aiwatch"); + if(thisclient->Session->accesslevel < 300 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + UINT newval = atoi(tmp); + Config.AIWatch = newval; + SendPM(thisclient, "AI script watching set to %i",newval); + } + else if (strcmp(command, "reload")==0) // *** REALOAD DATA FILES ****** + { + //SendPM(thisclient, "trying to use reload"); + if(Config.Command_Reload > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0 ) + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + if(strcmp(tmp, "config")==0) + { + LoadConfigurations( (const char*)filename.c_str() ); + GServer->LoadConfig( ); + } + else if(strcmp(tmp, "mobs")==0) + GServer->LoadNPCData( ); + else if(strcmp(tmp, "drops")==0) + //GServer->LoadPYDropsData( ); + GServer->LoadNewDrops( ); + else if(strcmp(tmp, "cmd")==0) + LoadConfigurations( "commands.ini" ); + else if(strcmp(tmp, "spawns")==0) + { + GServer->LoadMonsterSpawn(); + } + else if(strcmp(tmp, "tdef")==0) + LoadWayPoints( ); + //else if(strcmp(tmp, "equip")==0) + //{ + // GServer->LoadEquip(); + //} + else + { + Log( MSG_INFO, "Unrecognized reload command by GM %s" , thisclient->CharInfo->charname); + SendPM(thisclient, "/reload %s is not a valid command", tmp); + return true; + } + Log( MSG_GMACTION, " %s : /reload %s" , thisclient->CharInfo->charname, tmp); + SendPM(thisclient, "%s data has been reloaded", tmp); + return true; + } + else if(strcmp(command, "setspeedmod")==0) + { + if((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned mSpeedModif = atoi(tmp); + Log( MSG_GMACTION, " mSpeedModif changed to %i by %s" , mSpeedModif, thisclient->CharInfo->charname); + return pakGMChangeMSpeedModif(thisclient, mSpeedModif); + } + else if(strcmp(command, "mystat")==0) + { + //SendPM(thisclient, "trying to use mystat"); + if ((tmp = strtok(NULL, " "))==NULL)return true; + if(strcmp(tmp, "ap")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My Attack Power is %i", thisclient->Stats->Attack_Power ); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "acc")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My Accuracy is %i", thisclient->Stats->Accury ); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "hp")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My current HP is %i", thisclient->Stats->HP ); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "mp")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My current MP is %i", thisclient->Stats->MP ); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "maxhp")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My Maximum HP is %i", thisclient->GetMaxHP()); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "maxmp")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My maximum MP is %i", thisclient->GetMaxMP()); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "dodge")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My dodge is %i", thisclient->Stats->Dodge); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "def")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My defense is %i", thisclient->Stats->Defense); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "mdef")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My Magic defense is %i", thisclient->Stats->Magic_Defense); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "crit")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My critical is %i", thisclient->Stats->Critical); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "mspd")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My move speed is %i", thisclient->Stats->Move_Speed); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "aspd")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My attack speed is %i", thisclient->Stats->Attack_Speed); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "xprate")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My xp rate is %i", thisclient->Stats->xprate); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "stamina")==0) + { + char buffer2[200]; + sprintf ( buffer2, "My Stamina is %i", thisclient->CharInfo->stamina); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "weight")==0) + { + int weight = thisclient->GetCurrentWeight(); + int maxweight = thisclient->GetMaxWeight(); + char buffer2[200]; + sprintf ( buffer2, "My current weight is %i. max weight = %i", weight,maxweight); + SendPM(thisclient, buffer2); + } + else if(strcmp(tmp, "zulydrop")==0) + { + SendPM(thisclient, "My zuly drop rate is %i", thisclient->Stats->zulydroprate); + } + else if(strcmp(tmp, "itemdrop")==0) + { + SendPM(thisclient, "My item drop rate is %i", thisclient->Stats->itemdroprate); + } + else if(strcmp(tmp, "serverdrop")==0) + { + SendPM(thisclient, "Server item drop rate is %i", GServer->Config.DROP_RATE); + } + else if(strcmp(tmp, "clan")==0) + { + SendPM(thisclient, "My clan id is: %i My clan rank is: %i", thisclient->Clan->clanid, thisclient->Clan->clanrank); + } + else if(strcmp(tmp, "map")==0) + { + SendPM(thisclient, "My current is: %i ", thisclient->Position->Map); + } + else if(strcmp(tmp, "job")==0) + { + SendPM(thisclient, "My class id is: %i ", thisclient->CharInfo->Job); + } + } + else if (strcmp(command, "ann")==0) // *** SEND A ANNUNCEMENT *** + { + if(Config.Command_Ann > thisclient->Session->accesslevel) + return true; + Log( MSG_GMACTION, " %s : /ann %s" , thisclient->CharInfo->charname, &P->Buffer[5] ); + return pakGMAnn(thisclient, P); + } + else if(strcmp(command, "bodysize")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT size=atoi(tmp); + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 36); + ADDWORD(pak, size); + ADDWORD(pak, 0); + thisclient->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + thisclient->client->SendPacket(&pak); + SendSysMsg(thisclient, "Body size changed!"); + } + else if(strcmp(command, "headsize")==0) + { + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT size=atoi(tmp); + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 35); + ADDWORD(pak, size); + ADDWORD(pak, 0); + thisclient->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + thisclient->client->SendPacket(&pak); + SendSysMsg(thisclient, "Head size changed!"); + } + else if(strcmp(command, "face") == 0) + { + if(Config.Command_Face > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT face = atoi(tmp); + if (face > 15) return true; + UINT style; + if ((tmp = strtok(NULL, " ")) == NULL) style = 0; else style = atoi(tmp); + if (style > 3) style = 3; + thisclient->CharInfo->Face = ((face*7) + style + 1); + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 8); + ADDWORD(pak, thisclient->CharInfo->Face); + ADDWORD(pak, 0); + thisclient->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + thisclient->client->SendPacket(&pak); + SendPM(thisclient, "Face changed!"); + DB->QExecute("UPDATE characters SET face = %i WHERE id = %i", thisclient->CharInfo->Face, thisclient->CharInfo->charid); + } + else if(strcmp(command, "hair")==0) + { + if(Config.Command_Hair > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; UINT hair = atoi(tmp); + if (hair > 6) return true; + thisclient->CharInfo->Hair = (hair*5); + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 9); + ADDWORD(pak, thisclient->CharInfo->Hair); + ADDWORD(pak, 0); + thisclient->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + thisclient->client->SendPacket(&pak); + DB->QExecute("UPDATE characters SET hairStyle=%i WHERE id=%i", thisclient->CharInfo->Hair, thisclient->CharInfo->charid); + SendPM(thisclient, "Hair changed!"); + } + else if(strcmp(command, "pvp")==0) + { + if(Config.Command_Pvp > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + CMap* map = MapList.Index[thisclient->Position->Map]; + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 34); + if(map->allowpvp!=0) + { + map->allowpvp = 0; + ADDWORD(pak, 2); + SendSysMsg(thisclient, "PVP off"); + } + else + { + map->allowpvp = 1; + ADDWORD(pak, 51); + SendSysMsg(thisclient, "PVP on"); + } + ADDWORD(pak, 0); + SendToMap(&pak, thisclient->Position->Map); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + SendToMap(&pak, thisclient->Position->Map); + } + else if(strcmp(command, "who")==0) + { + if(Config.Command_Who > thisclient->Session->accesslevel) + return true; + int count = 1; + int gmcount = 0; + int playercount = 0; + char line0[200]; + while(count <= (ClientList.size()-1)) + { + CPlayer* whoclient = (CPlayer*)ClientList.at(count)->player; + if(whoclient != NULL) + { + if(whoclient->isInvisibleMode != true) + { + if(whoclient->Session->accesslevel > 100) + { + gmcount++; + } + else + { + playercount++; + } + } + count++; + } + } + sprintf(line0, "There are currently %i players and %i game moderators currently connected.", playercount, gmcount); + Log( MSG_GMACTION, " %s : /who" , thisclient->CharInfo->charname); + SendPM(thisclient, line0 ); + return true; + } + else if (strcmp(command, "go")==0) // AtCommandGo + { + SendPM(thisclient,"Got to the go command"); + if(Config.Command_Go > thisclient->Session->accesslevel) + return true; + SendPM(thisclient,"Got past the accesslevel check"); + if ((tmp = strtok(NULL, " ")) == NULL) tmp = 0; int loc=atoi(tmp); + int x = 0; + int y = 0; + int map = 0; + if(loc == 1) + { + map = 22; + x = 5749; + y = 5101; + } + else if(loc == 2) + { + map = 1; + x = 5240; + y = 5192; + } + else if (loc == 3) + { + map = 2; + x = 5516; + y = 5236; + } + else if (loc == 4) + { + map = 51; + x = 5357; + y = 5013; + } + else if (loc == 5) + { + map = 6; + x = 5243; + y = 5240; + } + else if (loc == 6) + { + map = 24; + x = 5525; + y = 5376; + } + else if (loc == 7) + { + map = 31; + x = 5516; + y = 5437; + } + else if (loc == 8) + { + map = 28; + x = 5639; + y = 4761; + } + else + { + SendPM(thisclient, "Please input a number after the go command, below is a list of places and their appropriate number"); + SendPM(thisclient, "1 = Adventurers plains"); + SendPM(thisclient, "2 = The city of Zant"); + SendPM(thisclient, "3 = Junon Polis"); + SendPM(thisclient, "4 = The city of Eucar"); + SendPM(thisclient, "5 = Training grounds"); + SendPM(thisclient, "6 = El Verloon Desert"); + SendPM(thisclient, "7 = Goblin Cave (B1)"); + SendPM(thisclient, "8 = Gorge of Silence"); + SendPM(thisclient, "Example; /go 3"); + } + + if( (x != 0) && (y != 0) && (map != 0) ) + { + fPoint coord; + coord.x = x; + coord.y = y; + SendPM(thisclient, "teleport to map: %i",map); + MapList.Index[map]->TeleportPlayer( thisclient, coord, false ); + Log( MSG_GMACTION, " %s : /go %i" , thisclient->CharInfo->charname, loc); + } + return true; + } + else if(strcmp(command, "p")==0) //*** READ THE PACKET.TXT AND SEND IT + { + if(Config.Command_Pak > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + + char buffer; + std::string fname = "packet/packet"; + fname += tmp; + fname += ".txt"; + FILE *packet1 = fopen(fname.c_str(),"r"); + if(packet1==NULL) + { + printf("Error opening packet.txt!\n"); + return true; + } + unsigned int comm = 0; + fread( &comm, 1, 2, packet1 ); + BEGINPACKET(pak,comm); + while((fscanf(packet1,"%c",&buffer))!=EOF) + ADDBYTE(pak,buffer); + thisclient->client->SendPacket( &pak ); + fclose(packet1); + } + else if (strcmp(command, "mybuffs")==0) + { + if(thisclient->Session->accesslevel < 300) + return true; + for(int i=0;i<15;i++) + { + if(thisclient->MagicStatus[i].Buff != 0) + { + // don't do anything for now. Still working on this concept. + Log(MSG_GMACTION, "MyBuffs command called. Not yet functional"); + } + } + } + else if (strcmp(command, "level")==0) + { + if(Config.Command_Level > thisclient->Session->accesslevel) + return true; + char* name; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned level=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) name = thisclient->CharInfo->charname; else name = tmp; + Log( MSG_GMACTION, " %s : /level %i %s" , thisclient->CharInfo->charname, level, name); + return pakGMLevel( thisclient, level, name ); + } + else if (strcmp(command, "info")==0) + { + if(Config.Command_Info > thisclient->Session->accesslevel) + return true; + Log( MSG_GMACTION, " %s : /info" , thisclient->CharInfo->charname); + thisclient->GetPlayerInfo( ); + return true; + } + else if (strcmp(command, "exp")==0) + { + if(Config.Command_Exp > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned exp=atoi(tmp); + char* name; + if ((tmp = strtok(NULL, " "))==NULL) + name = thisclient->CharInfo->charname; + else + name = tmp; + CPlayer* otherclient = GetClientByCharName( name ); + if (otherclient == NULL) + return true; + otherclient->CharInfo->Exp += exp; + BEGINPACKET( pak, 0x79b ); + ADDDWORD ( pak, otherclient->CharInfo->Exp ); + ADDWORD ( pak, otherclient->CharInfo->stamina ); + ADDWORD ( pak, 0 ); + otherclient->client->SendPacket( &pak ); + Log( MSG_GMACTION, " %s : /exp %i %s" , thisclient->CharInfo->charname, exp, name); + } + else if (strcmp(command, "tdmon") == 0) + { + if(Config.Command_Mon > thisclient->Session->accesslevel) + return true; + unsigned montype = 0; + unsigned moncount = 1; + unsigned monAI = 331; //TD Mob AI + if ((tmp = strtok(NULL, " "))==NULL) return true; montype = atoi(tmp); + if ((tmp = strtok(NULL, " "))!=NULL)moncount = atoi(tmp); + Log( MSG_GMACTION, " %s : spawned %i TD Mobs of type %i" , thisclient->CharInfo->charname, moncount, montype); + return pakGMMon( thisclient, montype, moncount, monAI ); + } + else if (strcmp(command, "mon") == 0) + { + if(Config.Command_Mon > thisclient->Session->accesslevel) + return true; + unsigned montype = 0; + unsigned moncount = 1; + unsigned monAI = 0; //default AI. Monster gets whatever AI is normal for its type + if ((tmp = strtok(NULL, " "))==NULL) return true; montype = atoi(tmp); + if ((tmp = strtok(NULL, " "))!=NULL)moncount = atoi(tmp); + if ((tmp = strtok(NULL, " "))!=NULL)monAI = atoi(tmp); + Log( MSG_GMACTION, " %s : spawned %i monsters of type %i with AI %i" , thisclient->CharInfo->charname, moncount, montype, monAI); + return pakGMMon( thisclient, montype, moncount, monAI ); + } + else if (strcmp(command, "kick")==0) + { + if(Config.Command_Kick > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + CPlayer* otherclient = GServer->GetClientByCharName(name); + if (otherclient == NULL) + return true; + if(thisclient->Session->accesslevel <= otherclient->Session->accesslevel) + { + SendPM(thisclient,"You cannot kick that player. His access level is as high as yours"); + SendPM(otherclient,"LOL %s just tried to kick you but his access level isn't higher than yours"); + Log( MSG_GMACTION, " %s : /kick %s" , thisclient->CharInfo->charname, name); + return true; + } + else + { + Log( MSG_GMACTION, " %s : /kick %s" , thisclient->CharInfo->charname, name); + return pakGMKick( thisclient, name ); + } + } + else if (strcmp(command, "job")==0) // *** Change Job ***** + { + if(Config.Command_Job > thisclient->Session->accesslevel) + return true; + //char* tmp; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* job=tmp; + Log( MSG_GMACTION, " %s : /job %s" , thisclient->CharInfo->charname, job); + if(thisclient->CharInfo->Job != 0 || thisclient->Stats->Level<10) + return true; + if(strcmp(job,"soldier")==0) + { + BEGINPACKET(pak, 0x721); + ADDWORD (pak, 0x0004); + ADDWORD (pak, 0x006f); + ADDWORD (pak, 0x0000); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x730); + ADDBYTE(pak, 0x05); + ADDBYTE(pak, 0x00); + ADDWORD(pak, 0xf52f); + ADDWORD(pak, 0x2964); + thisclient->client->SendPacket( &pak ); + thisclient->CharInfo->Job=111; + } + else + if(strcmp(job,"muse")==0) + { + BEGINPACKET(pak, 0x721); + ADDWORD (pak, 0x0004); + ADDWORD (pak, 0x00D3); + ADDWORD (pak, 0x0000); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x730); + ADDBYTE(pak, 0x05); + ADDBYTE(pak, 0x00); + ADDWORD(pak, 0xdc3b); + ADDWORD(pak, 0x20cd); + thisclient->client->SendPacket( &pak ); + thisclient->CharInfo->Job=211; + } + else + if(strcmp(job,"hawker")==0) + { + BEGINPACKET(pak, 0x721); + ADDWORD (pak, 0x0004); + ADDWORD (pak, 0x0137); + ADDWORD (pak, 0x0000); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x730); + ADDBYTE(pak, 0x05); + ADDBYTE(pak, 0x00); + ADDWORD(pak, 0x1104); + ADDWORD(pak, 0x5150); + thisclient->client->SendPacket( &pak ); + thisclient->CharInfo->Job=311; + } + else + if(strcmp(job,"dealer")==0) + { + BEGINPACKET(pak, 0x721); + ADDWORD (pak, 0x0004); + ADDWORD (pak, 0x019b); + ADDWORD (pak, 0x0000); + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x730); + ADDBYTE(pak, 0x05); + ADDBYTE(pak, 0x00); + ADDWORD(pak, 0x4c1c); + ADDWORD(pak, 0xef69); + thisclient->client->SendPacket( &pak ); + thisclient->CharInfo->Job=411; + } + } + else if (strcmp(command, "set")==0) + { + if(Config.Command_Set > thisclient->Session->accesslevel) + return true; + int refine; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + int id=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + refine = 0; + else + refine=atoi(tmp); + refine *= 16; + BEGINPACKET( pak, 0); + if(EquipList[2].Index[id+100]!=NULL) + { + if(id > 200) + { + thisclient->items[2].itemnum = id+100; //hats are always 100 positions higher so this should make set line up + } + else + { + thisclient->items[2].itemnum = id; + } + thisclient->items[2].itemtype = 2; + thisclient->items[2].refine =refine; + thisclient->items[2].durability = 50; + thisclient->items[2].lifespan = 100; + thisclient->items[2].count = 1; + thisclient->items[2].stats = 0; + thisclient->items[2].socketed = false; + thisclient->items[2].appraised = true; + thisclient->items[2].gem = 0; + thisclient->UpdateInventory( 2 ); + + RESETPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, 0x0002); + ADDWORD( pak, id+100); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[2] ) ); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE + SendToVisible( &pak,thisclient ); + } + if(EquipList[3].Index[id]!=NULL) + { + thisclient->items[3].itemnum = id; + thisclient->items[3].itemtype = 3; + thisclient->items[3].refine =refine; + thisclient->items[3].durability = 50; + thisclient->items[3].lifespan = 100; + thisclient->items[3].count = 1; + thisclient->items[3].stats = 0; + thisclient->items[3].socketed = false; + thisclient->items[3].appraised = true; + thisclient->items[3].gem = 0; + thisclient->UpdateInventory( 3 ); + + RESETPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, 0x0003); + ADDWORD( pak, id); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[3] )); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE + SendToVisible( &pak,thisclient ); + } + if(EquipList[4].Index[id]!=NULL) + { + thisclient->items[5].itemnum = id; + thisclient->items[5].itemtype = 4; + thisclient->items[5].refine =refine; + thisclient->items[5].durability = 50; + thisclient->items[5].lifespan = 100; + thisclient->items[5].count = 1; + thisclient->items[5].stats = 0; + thisclient->items[5].socketed = false; + thisclient->items[5].appraised = true; + thisclient->items[5].gem = 0; + + thisclient->UpdateInventory( 4 ); + + RESETPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, 5); + ADDWORD( pak, id); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[5] )); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE 2602 + SendToVisible( &pak,thisclient ); + } + if(EquipList[5].Index[id]!=NULL) + { + thisclient->items[6].itemnum = id; + thisclient->items[6].itemtype = 5; + thisclient->items[6].refine =refine; + thisclient->items[6].durability = 50; + thisclient->items[6].lifespan = 100; + thisclient->items[6].count = 1; + thisclient->items[6].stats = 0; + thisclient->items[6].socketed = false; + thisclient->items[6].appraised = true; + thisclient->items[6].gem = 0; + + thisclient->UpdateInventory( 6 ); + + RESETPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, 6); + ADDWORD( pak, id); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[6] )); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed); // REFINE + SendToVisible( &pak,thisclient ); + } + thisclient->SetStats( ); + } + else if (strcmp(command, "cha")==0) + { + if(Config.Command_Cha > thisclient->Session->accesslevel) + return true; + int slot; + int tipo; + int id; + int stats; + int socket; + int refine; + if ((tmp = strtok(NULL, " "))==NULL)return true; + if(strcmp(tmp, "mask")==0) + { + slot=1; + tipo=1; + } + else + if(strcmp(tmp, "cap")==0) + { + slot=2; + tipo=2; + } + else + if(strcmp(tmp, "suit")==0) + { + slot=3; + tipo=3; + } + else + if(strcmp(tmp, "back")==0) + { + slot=4; + tipo=6; + } + else + if(strcmp(tmp, "glov")==0) + { + slot=5; + tipo=4; + } + else + if(strcmp(tmp, "shoe")==0) + { + slot=6; + tipo=5; + } + else + if(strcmp(tmp, "weap")==0) + { + slot=7; + tipo=8; + } + else + if(strcmp(tmp, "shield")==0) + { + slot=8; + tipo=9; + } + else + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + id=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + stats=0; + else + stats=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + socket=0; + else + socket=1; + if ((tmp = strtok(NULL, " "))==NULL) + refine=0; + else + refine=atoi(tmp); + thisclient->items[slot].itemnum = id; + thisclient->items[slot].itemtype = tipo; + thisclient->items[slot].refine = refine; + thisclient->items[slot].durability = 50; + thisclient->items[slot].lifespan = 100; + thisclient->items[slot].count = 1; + thisclient->items[slot].stats = stats; + thisclient->items[slot].socketed = socket; + thisclient->items[slot].appraised = true; + if(stats > 300) + { + thisclient->items[slot].gem = stats; + thisclient->items[slot].socketed = 1; + } + else + { + thisclient->items[slot].gem = 0; + } + + BEGINPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, slot); + ADDWORD( pak, id); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[slot] )); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE 2602 + SendToVisible( &pak,thisclient ); + + thisclient->UpdateInventory( slot ); + thisclient->SetStats( ); + return true; + } + else if (strcmp(command, "learnskill")==0) + { + if (thisclient->Session->accesslevel < 300) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT skillId = atoi(tmp); + Log( MSG_GMACTION, " %s : /learnskill %i", thisclient->CharInfo->charname, skillId); + if (skillId < 0) return false; + if (skillId > SkillList.size()) return false; + LearnSkill(thisclient, skillId); + return true; + } + else if (strcmp(command, "spawn")==0) + { + if(Config.Command_ESpawn > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + int mobid; + int max; + int radius; + int stime; + int spawntype; + int triggermontype; + int triggeramount; + int lim; + int spawnk; + int aggro; + int refvar; //Objvar NPC reference + int refval; //Objvar array reference + + if ((tmp = strtok(NULL, " "))==NULL) + return true; + else + mobid = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + return true; + else + max = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + return true; + else + radius = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + return true; + else + stime = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + return true; + else + spawntype = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + triggermontype = 0; + else + triggermontype = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + triggeramount = 0; + else + triggeramount = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + lim = 0; + else + lim = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + spawnk = 1; + else + spawnk = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + aggro = 1; + else + aggro = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + refvar = 0; + else + refvar = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + refval = 0; + else + refval = atoi(tmp); + + GServer->DB->QExecute("INSERT INTO list_special_spawns (map,montype,min,max,respawntime,x,y,radius,agressive,triggerammount,triggermontype,spawntype,areatrigger,lim,spawnk,refvar,refval) VALUES(%i,%i,%i,%i,%i,%f,%f,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)", + thisclient->Position->Map,mobid,0,max,stime,thisclient->Position->current.x,thisclient->Position->current.y,radius,aggro,triggeramount,triggermontype,spawntype,1,lim,spawnk,refvar,refval ); + CSpawnArea* thisspawn = new (nothrow) CSpawnArea; + if(thisspawn == NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + DB->QFree( ); + return false; + } + GServer->maxSpawnId++; + thisspawn->id = GServer->maxSpawnId; + thisspawn->map = thisclient->Position->Map; + thisspawn->montype = mobid; + thisspawn->min = 0;//atoi(row[3]); + thisspawn->max = max; + thisspawn->respawntime = stime; + thisspawn->point.x = thisclient->Position->current.x; + thisspawn->point.y = thisclient->Position->current.y; + thisspawn->radius = radius; + thisspawn->amon = 0; + thisspawn->lastRespawnTime = 0; + thisspawn->type = spawntype; + thisspawn->triggertype = triggermontype; + thisspawn->triggeramount = triggeramount; + thisspawn->aggressive = aggro; + thisspawn->typeoftrigger = 1; + thisspawn->limit = lim; + thisspawn->spawnkey = spawnk; + thisspawn->triggercount = 0; + thisspawn->thisnpc = GetNPCDataByID( thisspawn->montype ); + thisspawn->RefVar = refvar; + thisspawn->RefVal = refval; + if(thisspawn->thisnpc == NULL) + { + SendPM(thisclient, "Invalid montype - Spawn %i will not be added", thisspawn->id ); + delete thisspawn; + return true; + } + MapList.Index[thisspawn->map]->MonsterSpawnList.push_back( thisspawn ); + SendPM(thisclient,"Spawn successfully saved with spawn id %i.",thisspawn->id); + return true; + } + else if (strcmp(command, "refine")==0) + { + if(thisclient->Session->accesslevel < 300) + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + int slot = 7; //defaults to weapon + int tipo; + int itemrefine; + if(strcmp(tmp, "mask")==0) + { + slot=1; + } + else + if(strcmp(tmp, "cap")==0) + { + slot=2; + } + else + if(strcmp(tmp, "suit")==0) + { + slot=3; + } + else + if(strcmp(tmp, "back")==0) + { + slot=4; + } + else + if(strcmp(tmp, "glov")==0) + { + slot=5; + } + else + if(strcmp(tmp, "shoe")==0) + { + slot=6; + } + else + if(strcmp(tmp, "weap")==0) + { + slot=7; + } + else + if(strcmp(tmp, "shield")==0) + { + slot=8; + } + if ((tmp = strtok(NULL, " "))==NULL) + itemrefine =0; + else + itemrefine = atoi(tmp)<10?atoi(tmp)*16:9*16; + thisclient->items[slot].refine = itemrefine; + + BEGINPACKET( pak, 0x7a5); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, slot); + ADDWORD( pak, thisclient->items[slot].itemnum); // ITEM NUM + ADDWORD( pak, BuildItemRefine( thisclient->items[slot] )); // REFINE + ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE 2602 + SendToVisible( &pak,thisclient ); + + thisclient->UpdateInventory( slot ); + thisclient->SetStats( ); + return true; + } + else if (strcmp(command, "itemstat")==0) + { + if(thisclient->Session->accesslevel < 300) + return true; + if ((tmp = strtok(NULL, " "))==NULL)return true; + int slot = 7; //defaults to weapon + int tipo; + int itemstat; + if(strcmp(tmp, "mask")==0) + { + slot=1; + } + else + if(strcmp(tmp, "cap")==0) + { + slot=2; + } + else + if(strcmp(tmp, "suit")==0) + { + slot=3; + } + else + if(strcmp(tmp, "back")==0) + { + slot=4; + } + else + if(strcmp(tmp, "glov")==0) + { + slot=5; + } + else + if(strcmp(tmp, "shoe")==0) + { + slot=6; + } + else + if(strcmp(tmp, "weap")==0) + { + slot=7; + } + else + if(strcmp(tmp, "shield")==0) + { + slot=8; + } + if ((tmp = strtok(NULL, " "))==NULL) + itemstat = 0; + else + itemstat = atoi(tmp); + thisclient->items[slot].stats= itemstat; + if(itemstat > 300) + { + thisclient->items[slot].socketed = 1; + thisclient->items[slot].gem = itemstat; + } + else + thisclient->items[slot].gem = 0; + thisclient->items[slot].appraised = true; + + //BEGINPACKET( pak, 0x7a5); + //ADDWORD( pak, thisclient->clientid ); + //ADDWORD( pak, slot); + //ADDWORD( pak, thisclient->items[slot].itemnum); // ITEM NUM + //ADDWORD( pak, BuildItemRefine( thisclient->items[slot] )); // REFINE + //ADDWORD( pak, thisclient->Stats->Move_Speed ); // REFINE 2602 + //SendToVisible( &pak,thisclient ); + + BEGINPACKET( pak,0x7a5 ); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, slot ); + ADDDWORD( pak, BuildItemShow(thisclient->items[slot])); // ITEM + ADDWORD ( pak, thisclient->Stats->Move_Speed); + SendToVisible( &pak, thisclient ); + + thisclient->UpdateInventory( slot ); + thisclient->SetStats( ); + return true; + } + else if (strcmp(command, "item")==0)//Modified by Hiei (added refine/socket/stats) + { + if(Config.Command_Item > thisclient->Session->accesslevel) + return true; + UINT itemrefine, itemstats, itemls, itemsocket; + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemid =atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemtype =atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemamount =atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + itemrefine =0; + else + itemrefine = atoi(tmp) < 10?atoi(tmp)*16:9*16; + if ((tmp = strtok(NULL, " ")) == NULL) + itemls = 100; + else + itemls = atoi(tmp); + if ((tmp = strtok(NULL, " ")) == NULL) + itemsocket = 0; + else + itemsocket = atoi(tmp)==0?false:true; + if ((tmp = strtok(NULL, " "))==NULL) + itemstats =0; + else + itemstats =atoi(tmp); + Log( MSG_GMACTION, " %s : /item %i,%i,%i,%i,%i,%i" , thisclient->CharInfo->charname, itemid, itemtype, itemamount , itemrefine , itemsocket ,itemstats); + return pakGMItem( thisclient , itemid , itemtype , itemamount , itemrefine , itemls, itemstats , itemsocket ); + } + else if (strcmp(command, "levelup")==0 || strcmp(command,"l")==0) + { + if(Config.Command_LevelUp > thisclient->Session->accesslevel) + return true; + Log( MSG_GMACTION, " %s : /levelup" , thisclient->CharInfo->charname); + thisclient->CharInfo->Exp += thisclient->GetLevelEXP(); + } + else if (strcmp(command, "drop")==0) + { + if(Config.Command_Drop > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned itemtype=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned itemid=atoi(tmp); + unsigned int itemcount = 0; + if ((tmp = strtok(NULL, " "))==NULL) + itemcount = 1; + else + itemcount = atoi(tmp); + if(itemtype < 10 || itemtype > 12 ) + itemcount = 1; + Log( MSG_GMACTION, " %s : /drop %i,%i" , thisclient->CharInfo->charname, itemtype, itemid); + + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GetNewClientID(); + thisdrop->type = 2; + thisdrop->pos.x = thisclient->Position->current.x; + thisdrop->pos.y = thisclient->Position->current.y; + thisdrop->posMap = thisclient->Position->Map; + thisdrop->droptime = time(NULL); + + ClearItem(thisdrop->item); + thisdrop->item.itemnum = itemid; + thisdrop->item.itemtype = itemtype; + thisdrop->item.count = itemcount; + thisdrop->item.refine = 0; + thisdrop->item.durability = 35; + thisdrop->item.lifespan = 100; + thisdrop->item.appraised = true; + thisdrop->item.socketed = false; + thisdrop->item.stats = 0; + thisdrop->item.gem = 0; + + thisdrop->amount = 1; + thisdrop->owner = 0; + CMap* map = MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + else if(strcmp(command, "givezuly")==0) + { + if(Config.Command_GiveZuly > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; char* name = tmp; + if((tmp = strtok(NULL, " ")) == NULL) return true; int zuly = atoi(tmp); + Log( MSG_GMACTION, " %s : /givezuly %s, %i" , thisclient->CharInfo->charname, name, zuly); + return pakGMZulygive(thisclient, name, zuly); + } + else if(strcmp(command, "unionpoints") == 0) + { + if(Config.Command_GiveZuly > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " ")) == NULL) return true; char* name = tmp; + if((tmp = strtok(NULL, " ")) == NULL) return true; int Upoints = atoi(tmp); + Log( MSG_GMACTION, " %s : /unionpoints %s, %i" , thisclient->CharInfo->charname, name, Upoints); + + return pakGMUnionPoints(thisclient, name, Upoints); + } + else if(strcmp(command, "npc")==0) + { + if(Config.Command_Npc > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned npcid=atoi(tmp); + unsigned npcdialog = 0; + if((tmp = strtok(NULL, " "))!=NULL) + npcdialog=atoi(tmp); + Log( MSG_GMACTION, " %s : /npc %i, %i" , thisclient->CharInfo->charname, npcid, npcdialog); + return pakGMNpc(thisclient, npcid, npcdialog); + } + else if(strcmp(command, "givefairy") == 0) + { + if(Config.Command_GiveFairy > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; char* name = tmp; + if((tmp = strtok(NULL, " ")) == NULL) return true; unsigned mode = atoi(tmp); + Log( MSG_GMACTION, " %s : /givefairy %s, %i" , thisclient->CharInfo->charname, name, mode); + return pakGMFairyto(thisclient, name, mode); + } + else if(strcmp(command, "fairymode") == 0) + { + if(Config.Command_ManageFairy > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned mode=atoi(tmp); + Log( MSG_GMACTION, " %s : /fairymode mode: %i" , thisclient->CharInfo->charname, mode); + return pakGMManageFairy(thisclient, mode); + } + else if(strcmp(command, "fairywait")==0) + { + if(Config.Command_ChangeFairyWait > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned value=atoi(tmp); + Log( MSG_GMACTION, " %s : /fairywait value: %i" , thisclient->CharInfo->charname, value); + return pakGMChangeFairyWait(thisclient, value); + } + else if(strcmp(command, "fairystay")==0) + { + if(Config.Command_ChangeFairyStay > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned value=atoi(tmp); + Log( MSG_GMACTION, " %s : /fairystay value: %i" , thisclient->CharInfo->charname, value); + return pakGMChangeFairyStay(thisclient, value); + } + else if(strcmp(command, "fairytestmode")==0) + { + if(Config.Command_ChangeFairyTestMode > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned mode=atoi(tmp); + Log( MSG_GMACTION, " %s : /fairytestmode mode: %i" , thisclient->CharInfo->charname, mode); + return pakGMChangeFairyTestMode(thisclient, mode); + } + else if (strcmp(command, "move")==0) + { + if(Config.Command_Move > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned map=atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; float x=(float)atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; float y=(float)atoi(tmp); + Log( MSG_GMACTION, " %s : /move %s,%i,%i,%i" , thisclient->CharInfo->charname, name, map, x, y); + return pakGMTeleOtherPlayer(thisclient, name, map, x, y); + } + else if (strcmp(command, "goto")==0) + { + if(Config.Command_Goto > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + Log( MSG_GMACTION, " %s : /goto %s" , thisclient->CharInfo->charname, name); + return pakGMTeleToPlayer(thisclient, name); + } + else if (strcmp(command, "teletome")==0) + { + if(Config.Command_TeleToMe > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + Log( MSG_GMACTION, " %s : /teletome %s" , thisclient->CharInfo->charname, name); + if(strcmp(name,"all")==0) + { + return pakGMTeleAllHere(thisclient); + } + return pakGMTelePlayerHere(thisclient, name); + } + else if(strcmp(command, "CharInfo")==0) + { + if(Config.Command_PlayerInfo > thisclient->Session->accesslevel) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + Log( MSG_GMACTION, " %s : /CharInfo %s" , thisclient->CharInfo->charname, name); + return pakGMInfo(thisclient, name); + } + /*else if (strcmp(command, "give2")==0) + { + if(Config.Command_Give2 > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned itemid = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned itemtype = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned itemamount = atoi(tmp); + Log( MSG_GMACTION, " %s : /give2 %s,%i,%i,%i" , thisclient->CharInfo->charname, name, itemid, itemtype, itemamount); + return pakGMItemtoplayer( thisclient , name , itemid , itemtype , itemamount ); + + }*/ + else if (strcmp(command, "give2")==0) + { + if(Config.Command_Item > thisclient->Session->accesslevel) + return true; + UINT itemrefine, itemstats, itemls, itemsocket; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name = tmp; + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemid = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemtype = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; UINT itemamount = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + itemrefine =0; + else + itemrefine = atoi(tmp)<10?atoi(tmp)*16:9*16; + if ((tmp = strtok(NULL, " "))==NULL) + itemls = 100; + else + itemls = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL) + itemsocket = 0; + else + itemsocket =atoi(tmp)==0?false:true; + if ((tmp = strtok(NULL, " "))==NULL) + itemstats = 0; + else + itemstats =atoi(tmp); + Log( MSG_GMACTION, " %s : /give2 %s,%i,%i,%i,%i,%i,%i,%i" , thisclient->CharInfo->charname, name, itemid , itemtype , itemamount , itemrefine , itemls, itemstats , itemsocket); + return pakGMItemtoplayer( thisclient , name , itemid , itemtype , itemamount , itemrefine , itemls, itemstats , itemsocket ); + } + else if (strcmp(command, "ban")==0) + { + if(Config.Command_Ban > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* name=tmp; + Log( MSG_GMACTION, " %s : /ban %s" , thisclient->CharInfo->charname, name); + return pakGMBan( thisclient, name ); + } + else if (strcmp(command, "ani")==0) + { + if(Config.Command_Ani > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned anid =atoi(tmp); + Log( MSG_GMACTION, " %s : /ani %i" , thisclient->CharInfo->charname, anid); + pakGMDoEmote( thisclient, anid ); + } + else if(strcmp(command,"summon")==0) + { + if(Config.Command_Summon > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned int summon = atoi(tmp); + Log( MSG_GMACTION, " %s : /summon %i" , thisclient->CharInfo->charname, summon); + fPoint position = RandInCircle( thisclient->Position->current, 5 ); + CMap* map = MapList.Index[thisclient->Position->Map]; + map->AddMonster( summon, position, thisclient->clientid, 0,0,168 ); + //Start Animation + BEGINPACKET( pak, 0x7b2 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, 2802 ); + SendToVisible( &pak, thisclient ); + //Finish Animation + RESETPACKET( pak, 0x7bb ); + ADDWORD ( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient ); + //???? + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, thisclient->clientid); + ADDWORD ( pak, 1286 ); + SendToVisible( &pak, thisclient ); + // Add our Mob to the mobs list + } + else if (strcmp(command,"setxprate")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + unsigned int newxprate = atoi(tmp); + GServer->Config.EXP_RATE = newxprate; + Log( MSG_GMACTION, "EXP Rate set to %i" , newxprate); + } + else if (strcmp(command,"setdroprate")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + unsigned int newdroprate = atoi(tmp); + GServer->Config.DROP_RATE= newdroprate; + Log( MSG_GMACTION, "Drop Rate set to %i" , newdroprate); + } + else if (strcmp(command,"setzulyrate")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + unsigned int newzulyrate = atoi(tmp); + GServer->Config.ZULY_RATE = newzulyrate; + Log( MSG_GMACTION, "Zuly Rate set to %i" , newzulyrate); + } + else if (strcmp(command,"delwp")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + int counter = 0; + for(int i=0;i<100;i++) + { + + if(GServer->WPList[thisclient->Position->Map][i].WPType != 0) + { + GServer->WPList[thisclient->Position->Map][i].WPType = 0; + GServer->WPList[thisclient->Position->Map][i].X = 0; + GServer->WPList[thisclient->Position->Map][i].Y = 0; + counter++; + } + } + if(counter > 0) + { + SendPM(thisclient, "All WayPoints in this map have been deleted. Use /savewp to make the change in the DB too"); + return true; + } + SendPM(thisclient, "No Waypoints were found for this map"); + return true; + } + else if (strcmp(command,"setwp")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + { + SendPM(thisclient, "Sorry! You do not have access to this command"); + return true; + } + if ((tmp = strtok(NULL, " "))==NULL) + return true; + unsigned int wptype = atoi(tmp); + if(wptype > 3 ) + { + SendPM(thisclient, "Data not saved. WP Type cannot be greater than 3"); + return true; + } + if(wptype == 0) + { + SendPM(thisclient, "Data not saved. WP Type cannot be zero"); + return true; + } + SendPM(thisclient, "REMINDER. Changes made here are not permanent. please use /savewp to make the change in the DB too"); + UINT nextentry = 1; + bool firstentry = false; + bool finalentry = false; + UINT mapid = thisclient->Position->Map; + for(int i=1;i<100;i++) //let's see what is already set for this map + { + int j = GServer->WPList[mapid][i].WPType; + if(j > 0) + { + nextentry++; + if(j == 1) + firstentry = true; + if(j == 3) + finalentry = true; + } + } + if(wptype == 1) //first Way Point + { + if(firstentry == true) + { + SendPM(thisclient, "The path for this map already contains a start point. You will need to delete the list before adding a new one"); + return true; + } + GServer->WPList[mapid][nextentry].WPType = 1; + GServer->WPList[mapid][nextentry].X = thisclient->Position->current.x; + GServer->WPList[mapid][nextentry].Y = thisclient->Position->current.y; + SendPM(thisclient, "A new TDef Start point has been added for this map"); + return true; + } + if(wptype == 2) //regular Way Point + { + if(firstentry == false) + { + SendPM(thisclient, "The path for this map does not contain a start point. You will need to add one before you can add regular waypoints"); + return true; + } + if(finalentry == true) + { + SendPM(thisclient, "The path for this map already contains a Final destination point. You cannot add more regular way points until you delete it"); + return true; + } + GServer->WPList[mapid][nextentry].WPType = 2; + GServer->WPList[mapid][nextentry].X = thisclient->Position->current.x; + GServer->WPList[mapid][nextentry].Y = thisclient->Position->current.y; + SendPM(thisclient, "A new TDef Way Point has been added for this map"); + return true; + } + if(wptype == 3) //Final Way Point + { + if(finalentry == true) + { + SendPM(thisclient, "The path for this map already contains a Final destination point. You will need to delete the list before adding a new one"); + return true; + } + GServer->WPList[mapid][nextentry].WPType = 3; + GServer->WPList[mapid][nextentry].X = thisclient->Position->current.x; + GServer->WPList[mapid][nextentry].Y = thisclient->Position->current.y; + SendPM(thisclient, "A new TDef Final Point has been added for this map"); + return true; + } + SendPM(thisclient, "Unrecognised SetWP command"); + return true; //just to catch any non handled stuff + } + else if (strcmp(command,"savewp")==0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + bool firstentry = false; + bool finalentry = false; + UINT mapid = thisclient->Position->Map; + for(int i=1;i<100;i++) //let's see what is already set for this map + { + int j = GServer->WPList[mapid][i].WPType; + if(j > 0) + { + if(j == 1) + firstentry = true; + if(j == 3) + finalentry = true; + } + } + if(firstentry == false) + { + SendPM(thisclient, "Cannot save to the database. The WayPoint list for this map contains no start point."); + return true; + } + if(finalentry == false) + { + SendPM(thisclient, "Cannot save to the database. The WayPoint list for this map contains no final destination point."); + return true; + } + //save the data + GServer->DB->QExecute("DELETE FROM list_waypoints WHERE mapid=%i", mapid); + for(int j=1;j<100;j++) + { + if(GServer->WPList[mapid][j].WPType != 0) + GServer->DB->QExecute("INSERT INTO list_waypoints (mapid,wpnum,mapx,mapy,wptype) VALUES(%i,%i,%f,%f,%i)", + mapid, j, GServer->WPList[mapid][j].X, GServer->WPList[mapid][j].Y,GServer->WPList[mapid][j].WPType); + } + SendPM(thisclient, "The Waypoint list for this map has been saved to the database."); + return true; + } + else if (strcmp(command,"checkwp") == 0) + { + if(thisclient->Session->accesslevel < 900 || thisclient->CharInfo->IsDev == 0) + return true; + UINT mapid = thisclient->Position->Map; + for(unsigned int i=0;i<20;i++) + { + SendPM(thisclient, "Waypoint %i type is %i",i,GServer->WPList[mapid][i].WPType); + } + } + else if (strcmp(command, "shutdown")==0) + { + if(Config.Command_Shutdown > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned int minutes =atoi(tmp); + char text[200]; + sprintf( text, "Server will shutdown in %i minutes, Please logout NOW to be sure your information is saved correctly.", + minutes ); + BEGINPACKET( pak, 0x702 ); + ADDSTRING ( pak, thisclient->CharInfo->charname ); + ADDSTRING ( pak, "> " ); + ADDSTRING ( pak, text ); + ADDBYTE ( pak, 0x00 ); + SendToAll ( &pak ); + pthread_create( &WorldThread[SHUTDOWN_THREAD], NULL, (void *(__cdecl *)(void *))ShutdownServer, (PVOID)minutes); + Log( MSG_GMACTION, " %s : /shutdown %u" , thisclient->CharInfo->charname, shutdown); + } + else if(strcmp(command, "dquest")==0) // toggle quest debug mode + { + if(Config.Command_DQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + char buffer2[200]; + if( thisclient->questdebug ) + { + thisclient->questdebug = false; + sprintf ( buffer2, "Quest Debug Mode disabled" ); + SendPM(thisclient, buffer2); + } + else + { + thisclient->questdebug = true; + sprintf ( buffer2, "Quest Debug Mode enabled" ); + SendPM(thisclient, buffer2); + } + return true; + } + else if(strcmp(command, "debugmode")==0) //sets the GM into debugmode. PMs will be sent at various parts of the code + { + if(thisclient->Session->accesslevel <= 100) + return true; + char buffer2[200]; + if( thisclient->Session->codedebug || thisclient->CharInfo->IsDev == 0) + { + thisclient->Session->codedebug = false; + + sprintf ( buffer2, "Debug Mode disabled" ); + SendPM(thisclient, buffer2); + } + else + { + thisclient->Session->codedebug = true; + sprintf ( buffer2, "Debug Mode enabled" ); + SendPM(thisclient, buffer2); + } + return true; + } + else if(strcmp(command, "iquest")==0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + int n=1; + if ((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned int itemquest = atoi(tmp); + if ((tmp = strtok(NULL, " ")) != NULL) + n = atoi(tmp); + if( thisclient->questdebug ) + { + for(int i=0;iclient->SendPacket( &pak ); + } + } + return true; + } + else if(strcmp(command, "addquest")==0) + { + + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned int questnum =atoi(tmp); + Log( MSG_GMACTION, "add quest %i" , questnum); + for(int i=0;i<10;i++) + { + if (thisclient->quest.quests[i].QuestID != 0) continue; + thisclient->quest.quests[i].QuestID = questnum; + thisclient->quest.quests[i].StartTime = clock_t(); + Log( MSG_GMACTION, "quest added %i" , thisclient->quest.quests[i].QuestID); + + } + } + else if(strcmp(command, "listquest")==0) + { + + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + SendPM( thisclient, "Current Quests:" ); + for(int i=0;i<10;i++) + { + if (thisclient->quest.quests[i].QuestID == 0) continue; + SendPM( thisclient, "Quest[%i]: %i[%i] %i[%i] %i[%i] %i[%i] %i[%i]" , thisclient->quest.quests[i].QuestID, thisclient->quest.quests[i].Items[0].itemnum, thisclient->quest.quests[i].Items[0].count, thisclient->quest.quests[i].Items[1].itemnum, thisclient->quest.quests[i].Items[1].count, thisclient->quest.quests[i].Items[2].itemnum,thisclient->quest.quests[i].Items[2].count,thisclient->quest.quests[i].Items[3].itemnum,thisclient->quest.quests[i].Items[3].count,thisclient->quest.quests[i].Items[4].itemnum,thisclient->quest.quests[i].Items[4].count); + } + } + else if(strcmp(command, "listqflag") == 0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + SendPM( thisclient, "The following quest flags are set: "); + //for (dword i = 0; i < 0x40; i++) + //{ + // char buf2[5]; + // sprintf(buf2, "%i ", thisclient->quest.flags[i]); + // buffer.append(buf2); + // if (i > 0 && i%10 == 0) + // { + // SendPM(thisclient, (char*)buffer.c_str()); + // buffer = ""; + // } + //} + for (int i=0;i<64;i++) + { + for(int j=0;j<8;j++) + { + if(thisclient->quest.Qflags[i][j] == 1) + { + int flagval = i*8 + j; + SendPM( thisclient, "flag %i",flagval); + } + } + } + } + else if(strcmp(command, "listqswitch") == 0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + SendPM( thisclient, "The following quest switches are set: "); + char buffer[200]; + for(unsigned int j=0;j<=5;j++) + { + sprintf(buffer, "Switch [%i] = %02x ", j, thisclient->quest.quests[0].Switches[j]); + SendPM( thisclient, buffer); + } + } + else if(strcmp(command, "listqvar") == 0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + SendPM( thisclient, "Quest Variables"); + + string buffer = "Episode: "; + for(dword i = 0; i < 5; i++) + { + char buf2[5]; + sprintf(buf2, "%02x ", thisclient->quest.EpisodeVar[i]); + buffer.append(buf2); + } + SendPM( thisclient, (char*)buffer.c_str() ); + + buffer = "Job: "; + for(dword i = 0; i < 3; i++) + { + char buf2[5]; + sprintf(buf2, "%02x ", thisclient->quest.JobVar[i]); + buffer.append(buf2); + } + SendPM( thisclient, (char*)buffer.c_str()); + + buffer = "Planet: "; + for(dword i = 0; i < 7; i++) + { + char buf2[5]; + sprintf(buf2, "%02x ", thisclient->quest.PlanetVar[i]); + buffer.append(buf2); + } + SendPM( thisclient, (char*)buffer.c_str()); + + buffer = "Union: "; + for(dword i = 0; i < 10; i++) + { + char buf2[5]; + sprintf(buf2, "%02x ", thisclient->quest.UnionVar[i]); + buffer.append(buf2); + } + SendPM( thisclient, (char*)buffer.c_str()); + + for (dword j = 0; j < 10; j++) + { + if (thisclient->quest.quests[j].QuestID == 0) continue; + buffer = "Quest: "; + char buf3[5]; + sprintf(buf3, "%i ",thisclient->quest.quests[j].QuestID); + buffer.append(buf3); + for(dword i = 0; i < 10; i++) + { + char buf2[5]; + sprintf(buf2, "%02x ", thisclient->quest.quests[j].Variables[i]); + buffer.append(buf2); + } + SendPM( thisclient, (char*)buffer.c_str()); + } + } + else if(strcmp(command, "setqvar") == 0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned int vartype = atoi(tmp); + if ((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned int var = atoi(tmp); + if ((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned int val = atoi(tmp); + if (vartype > 3) return true; + switch (vartype) + { + case 0: + if (var >= 5) return true; + thisclient->quest.EpisodeVar[var] = val; + break; + case 1: + if (var >= 3) return true; + thisclient->quest.JobVar[var] = val; + break; + case 2: + if (var >= 7) return true; + thisclient->quest.PlanetVar[var] = val; + break; + case 3: + if (var >= 10) return true; + thisclient->quest.UnionVar[var] = val; + break; + default: + return true; + break; + } + SendPM( thisclient, "Set Var[%i][%i] = %i", vartype, var, val); + } + else if (strcmp(command, "qtrigger")==0) + { + if(Config.Command_IQuest > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + char* tempName; + int mode; + int success; + if ((tmp = strtok(NULL, " ")) == NULL)return true; tempName = tmp; + dword hash = MakeStrHash(tempName); + if ((tmp = strtok(NULL, " ")) == NULL) + mode = 0; + else + mode = 1; + SendPM(thisclient, "Execute Quest Trigger %s hash: [%08x]", tempName, hash); + success = thisclient->ExecuteQuestTrigger(hash); + SendPM(thisclient, "Quest returned %i", success); + if(mode = 1) + success = atoi(tmp); + SendPM(thisclient, "Quest value sent %i", success); + CPacket pakout(0x730); + pakout.AddByte(success); + pakout.AddByte(0); + pakout.AddDWord(hash); + thisclient->client->SendPacket(&pakout); + } + else if (strcmp(command, "shoptype")==0) + { + if(Config.Command_ShopType > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) return true; + unsigned int shoptype = atoi(tmp); + thisclient->Shop->ShopType = shoptype; + Log( MSG_GMACTION, " %s : /shoptype %i" , thisclient->CharInfo->charname, shoptype); + } + else if (strcmp(command, "stat")==0) /// Code By Minoc + { + if(Config.Command_Stat > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; char* statname =(char*)tmp; + if ((tmp = strtok(NULL, " "))==NULL) return true; int statvalue = atoi(tmp); + Log( MSG_GMACTION, " %s : /stat %s,%i" , thisclient->CharInfo->charname, statname, statvalue); + return pakGMStat( thisclient , statname , statvalue); + } + else if (strcmp(command, "statp")==0) + { + if (Config.Command_Stat > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; int statvalue = atoi(tmp); + char* name; + if ((tmp = strtok(NULL, " "))==NULL) + name = thisclient->CharInfo->charname; + else + name = tmp; + CPlayer* otherclient = GetClientByCharName(name); + if (otherclient == NULL) + return true; + if ((int)otherclient->CharInfo->StatPoints + statvalue < 0) + otherclient->CharInfo->StatPoints = 0; + else + otherclient->CharInfo->StatPoints += statvalue; + BEGINPACKET( pak, 0x720 ); + ADDWORD( pak, 32 ); + ADDWORD( pak, statvalue ); + ADDWORD( pak, 0 ); + otherclient->client->SendPacket( &pak ); + RESETPACKET( pak, 0x0730 ); + ADDWORD( pak, 5 ); + ADDWORD( pak, 0xa24d ); + ADDWORD( pak, 0x40b3 ); + otherclient->client->SendPacket( &pak ); + Log(MSG_GMACTION, " %s : /stats %i %s", thisclient->CharInfo->charname, statvalue, name); + return true; + } + else if (strcmp(command, "skillp")==0) + { + if (Config.Command_Stat > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; int skillp = atoi(tmp); + char* name; + if ((tmp = strtok(NULL, " "))==NULL) + name = thisclient->CharInfo->charname; + else + name = tmp; + CPlayer* otherclient = GetClientByCharName(name); + if (otherclient == NULL) + return true; + if ((int)otherclient->CharInfo->SkillPoints + skillp < 0) + otherclient->CharInfo->SkillPoints = 0; + else + otherclient->CharInfo->SkillPoints += skillp; + BEGINPACKET( pak, 0x720 ); + ADDWORD( pak, 37 ); + ADDWORD( pak, skillp ); + ADDWORD( pak, 0 ); + otherclient->client->SendPacket( &pak ); + RESETPACKET( pak, 0x0730 ); + ADDWORD( pak, 5 ); + ADDWORD( pak, 0xa24d ); + ADDWORD( pak, 0x40b3 ); + otherclient->client->SendPacket( &pak ); + Log(MSG_GMACTION, " %s : /skillp %i %s", thisclient->CharInfo->charname, skillp, name); + return true; + } + else if (strcmp(command, "killinrange")==0) + { + if(Config.Command_KillInRange > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned range=atoi(tmp); + Log( MSG_GMACTION, " %s : /killinrange %i" , thisclient->CharInfo->charname, range); + return pakGMKillInRange( thisclient, range ); + } + else if (strcmp(command, "gotomap")==0) // *** TELEPORT WITH MAP ID ***** + { // credits to Blackie + if(Config.Command_GoToMap > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; unsigned map=atoi(tmp); + Log( MSG_GMACTION, " %s : /gotomap %i" , thisclient->CharInfo->charname, map); + return pakGMGotomap(thisclient, map); + } + else if(strcmp(command, "heal")==0) + { + int hpamount = 0; + int mpamount = 0; + if(Config.Command_Heal > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " ")) == NULL) + { + hpamount = thisclient->Stats->MaxHP; + } + else + { + hpamount = atoi(tmp); + } + if ((tmp = strtok(NULL, " ")) == NULL) + { + mpamount = thisclient->Stats->MaxMP; + } + else + { + mpamount = atoi(tmp); + } + Log( MSG_GMACTION, " %s : /heal", thisclient->CharInfo->charname ); + return pakGMHeal( thisclient, hpamount, mpamount ); + } + else if(strcmp(command, "serverinfo")==0) + { + if(Config.Command_ServerInfo > thisclient->Session->accesslevel) + return true; + Log( MSG_GMACTION, " %s : /serverinfo" , thisclient->CharInfo->charname); + return pakGMServerInfo( thisclient ); + } + else if(strcmp(command, "targetinfo")==0) + { + if(Config.Command_TargetInfo > thisclient->Session->accesslevel) + return true; + return GMShowTargetInfo( thisclient ); + } + else if (strcmp(command, "hide")==0) + { + if(Config.Command_Hide > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + int mode= atoi( tmp ); + return pakGMHide( thisclient, mode ); + } + else if (strcmp(command, "class")==0) + { + if(Config.Command_Class > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) + return true; + char* classid = (char*)tmp; + Log( MSG_GMACTION, " %s : /class %s" , thisclient->CharInfo->charname, classid); + return pakGMClass( thisclient, classid ); + } + else if(strcmp(command, "convert")==0) + { + if(Config.Command_Convert > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if(thisclient->Battle->target==0) return true; + if ((tmp = strtok(NULL, " " ))==NULL) + return true; + UINT newmon = atoi(tmp); + if(newmon==0) return true; + CMonster* thismon = GetMonsterByID( thisclient->Battle->target, thisclient->Position->Map ); + if(thismon == NULL) + { + CNPC* thisnpc = GetNPCByID( thisclient->Battle->target, thisclient->Position->Map ); + if(thisnpc==NULL) return true; + MapList.Index[thisclient->Position->Map]->ConverToMonster( thisnpc, newmon ); + } + else + { + MapList.Index[thisclient->Position->Map]->ConverToMonster( thismon, newmon ); + } + return true; + } + + else if(strcmp(command, "who2")==0) + { + if(Config.Command_Who2 > thisclient->Session->accesslevel) + return true; + SendPM(thisclient, "The following players are currently connected;"); + int count=1; + int hiddenam=0; + char line0[200]; + while(count <= (ClientList.size()-1)) + { + CPlayer* whoclient = (CPlayer*)ClientList.at(count)->player; + if(whoclient != NULL) + { + if(whoclient->Session->accesslevel > 100) + { + //sprintf(line0, "%s - GM[%i]", whoclient->CharInfo->charname, whoclient->Session->accesslevel); + sprintf(line0, "%s - (GM %i, lvl %u), map %u[%.2f,%.2f]", whoclient->CharInfo->charname, whoclient->Session->accesslevel, + whoclient->Stats->Level, + whoclient->Position->Map, + whoclient->Position->current.x, + whoclient->Position->current.y); + + } + else + { + //sprintf(line0, "%s", whoclient->CharInfo->charname); + + sprintf(line0, "%s (job %u, lvl %u), map %u[%.2f,%.2f]", whoclient->CharInfo->charname, + whoclient->CharInfo->Job, + whoclient->Stats->Level, + whoclient->Position->Map, + whoclient->Position->current.x, + whoclient->Position->current.y); + + } + if(!whoclient->isInvisibleMode || thisclient->Session->accesslevel >= whoclient->Session->accesslevel) + { + SendPM(thisclient, line0 ); + } + else + { + hiddenam++; + } + count++; + } + } + sprintf(line0, "There are currently %i players connected!", ((ClientList.size()-1)-hiddenam)); + Log( MSG_GMACTION, " %s : /who2" , thisclient->CharInfo->charname); + SendPM(thisclient, line0 ); + return true; + } + else if(strcmp(command, "rate")==0) //Highly expandable function to change most config values related to rates + { + if(Config.Command_Rate > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + char* type = tmp; + if(strcmp(type,"death")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.DeathDelay = atoi(tmp); + SendPM(thisclient, "Monster Death Delay has been reset to %i",Config.DeathDelay ); + } + else + if(strcmp(type,"drop")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.DROP_RATE = atoi(tmp); + SendPM(thisclient, "Drop rate has been reset to %i",Config.DROP_RATE ); + } + else + if(strcmp(type,"exp")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.EXP_RATE = atoi(tmp); + SendPM(thisclient, "EXP rate has been reset to %i",Config.EXP_RATE ); + } + else + if(strcmp(type,"blue")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.BlueChance = atoi(tmp); + SendPM(thisclient, "Blue item conversion rate has been reset to %i",Config.BlueChance ); + } + if(strcmp(type,"attkmult")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.AttkMult = atoi(tmp); + SendPM(thisclient, "Attack delay multiplier has been reset to %i",Config.AttkMult ); + } + if(strcmp(type,"skillmult")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.SkillMult = atoi(tmp); + SendPM(thisclient, "Skill delay multiplier has been reset to %i",Config.SkillMult ); + } + if(strcmp(type,"dropdelay")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.DropDelay = atoi(tmp); + SendPM(thisclient, "Drop delay multiplier has been reset to %i",Config.DropDelay ); + } + if(strcmp(type,"floordrop")==0) + { + if ((tmp = strtok(NULL, " "))==NULL) return true; + Config.BASE_DROP_CHANCE = atoi(tmp); + SendPM(thisclient, "Minimum drop rate has been reset to %i",Config.BASE_DROP_CHANCE ); + } + return true; + } + else if(strcmp(command, "moveto")==0) + { + if(Config.Command_Moveto > thisclient->Session->accesslevel) + return true; + fPoint position; + if ((tmp = strtok(NULL, " "))==NULL) return true; + position.x = atof(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; + position.y = atof(tmp); + if ((tmp = strtok(NULL, " "))==NULL) return true; + position.z = atof(tmp); + pakGMMoveTo( thisclient, position ); + } + else if(strcmp(command, "settime")==0) + { + if(Config.Command_Settime > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned int time = atoi(tmp); + if(MapList.Index[thisclient->Position->Map]!=NULL) + { + switch(time) + { + case MORNING: + MapList.Index[thisclient->Position->Map]->MapTime = MapList.Index[thisclient->Position->Map]->morningtime; + SendSysMsg( thisclient, "Time changed!, rejoin to see change [MORNING]" ); + break; + case DAY: + MapList.Index[thisclient->Position->Map]->MapTime = MapList.Index[thisclient->Position->Map]->daytime; + SendSysMsg( thisclient, "Time changed!, rejoin to see change [DAY]" ); + break; + case EVENING: + MapList.Index[thisclient->Position->Map]->MapTime = MapList.Index[thisclient->Position->Map]->eveningtime; + SendSysMsg( thisclient, "Time changed!, rejoin to see change [EVENING]" ); + break; + case NIGHT: + MapList.Index[thisclient->Position->Map]->MapTime = MapList.Index[thisclient->Position->Map]->nighttime; + SendSysMsg( thisclient, "Time changed!, rejoin to see change [NIGHT]" ); + break; + default: + SendSysMsg( thisclient, "Time: 0=MORNING | 1=DAY | 2=EVENING | 3=NIGHT" ); + } + } + } + else if(strcmp(command, "transx")==0) + { + if(Config.Command_Transx > thisclient->Session->accesslevel) + return true; + thisclient->CharInfo->Sex = thisclient->CharInfo->Sex==0?1:0; + BEGINPACKET( pak, 0x720 ); + ADDWORD( pak, 2 ); + ADDWORD( pak, thisclient->CharInfo->Sex ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + RESETPACKET( pak, 0x0730 ); + ADDWORD( pak, 5 ); + ADDWORD( pak, 0xa24d ); + ADDWORD( pak, 0x40b3 ); + thisclient->client->SendPacket( &pak ); + } + else if (strcmp(command, "partylvl")==0) + { + if(Config.Command_Partylvl > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; int level= atoi( tmp ); + return pakGMPartylvl( thisclient, level ); + } + else if(strcmp(command, "pdmg")==0) + { + if(Config.Command_Pdmg > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned rate=atoi(tmp); + Log( MSG_GMACTION, " Rate for Player Dmg is now set at %i by %s" , rate, thisclient->CharInfo->charname); + return pakGMChangePlayerDmg(thisclient, rate); + } + else if(strcmp(command, "mdmg")==0) + { + if(Config.Command_Mdmg > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned rate=atoi(tmp); + Log( MSG_GMACTION, " Rate for Monster Dmg is now set at %i by %s" , rate, thisclient->CharInfo->charname); + return pakGMChangeMonsterDmg(thisclient, rate); + } + else if(strcmp(command, "rules")==0) // Rules Command by Matt + { + if(Config.Command_Rules > thisclient->Session->accesslevel) + return true; + SendPM(thisclient, "Please follow the following rules to ensure you have a fun time on this server!"); + SendPM(thisclient, "Rule 1: No hacking"); + SendPM(thisclient, "Rule 2: Do not abuse any bugs in the game"); + SendPM(thisclient, "Rule 3: Be respectful to all players and GMs"); + SendPM(thisclient, "Rule 4: Do not beg for items"); + SendPM(thisclient, "Rule 5: Do not harass any players or GM's"); + SendPM(thisclient, "Rule 6: Do not abuse any bugs in the game"); + } + else if(strcmp(command, "cfmode")==0) + { + if(Config.Command_Cfmode > thisclient->Session->accesslevel || thisclient->CharInfo->IsDev == 0) + return true; + if((tmp = strtok(NULL, " "))==NULL) return true; unsigned mode=atoi(tmp); + Log( MSG_GMACTION, " Clan field mode changed to %i by %s" , mode, thisclient->CharInfo->charname); + return pakGMChangeCfmode(thisclient, mode); + } + else if(strcmp(command, "skill")==0) + { + if (Config.Command_KillInRange > thisclient->Session->accesslevel) + return true; + if ((tmp = strtok(NULL, " "))==NULL) return true; + unsigned skillid = atoi(tmp); + Log(MSG_GMACTION, "%s cast skill %i", thisclient->CharInfo->charname, skillid); + SendPM(thisclient, "Casting skill %i", skillid); + CCharacter* Target = thisclient->GetCharTarget( ); + CSkills* thisskill = GetSkillByID(skillid); + if(thisskill == NULL)return true; + thisclient->UseSkill(thisskill, Target); + } + else if(strcmp(command, "sendhp")==0) + { + SendPM(thisclient, "Testing HP send"); + // SET PLAYER HEALTH + thisclient->Stats->HP = thisclient->Stats->MaxHP; + BEGINPACKET( pak, 0x79f ); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, thisclient->Stats->HP ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + } + else if (strcmp(command, "dialog")==0 && thisclient->Session->accesslevel >= 300 || thisclient->CharInfo->IsDev == 0) + { + //can be a very dangerous tool to use + UINT npc; + UINT dialog; + UINT eventid; + if ((tmp = strtok(NULL, " "))==NULL)return true; + npc = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL)return true; + dialog = atoi(tmp); + if ((tmp = strtok(NULL, " "))==NULL)return true; + eventid = atoi(tmp); + if(thisclient->ChangeDialog(npc, dialog, eventid)) + { + char buffer2[200]; + sprintf ( buffer2, "dialog for npc %i set to %i event set to %i", npc, dialog, eventid ); + SendPM(thisclient, buffer2); + } + else + { + char buffer2[200]; + sprintf ( buffer2, "dialog and event for npc %i not changed", npc ); + SendPM(thisclient, buffer2); + } + //thisclient->refresh = true; //doesn't exist in osprose. trying a different approach first + //thisclient->VisiblityList( ); + } + else + { + Log( MSG_WARNING, "Invalid GM Command '%s' by '%s'", command, thisclient->CharInfo->charname); + } + return true; +} + +// GM: Announcment +bool CWorldServer::pakGMAnn( CPlayer* thisclient, CPacket *P ) +{ + BEGINPACKET( pak, 0x702 ); + ADDSTRING( pak, thisclient->CharInfo->charname ); + ADDSTRING( pak, "> " ); + ADDSTRING( pak, (&P->Buffer[5])); + ADDBYTE( pak, 0x00); + SendToAll( &pak ); + + return true; +} + +// GM: Spawn x mobs +bool CWorldServer::pakGMMon( CPlayer* thisclient, int montype, int moncount, int monAI ) +{ + CMap* map = MapList.Index[thisclient->Position->Map]; //find current map + if(monAI == 331) //it's a TD mob + { + SendPM(thisclient,"%i monsters of type %i summoned in Map: %i ",moncount,montype,map->id); + for (int i=0; iTDMobList[map->TDNextMon] == 0) + { + map->TDMobList[map->TDNextMon] = montype; + map->TDNextMon ++; + if(map->TDNextMon > 100)map->TDNextMon = 0; + } + else + { + SendPM(thisclient,"Sorry! Cannot add monster %i. the TD Spawn list is full",i); + } + } + } + else // normal spawn + { + for (int i=0; iPosition->current, 10 ); + map->AddMonster( montype, position, 0, 0, 1, 0, 0, monAI ); + } + SendPM(thisclient,"%i monsters of type %i summoned in Map: %i using AI %i",moncount,montype,map->id,monAI); + } + + SendPM(thisclient,"Spawning successful"); + return true; +} + +// GM: Teleport somewhere +bool CWorldServer::pakGMTele( CPlayer* thisclient, int map, float x, float y ) +{ + fPoint coord; + coord.x = x; + coord.y = y; + CMap* mapid = GServer->MapList.Index[map]; + if(mapid == NULL) + { + SendPM(thisclient,"%i is not a valid map id",map); + return true; + } + if(thisclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + MapList.Index[map]->TeleportPlayer( thisclient, coord, false ); + return true; +} + +// GM: apply Status effects for a skill id defined in 'effect' +bool CWorldServer::pakGMEffect(CPlayer* thisclient, int effect) +{ + if(thisclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + // build buffs first + BYTE buff1 = 0; + BYTE buff2 = 0; + BYTE buff3 = 0; + BYTE buff4 = 0; + //Build Debuffs and Buffs + //Up + if(effect == 18)//A_ATTACK: + buff2 += ATTACK_UP; + if(effect == 20)//A_DEFENSE: + buff2 += DEFENSE_UP; + if(effect == 22)//A_MDEFENSE_UP + buff2 += MDEFENSE_UP; + if(effect == 24)//A_ACCUR: + buff3 += HITRATE_UP; + if(effect == 14)//A_DASH: + buff1 += DASH_UP; + if(effect == 16)//A_HASTE: + buff2 += HASTE_UP; + if(effect == 12)//A_HP: + buff1 += HP_UP; + if(effect == 13)//A_MP: + buff1 += MP_UP; + if(effect == 26)//A_CRITICAL: + buff3 += CRITICAL_UP; + if(effect == 28)//A_DODGE: + buff1 += DODGE_UP; + if(effect == 36)//A_Extra_Damage: + buff4 += DAMAGE_UP; + //Down + if(effect == 19) // A_ATTACK: + buff2 += ATTACK_DOWN; + if(effect == 21)//A_DEFENSE: + buff2 += DEFENSE_DOWN; + if(effect == 23) + buff2 += MDEFENSE_DOWN; + if(effect == 25)//A_ACCUR: + buff3 += HITRATE_DOWN; + if(effect == 15)//A_DASH: + buff1 += DASH_DOWN; + if(effect == 17)//A_HASTE: + buff2 += HASTE_DOWN; + if(effect == 27)//A_CRITICAL: + buff3 += CRITICAL_DOWN; + if(effect == 32)//A_STUN + buff4+= STUN; + if(effect >= 7 && effect <= 11)//A_POISON + buff1+= POISONED; + if(effect == 30)//A_MUTE + buff3+= MUTED; + int thisbuff = (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000); + + BEGINPACKET( pak,0x7b7 ); + ADDWORD ( pak, thisclient->clientid ); + ADDDWORD ( pak, thisbuff ); + GServer->SendToVisible( &pak, thisclient ); + SendPM(thisclient,"Buff effect applied %i",thisbuff); +} + +// [by Paul_T] [Thanks to AridTag for the packet :D] +bool CWorldServer::pakGMMute( CPlayer* thisclient, char* name, int time) +{ + CPlayer* otherclient = GetClientByCharName ( name ); + if(otherclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + BEGINPACKET( pak, 0x70d ); + ADDBYTE ( pak, 0 ); + ADDWORD ( pak, time ); + ADDSTRING ( pak, thisclient->CharInfo->charname ); + ADDBYTE ( pak, 0 ); + otherclient->client->SendPacket( &pak ); + return true; +} + +// GM: Item - Modified by Hiei (added refine/socket/stats) +bool CWorldServer::pakGMItem( CPlayer* thisclient, UINT itemid, UINT itemtype, UINT itemamount, UINT itemrefine, UINT itemls, UINT itemstats, UINT itemsocket ) +{ + CItem item; + item.count = itemamount; + item.durability = 40; + item.itemnum = itemid; + item.itemtype = itemtype; + item.lifespan = itemls; + item.refine = itemrefine; + item.stats = itemstats; + item.socketed = itemsocket; + item.appraised = 1; + item.gem = 0; + unsigned newslot = thisclient->GetNewItemSlot( item ); + if (newslot != 0xffff) + { + thisclient->items[newslot] = item; + thisclient->UpdateInventory( newslot ); + char buffer[200]; + sprintf( buffer, "Item added! (ID: %i) (Type: %i) (Refine: %i) (Socket: %i)", item.itemnum, item.itemtype, item.refine, item.socketed ); + BEGINPACKET ( pak, 0x702 ); + ADDSTRING( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + + }else{ + BEGINPACKET( pak, 0x7a7 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 5 ); + thisclient->client->SendPacket( &pak ); + + RESETPACKET ( pak, 0x702 ); + ADDSTRING( pak, "No free slot !" ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + } + return true; +} + +// GM: Kick +bool CWorldServer::pakGMKick( CPlayer* thisclient, char* name ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if(otherclient == NULL) + return true; + BEGINPACKET( pak, 0x702 ); + ADDSTRING( pak, "You were disconnected from the server !" ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); + + RESETPACKET( pak, 0x707 ); + ADDWORD( pak, 0 ); + otherclient->client->SendPacket( &pak ); + + otherclient->client->isActive = false; + + return true; +} + +// GM: Ban +bool CWorldServer::pakGMBan( CPlayer* thisclient, char* name ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if(otherclient == NULL) + return true; + otherclient->Session->accesslevel = -1; + DB->QExecute( "UPDATE accounts SET accesslevel='0' WHERE id=%i", otherclient->Session->userid); + BEGINPACKET( pak, 0x702 ); + ADDSTRING( pak, "You were banned from the server !" ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); + + RESETPACKET( pak, 0x707 ); + ADDWORD( pak, 0 ); + otherclient->client->SendPacket( &pak ); + + otherclient->client->isActive = false; + + return true; +} + +// GM: Add/Remove/Drop/Set zuly +bool CWorldServer::pakGMZuly( CPlayer* thisclient, int mode, int amount, char* charname ) +{ + CPlayer* otherclient = GetClientByCharName(charname); + if(otherclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + if (mode == 1) + { + //ADD + otherclient->CharInfo->Zulies += amount; + BEGINPACKET( pak, 0x71d ); + ADDQWORD( pak, otherclient->CharInfo->Zulies ); + otherclient->client->SendPacket( &pak ); + } + if (mode == 2) + { + //REMOVE + otherclient->CharInfo->Zulies -= amount; + BEGINPACKET( pak, 0x71d ); + ADDQWORD( pak, otherclient->CharInfo->Zulies ); + otherclient->client->SendPacket( &pak ); + } + if (mode == 3) + { + //DROP + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GetNewClientID(); + thisdrop->type = 1; // ZULY + thisdrop->pos.x = otherclient->Position->current.x; + thisdrop->pos.y = otherclient->Position->current.y; + thisdrop->posMap = otherclient->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = amount; + thisdrop->owner = 0; + CMap* map = MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + } + if (mode == 4) + { + //SET + otherclient->CharInfo->Zulies = amount; + BEGINPACKET( pak, 0x71d ); + ADDQWORD( pak, otherclient->CharInfo->Zulies ); + otherclient->client->SendPacket( &pak ); + } + return true; +} + +// Change player Level +bool CWorldServer::pakGMLevel( CPlayer* thisclient, int level, char* name ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if(otherclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + if (((int)otherclient->Stats->Level + level) > 0) + otherclient->Stats->Level += level; + else + otherclient->Stats->Level = 1; + if (otherclient->Stats->Level > 250) + otherclient->Stats->Level = 250; + otherclient->CharInfo->Exp = 0; + if (level < 0) { + otherclient->CharInfo->StatPoints = 0; + otherclient->CharInfo->SkillPoints = 0; + if(otherclient->Stats->Level > 1) + { + for(int s = 2; s <= otherclient->Stats->Level; s++) + { + otherclient->CharInfo->StatPoints += 10; + otherclient->CharInfo->StatPoints += s / 2; + if (s >= 10) + { + switch (s) { + case 10: + case 14: + otherclient->CharInfo->SkillPoints += 2; + break; + case 18: + otherclient->CharInfo->SkillPoints += 3; + break; + case 22: + otherclient->CharInfo->SkillPoints += 4; + break; + default: + break; + } + // 5 skill points every 4 levels from 22 + if (s > 22 && s < 99 && ((s - 22) % 4) == 0) + otherclient->CharInfo->SkillPoints += 5; + // 5 skill points every 2 levels from 98 + if (s > 98 && (s % 2) == 0) + otherclient->CharInfo->SkillPoints += 5; + } + } + } + pakGMStat(otherclient, "str", 15); + pakGMStat(otherclient, "dex", 15); + pakGMStat(otherclient, "con", 15); + pakGMStat(otherclient, "int", 15); + pakGMStat(otherclient, "cha", 10); + pakGMStat(otherclient, "sen", 10); + } else { + for(int s = (otherclient->Stats->Level - level + 1); s <= otherclient->Stats->Level; s++) + { + otherclient->CharInfo->StatPoints += 10; + otherclient->CharInfo->StatPoints += s / 2; + if (s >= 10) + { + switch (s) + { + case 10: + case 14: + otherclient->CharInfo->SkillPoints += 2; + break; + case 18: + otherclient->CharInfo->SkillPoints += 3; + break; + case 22: + otherclient->CharInfo->SkillPoints += 4; + break; + default: + break; + } + // 5 skill points every 4 levels from 22 + if (s > 22 && s < 99 && ((s - 22) % 4) == 0) + otherclient->CharInfo->SkillPoints += 5; + // 5 skill points every 2 levels from 98 + if (s > 98 && (s % 2) == 0) + otherclient->CharInfo->SkillPoints += 5; + } + } + } + BEGINPACKET( pak, 0x79e ); + ADDWORD( pak, otherclient->clientid ); + ADDWORD( pak, otherclient->Stats->Level ); + ADDDWORD( pak, otherclient->CharInfo->Exp ); + ADDWORD( pak, otherclient->CharInfo->StatPoints ); + ADDWORD( pak, otherclient->CharInfo->SkillPoints ); + otherclient->client->SendPacket( &pak ); + + RESETPACKET( pak, 0x79e ); + ADDWORD( pak, otherclient->clientid ); + SendToVisible( &pak, otherclient, false ); + + otherclient->SetStats( ); + otherclient->Stats->HP = otherclient->Stats->MaxHP; + otherclient->Stats->MP = otherclient->Stats->MaxMP; + return true; +} + + +// Reload Mob Spawn +bool CWorldServer::ReloadMobSpawn( CPlayer* thisclient, int id ) +{ + CSpawnArea* thisspawn = new (nothrow) CSpawnArea; + if(thisspawn == NULL) + { + Log(MSG_WARNING, "Error allocing memory" ); + return true; + } + thisspawn->thisnpc = GetNPCDataByID( thisclient->GMRespawnPoints.mobID ); + if(thisspawn->thisnpc == NULL) + { + SendSysMsg( thisclient, "Invalid Respawn" ); + return true; + } + thisspawn->id = id; + thisspawn->map = thisclient->GMRespawnPoints.map; + thisspawn->montype = thisclient->GMRespawnPoints.mobID; + thisspawn->min = thisclient->GMRespawnPoints.min; + thisspawn->max = thisclient->GMRespawnPoints.max; + thisspawn->respawntime = thisclient->GMRespawnPoints.respawntime; + thisspawn->pcount = thisclient->GMRespawnPoints.n; + thisspawn->points = thisclient->GMRespawnPoints.points; + CMap* map = MapList.Index[thisspawn->map]; + for(int j=0; jmax; j++) + { + fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount ); + map->AddMonster( thisspawn->montype, position, 0, thisspawn->id, 1 ); + } + thisspawn->lastRespawnTime=clock(); + return true; +} + +// Teleport To player to other place +bool CWorldServer::pakGMTeleOtherPlayer( CPlayer *thisclient, char* name, int map, float x, float y ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if (otherclient != NULL) + { + fPoint coord; + coord.x = x; + coord.y = y; + MapList.Index[map]->TeleportPlayer( otherclient, coord, false ); + } + else + { + SendSysMsg( thisclient, "User does not exist or is not online!" ); + } + return true; +} + +// Teleport Me to player +bool CWorldServer::pakGMTeleToPlayer( CPlayer* thisclient, char* name ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if (otherclient != NULL) + { + MapList.Index[otherclient->Position->Map]->TeleportPlayer( thisclient, otherclient->Position->current, false ); + } + else + { + SendSysMsg( thisclient, "User does not exist or is not online!" ); + } + return true; +} + +// Teleport Player Here +bool CWorldServer::pakGMTelePlayerHere( CPlayer* thisclient, char* name ) +{ + CPlayer* otherclient = GetClientByCharName( name ); + if (otherclient != NULL) + { + CMap* map = MapList.Index[thisclient->Position->Map]; + map->TeleportPlayer( otherclient, thisclient->Position->current, false ); + } + else + { + SendSysMsg( thisclient, "User does not exist or is not online!"); + } + return true; +} + +// Show Player info +bool CWorldServer::pakGMInfo(CPlayer* thisclient, char* name) +{ + CPlayer* otherclient = GetClientByCharName(name); + if(otherclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + char buffer[200]; + const char* jobname; + + if(otherclient != 0) { + if((otherclient->CharInfo->Job - 0) == 0) { jobname = "Visitor"; } + else if((otherclient->CharInfo->Job - 0) == 111) { jobname = "Soldier"; } + else if((otherclient->CharInfo->Job - 0) == 121) { jobname = "Knight"; } + else if((otherclient->CharInfo->Job - 0) == 122) { jobname = "Champion"; } + else if((otherclient->CharInfo->Job - 0) == 211) { jobname = "Muse"; } + else if((otherclient->CharInfo->Job - 0) == 221) { jobname = "Mage"; } + else if((otherclient->CharInfo->Job - 0) == 222) { jobname = "Cleric"; } + else if((otherclient->CharInfo->Job - 0) == 311) { jobname = "Hawker"; } + else if((otherclient->CharInfo->Job - 0) == 321) { jobname = "Raider"; } + else if((otherclient->CharInfo->Job - 0) == 322) { jobname = "Scout"; } + else if((otherclient->CharInfo->Job - 0) == 411) { jobname = "Dealer"; } + else if((otherclient->CharInfo->Job - 0) == 421) { jobname = "Bourgeois"; } + else if((otherclient->CharInfo->Job - 0) == 422) { jobname = "Artisan"; } + else { jobname = "Unknown"; } + + BEGINPACKET(pak, 0x702); + + sprintf(buffer, "Info about <%s>" , otherclient->CharInfo->charname); + ADDSTRING(pak, buffer); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + + RESETPACKET(pak, 0x702); + sprintf(buffer, "Account: %s | ID: %i" , otherclient->Session->username, otherclient->Session->userid); + ADDSTRING(pak, buffer); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + + RESETPACKET(pak, 0x702); + sprintf(buffer, "Level: %i | Job: %s" , (otherclient->Stats->Level-0) , jobname); + ADDSTRING(pak, buffer); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + + RESETPACKET(pak, 0x702); + sprintf(buffer,"Map: %i , X: %i , Y: %i", (otherclient->Position->Map-0), (int)otherclient->Position->current.x, (int)otherclient->Position->current.y); + ADDSTRING(pak, buffer); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + } + else { + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User does not exist or is not online!"); + ADDBYTE(pak, 0) + thisclient->client->SendPacket(&pak); + } + return true; +} + +// Add Fairy +bool CWorldServer::pakGMFairyto(CPlayer* thisclient, char* name, int mode) +{ + if (GServer->Config.FairyMode== 0){ + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "Fairy feature is de-activated."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; + } + CPlayer* otherclient = GetClientByCharName (name); + if(otherclient == NULL){ + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User does not exist or is not online."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; + } + if(mode == 0 && otherclient->Fairy == false){ + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User already not fairied."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; + } + if(mode == 1 && otherclient->Fairy == true) + { + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User already fairied."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; + } + if(!otherclient->Fairy && mode == 1) + { + int FairyIndex=100; + for (int i=0; iConfig.FairyMax; i++) + { + if (GServer->FairyList.at(i)->assigned == false) + { + FairyIndex=i; + i=GServer->Config.FairyMax; + } + } + if (FairyIndex == 100) + { + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "No free Fairy."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; + } + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User fairied."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + int ListIndex; + for (int i=0; iClientList.at(i)->player == otherclient) + { + ListIndex = i; + i = GServer->ClientList.size(); + } + } + otherclient->Fairy = true; + otherclient->FairyListIndex = FairyIndex; + GServer->FairyList.at(FairyIndex)->ListIndex = ListIndex; + GServer->FairyList.at(FairyIndex)->assigned = true; + GServer->FairyList.at(FairyIndex)->LastTime = clock(); + GServer->FairyList.at(FairyIndex)->WaitTime = GServer->Config.FairyWait; + GServer->DoFairyStuff( otherclient, mode ); + otherclient->SetStats(); + Log( MSG_INFO, "HP: %i MP: %i ATK: %i DEF: %i CRI: %i MSPD: %i", otherclient->Stats->MaxHP, otherclient->Stats->MaxMP, otherclient->Stats->Attack_Power, otherclient->Stats->Defense, otherclient->Stats->Critical, otherclient->Stats->Move_Speed); + } + if(otherclient->Fairy && mode == 0){ + GServer->DoFairyFree(otherclient->FairyListIndex); + GServer->FairyList.at(otherclient->FairyListIndex)->WaitTime = GServer->Config.FairyWait; + otherclient->Fairy = false; + otherclient->FairyListIndex = 0; + GServer->DoFairyStuff( otherclient, mode ); + BEGINPACKET(pak, 0x702); + ADDSTRING(pak, "User unfairied."); + ADDBYTE(pak, 0); + otherclient->client->SendPacket(&pak); + otherclient->SetStats(); + Log( MSG_INFO, "HP: %i MP: %i ATK: %i DEF: %i CRI: %i MSPD: %i", otherclient->Stats->MaxHP, otherclient->Stats->MaxMP, otherclient->Stats->Attack_Power, otherclient->Stats->Defense, otherclient->Stats->Critical, otherclient->Stats->Move_Speed); + } + otherclient->SetStats(); + return true; +} + +// Activate de-activate Fairy mode in game +bool CWorldServer::pakGMManageFairy(CPlayer* thisclient, int mode) +{ + BEGINPACKET (pak, 0x702); + if(mode == 0) + { + if (GServer->Config.FairyMode != mode) + { + GServer->Config.FairyMode = 0; + ADDSTRING(pak, "You have de-activated the Fairy mode ingame."); + } + else + { + ADDSTRING(pak, "The Fairy mode is already de-activated."); + } + } + else + { + if (GServer->Config.FairyMode != mode) + { + GServer->Config.FairyMode = 1; + for (int i=0; iConfig.FairyMax; i++) + { + GServer->DoFairyFree(i); + GServer->FairyList.at(i)->WaitTime = GServer->Config.FairyWait; + } + ADDSTRING(pak, "You have activated the Fairy mode ingame."); + } + else + { + ADDSTRING(pak, "The Fairy mode is already activated."); + } + } + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + + return true; +} + +// Change FairyWait +bool CWorldServer::pakGMChangeFairyWait(CPlayer* thisclient, int newvalue) +{ + GServer->Config.FairyWait = newvalue; + for (int i=0; iFairyList.size(); i++) + { + GServer->FairyList.at(i)->WaitTime = newvalue; + } + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the time between each Fairies"); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +// Change FairyStay +bool CWorldServer::pakGMChangeFairyStay(CPlayer* thisclient, int newvalue) +{ + GServer->Config.FairyStay = newvalue; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the time of Fairies for Buffing "); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +// Change Fairy Test mode. +// 0 -> normal random x * Fairywait +// 1 -> Test mode activated: 1 * Fairywait only +bool CWorldServer::pakGMChangeFairyTestMode(CPlayer* thisclient, int mode) +{ + GServer->Config.FairyTestMode = mode; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the Fairy test mode."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +// Give Zuly +bool CWorldServer::pakGMZulygive(CPlayer* thisclient, char* name, int zuly) +{ + CPlayer* otherclient = GetClientByCharName (name); + if(otherclient == NULL) + { + SendPM(thisclient,"Target Player does not exist or is offline"); + return true; + } + otherclient->CharInfo->Zulies += zuly; + BEGINPACKET(pak, 0x7a7); + ADDWORD(pak, otherclient->clientid); + ADDWORD(pak, 0); + ADDBYTE(pak, 0); + ADDWORD(pak, 0xccdf );//0xccccccdf); + ADDDWORD(pak, zuly); + otherclient->client->SendPacket(&pak); + + return true; +} + +// Spawn a NPC +bool CWorldServer::pakGMNpc(CPlayer* thisclient, int npcid,int dialogid) +{ + CNPC* thisnpc = new CNPC; + assert(thisnpc); + thisnpc->clientid = GetNewClientID(); + thisnpc->dir = 0; + thisnpc->npctype = npcid; + thisnpc->pos = thisclient->Position->current; + thisnpc->posMap = thisclient->Position->Map; + thisnpc->thisnpc = GetNPCDataByID( npcid ); + if( thisnpc->thisnpc == NULL ) + { + SendPM(thisclient,"Cannot create NPC"); + delete thisnpc; + return true; + } + thisnpc->thisnpc->dialogid = dialogid; + CMap* map = MapList.Index[thisclient->Position->Map]; + map->AddNPC( thisnpc ); + SendPM(thisclient,"NPC type %i added to map %i",npcid,thisnpc->posMap); + return true; +} + +// Give Item to Player +bool CWorldServer::pakGMItemtoplayer(CPlayer* thisclient, char* name , UINT itemid, UINT itemtype, UINT itemamount, UINT itemrefine, UINT itemls, UINT itemstats, UINT itemsocket) +{ + CItem item; + item.count = itemamount; + item.durability = 40; + item.itemnum = itemid; + item.itemtype = itemtype; + item.lifespan = 100; //itemls Set lifespan to 100 + item.refine = itemrefine; + item.stats = itemstats; + item.socketed = itemsocket; + item.appraised = 1; + if(itemstats > 300) + { + item.gem = itemstats; + } + else + { + item.gem = 0; + } + + CPlayer* otherclient = GetClientByCharName ( name ); + + if(otherclient != NULL) + { + unsigned newslot = otherclient->GetNewItemSlot( item ); + if(newslot != 0xffff) + { + otherclient->items[newslot] = item; + otherclient->UpdateInventory( newslot ); + + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have recieved an item from a GM !"); + ADDBYTE(pak, 0); + otherclient->client->SendPacket(&pak); + + RESETPACKET (pak, 0x702); + ADDSTRING(pak, "Item has been given!"); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + } + else + { + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "No free slot !"); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + } + } + + return true; +} + +// Do Emotion +bool CWorldServer::pakGMDoEmote( CPlayer* thisclient, int emotionid ) +{ + ClearBattle( thisclient->Battle ); + BEGINPACKET( pak, 0x781 ); + ADDDWORD( pak, emotionid ); + ADDWORD( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient ); + return true; +} + +// Change GM Stats (Coded by Minoc) +bool CWorldServer::pakGMStat( CPlayer* thisclient, const char* statname, int statvalue ) +{ + int statid; + if (strcmp( statname, "Str" )==0 || strcmp( statname, "str" )==0) + { + thisclient->Attr->Str = statvalue; + statid = 0; + } + else if (strcmp( statname, "Dex" )==0 || strcmp( statname, "dex" )==0) + { + thisclient->Attr->Dex = statvalue; + statid = 1; + } + else if (strcmp( statname, "Int" )==0 || strcmp( statname, "int" )==0) + { + thisclient->Attr->Int = statvalue; + statid = 2; + } + else if (strcmp( statname, "Con" )==0 || strcmp( statname, "con" )==0) + { + thisclient->Attr->Con = statvalue; + statid = 3; + } + else if (strcmp( statname, "Cha" )==0 || strcmp( statname, "cha" )==0) + { + thisclient->Attr->Cha = statvalue; + statid = 4; + } + else if (strcmp( statname, "Sen" )==0 || strcmp( statname, "sen" )==0) + { + thisclient->Attr->Sen = statvalue; + statid = 5; + } + else + { + return true; + } + BEGINPACKET( pak, 0x7a9 ); + ADDBYTE( pak, statid ); + ADDWORD( pak, (unsigned short)statvalue ); + thisclient->client->SendPacket( &pak ); + thisclient->SetStats( ); + return true; +} + +// GM: Teleport using map id credits to Blackie +bool CWorldServer::pakGMGotomap( CPlayer* thisclient, int map ) +{ + CMap* thismap = GServer->MapList.Index[map]; + if(thismap == NULL) + { + SendSysMsg( thisclient, "Invalid map id" ); + return true; + } + CRespawnPoint* thisrespawn = MapList.Index[map]->GetFirstRespawn( ); + if(thisrespawn==NULL) + { + SendSysMsg( thisclient, "This map have no respawn" ); + return true; + } + MapList.Index[map]->TeleportPlayer( thisclient, thisrespawn->dest, false ); + return true; +} + +// Heal a player ( by rl2171 ) +bool CWorldServer::pakGMHeal( CPlayer* thisclient, int hpamount, int mpamount ) +{ + //this one works with the modified trose. pak 0x7ec does not work with base 112 client + thisclient->Stats->HP = hpamount; + thisclient->Stats->MP = mpamount; + BEGINPACKET( pak, 0x7ec ); + ADDWORD( pak, hpamount ); + ADDWORD( pak, mpamount ); + thisclient->client->SendPacket( &pak ); + + //experimental heal command. Might not work but if it does then we are sorted. + //BEGINPACKET( pak, 0x7d5 ); + //ADDDWORD ( pak, thisclient->CharInfo->charid ); + //ADDWORD ( pak, thisclient->clientid ); + //ADDWORD ( pak, thisclient->Stats->MaxHP ); + //ADDWORD ( pak, thisclient->Stats->HP ); + //ADDDWORD ( pak, 0x01000000 ); + //ADDDWORD ( pak, 0x0000000f ); + //ADDWORD ( pak, 0x1388 ); + // didn't work either + + //let's try this + //BEGINPACKET( pak, 0x753 ); + //ADDWORD ( pak, thisclient->clientid ); // USER ID + //ADDWORD ( pak, thisclient->Stats->HP ); // CURRENT HP + //ADDWORD ( pak, thisclient->Stats->MP ); // CURRENT MP + //well it kind of works. You get healed OK but with a pillar of light. + //works Ok here but we need a heal without effects for other places + //also resets the zone time lol. Apparently this is not a good plan + + //thisclient->client->SendPacket( &pak ); + Log(MSG_INFO,"GM Heal used for HP %i MP %i",hpamount,mpamount); + SendPM(thisclient, "Set HP to %i and MP to %i",hpamount,mpamount); + return true; +} + +// GM: Server Information ( by rl2171 ) modified by me +bool CWorldServer::pakGMServerInfo( CPlayer* thisclient ) +{ + char buffer[200]; + // Players Online + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[SYS]ServerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, "SERVER INFORMATION" ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Online Players: %i", (int)ClientList.size()-1 );// -1 (we don't count charserver) + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[SYS]ServerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + // Exp / Zulies / Drop rates + sprintf( buffer, "Exp %i | Zulies %i | Drops %i", Config.EXP_RATE, Config.ZULY_RATE, Config.DROP_RATE ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[SYS]ServerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + // Send map time + if(MapList.Index[thisclient->Position->Map]!=NULL) + { + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[SYS]ServerInfo" ); + ADDBYTE( pak, 0 ); + switch(MapList.Index[thisclient->Position->Map]->CurrentTime) + { + case MORNING: + sprintf( buffer, "The Time is: Morning[%i]", MapList.Index[thisclient->Position->Map]->MapTime%MapList.Index[thisclient->Position->Map]->dayperiod ); + ADDSTRING( pak, buffer ); + break; + case DAY: + sprintf( buffer, "The Time is: Day[%i]", MapList.Index[thisclient->Position->Map]->MapTime%MapList.Index[thisclient->Position->Map]->dayperiod ); + ADDSTRING( pak, buffer ); + break; + case EVENING: + sprintf( buffer, "The Time is: Evening[%i]", MapList.Index[thisclient->Position->Map]->MapTime%MapList.Index[thisclient->Position->Map]->dayperiod ); + ADDSTRING( pak, buffer ); + break; + case NIGHT: + sprintf( buffer, "The Time is: Night[%i]", MapList.Index[thisclient->Position->Map]->MapTime%MapList.Index[thisclient->Position->Map]->dayperiod ); + ADDSTRING( pak, buffer ); + break; + default: + sprintf( buffer, "Invalid Time is the End of world [%i]", MapList.Index[thisclient->Position->Map]->MapTime ); + ADDSTRING( pak, buffer ); + } + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + } + return true; +} + +// Show Target Info +bool CWorldServer::GMShowTargetInfo( CPlayer* thisclient ) +{ + if(thisclient->Battle->target==0) return true; + char buffer[200]; + CMonster* monster = GetMonsterByID( thisclient->Battle->target, thisclient->Position->Map ); + if(monster == NULL) return true; + float dist = distance( thisclient->Position->current, monster->Position->current ); + sprintf( buffer, "Target Position: %.4f, %.4f", monster->Position->current.x, monster->Position->current.y ); + BEGINPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Distance: %.0f", dist ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target Defense: %i", monster->Stats->Defense ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target Level: %i", monster->thisnpc->level ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target Attack Power: %i", monster->Stats->Attack_Power ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target Attack Speed: %.0f", monster->Stats->Attack_Speed ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target Move Speed: %i", monster->Stats->Move_Speed ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + sprintf( buffer, "Target HP/MAXHP: %i/%i", monster->Stats->HP, monster->Stats->MaxHP ); + RESETPACKET( pak, 0x784 ); + ADDSTRING ( pak, "[SYS]TargetInfo" ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, buffer ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// GM: Make yourself invisible from tomiz +bool CWorldServer::pakGMHide( CPlayer* thisclient, int mode ) +{ + BEGINPACKET( pak, 0x702 ); + if ( mode == 1 ) + { + thisclient -> isInvisibleMode = true; + ADDSTRING( pak, "You are now invisible !" ); + Log( MSG_GMACTION, " %s : /hide invisible" , thisclient->CharInfo->charname); + } + else + { + thisclient -> isInvisibleMode = false; + ADDSTRING( pak, "You are now visible !" ); + Log( MSG_GMACTION, " %s : /hide visible" , thisclient->CharInfo->charname); + } + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket ( &pak ); + return true; +} +// GM : Change the party lvl +bool CWorldServer::pakGMPartylvl( CPlayer* partyclient, int level ) +{ + if (partyclient->Party->party == NULL) return true; + if (level < 0 || level >50) return true; + partyclient->Party->party->PartyLevel = level; + if( partyclient->Party->party->PartyLevel == 50) + partyclient->Party->party->Exp = 0; + //Send Party Level and Party Exp + BEGINPACKET( pak, 0x7d4 ); // + ADDBYTE ( pak, partyclient->Party->party->PartyLevel); + ADDDWORD ( pak, partyclient->Party->party->Exp ); + partyclient->Party->party->SendToMembers( &pak ); + return true; +} + +// GM: Kill all mobs in a range of x-Fields +bool CWorldServer::pakGMKillInRange( CPlayer* thisclient, int range ) +{ + for(UINT j = 0; j < MapList.Index[thisclient->Position->Map]->MonsterList.size(); j++) + { + CMonster* thismon = MapList.Index[thisclient->Position->Map]->MonsterList.at(j); + if(thismon == NULL) + { + //SendPM(thisclient,"Target Monster does not exist"); + continue; + } + if( IsMonInCircle( thisclient, thismon, (float)range )) + { + //Kill the mob + thismon->Stats->HP = -1; + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, thismon->clientid ); + ADDWORD ( pak, thismon->clientid ); + ADDWORD ( pak, 0x8005); + //ADDDWORD ( pak, thismon->thisnpc->hp*thismon->thisnpc->level ); + //ADDDWORD ( pak, 16 ); + SendToVisible( &pak, thismon ); + CMap* map = MapList.Index[thisclient->Position->Map]; + map->DeleteMonster( thismon ); + } + } + return true; +} + +// GM: Change Class (from Crash) +bool CWorldServer::pakGMClass( CPlayer* thisclient, char* classid ) +{ + int classid_new; + + if ( strcmp ( classid , "Visitor" ) == 0 || strcmp ( classid , "visitor" ) == 0) + { + classid_new = 0; + } + else if ( strcmp ( classid , "Soldier" ) == 0 || strcmp ( classid , "soldier" ) == 0) + { + classid_new = 111; + } + else if ( strcmp ( classid , "Knight" ) == 0 || strcmp ( classid , "knight" ) == 0) + { + classid_new = 121; + } + else if ( strcmp ( classid , "Champion" ) == 0 || strcmp ( classid , "champion" ) == 0) + { + classid_new = 122; + } + else if ( strcmp ( classid , "Muse" ) == 0 || strcmp ( classid , "muse" ) == 0) + { + classid_new = 211; + } + else if ( strcmp ( classid , "Mage" ) == 0 || strcmp ( classid , "mage" ) == 0) + { + classid_new = 221; + } + else if ( strcmp ( classid , "Cleric" ) == 0 || strcmp ( classid , "cleric" ) == 0) + { + classid_new = 222; + } + else if ( strcmp ( classid , "Hawker" ) == 0 || strcmp ( classid , "hawker" ) == 0) + { + classid_new = 311; + } + else if ( strcmp ( classid , "Raider" ) == 0 || strcmp ( classid , "raider" ) == 0) + { + classid_new = 321; + } + else if ( strcmp ( classid , "Scout" ) == 0 || strcmp ( classid , "scout" ) == 0) + { + classid_new = 322; + } + else if ( strcmp ( classid , "Dealer" ) == 0 || strcmp ( classid , "dealer" ) == 0) + { + classid_new = 411; + } + else if ( strcmp ( classid , "Bourgeois" ) == 0 || strcmp ( classid , "bourgeois" ) == 0) + { + classid_new = 421; + } + else if ( strcmp ( classid , "Artisan" ) == 0 || strcmp ( classid , "artisan" ) == 0) + { + classid_new = 422; + } + else + { + return true; + } + // Set the quest vars for the new class + dword jVarSw = classid_new % 10; + thisclient->quest.JobVar[0] = 0; + thisclient->quest.JobVar[1] = 0; + if (jVarSw == 1) + { + thisclient->quest.JobVar[0] = 1; + } + else if (jVarSw == 2) + { + thisclient->quest.JobVar[0] = 1; + thisclient->quest.JobVar[1] = 2; + } + thisclient->CharInfo->Job = classid_new; + BEGINPACKET(pak, 0x0721); + ADDWORD(pak,4); + ADDWORD(pak, thisclient->CharInfo->Job); + ADDWORD(pak,0); + thisclient->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730);//05 00 2f f5 64 29 + ADDWORD(pak, 5); + ADDWORD(pak, 0xf52f);//ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x2964);//ADDWORD(pak, 0x40b3); + thisclient->client->SendPacket(&pak); + SendPM(thisclient, "Class changed!" ); + + return true; +} + +bool CWorldServer::pakGMTeleAllHere( CPlayer* thisclient ) +{ + int count=1; + while(count <= (ClientList.size()-1)) + { + CPlayer* otherclient = (CPlayer*)ClientList.at(count)->player; + if ((otherclient != NULL) && (otherclient != thisclient)) + { + CMap* map = MapList.Index[thisclient->Position->Map]; + map->TeleportPlayer( otherclient, thisclient->Position->current, false ); + } + count++; + } + return true; +} + +bool CWorldServer::pakGMMoveTo( CPlayer* thisclient, fPoint position ) +{ + if( thisclient->Shop->open || (!thisclient->Ride->Drive && thisclient->Ride->Ride) || !thisclient->Status->CanMove ) + return true; + if( thisclient->Status->Stance==1 ) + thisclient->Status->Stance=3; + thisclient->Position->destiny = position; + ClearBattle( thisclient->Battle ); + BEGINPACKET( pak, 0x79a ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisclient->Battle->target ); + ADDWORD ( pak, thisclient->Stats->Move_Speed ); + ADDFLOAT ( pak, thisclient->Position->destiny.x*100 ); + ADDFLOAT ( pak, thisclient->Position->destiny.y*100 ); + ADDFLOAT ( pak, thisclient->Position->destiny.z*100 ); + SendToVisible( &pak, thisclient ); + return true; +} +//change the player dmg rate +bool CWorldServer::pakGMChangePlayerDmg(CPlayer* thisclient, int rate) +{ + GServer->Config.PlayerDmg = rate; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the player dmg rate."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} +//change the monster dmg rate +bool CWorldServer::pakGMChangeMonsterDmg(CPlayer* thisclient, int rate) +{ + GServer->Config.MonsterDmg = rate; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the Monster dmg rate."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +//change the mSpeedModif || For test only +bool CWorldServer::pakGMChangeMSpeedModif(CPlayer* thisclient, int modif) +{ + GServer->MOVE_SPEED_MOD = modif; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "mSpeedModif Changed."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +//change the Cfmode +bool CWorldServer::pakGMChangeCfmode(CPlayer* thisclient, int mode) +{ + if(mode<0) mode = 0; + if(mode>1) mode = 1; + GServer->Config.Cfmode = mode; + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "You have changed the Clan Field Mode."); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + return true; +} + +//LMA: Giving union points to a player. +bool CWorldServer::pakGMUnionPoints(CPlayer* thisclient, char* name, int nb_points) +{ + CPlayer* otherclient = GetClientByCharName (name); + if(otherclient == NULL) + { + SendPM(otherclient,"Target Player does not exist or is offline"); + return true; + } + if(otherclient->Union_s->unionvar[0] == 0) + { + // Otherclient not in a union + Log(MSG_INFO,"Could not give union Points as this player is not in a Union"); + return true; + } + + otherclient->Union_s->unionvar[otherclient->Union_s->unionvar[0]] += nb_points; + int new_amount = otherclient->Union_s->unionvar[otherclient->Union_s->unionvar[0]]; + + //Need to run a quest trigger to make this happen client side too. Have to write the QSD first though. + + BEGINPACKET( pak, 0x720 ); // i think this should be 720 and not 721 as Lmame had it. 721 will change your union ID + ADDWORD( pak, otherclient->Union_s->unionvar[0] ); + ADDWORD( pak, new_amount ); + ADDWORD( pak, 0x0000 ); + otherclient->client->SendPacket( &pak ); + RESETPACKET( pak, 0x730 ); + ADDWORD ( pak, 0x0005 ); + ADDDWORD ( pak, 0x40b3a24d ); + otherclient->client->SendPacket( &pak ); + + SendPM(otherclient, "You have been given %i Faction Points by %s",nb_points,thisclient->CharInfo->charname); + Log( MSG_GMACTION, "%i Faction points given to %s by %s" , nb_points,name,thisclient->CharInfo->charname); + + + return true; +} diff --git a/Src/World Server/itemdata.cpp b/Src/World Server/itemdata.cpp new file mode 100644 index 0000000..52d460d --- /dev/null +++ b/Src/World Server/itemdata.cpp @@ -0,0 +1,45 @@ +// Added By Raven0123 + +#include "datatypes.h" + +unsigned CItem::GetVisInfo( ) +{ + if ( count == 0 ) Clear( ); + return ( ( refine >> 4 ) << 20 ) | ( socketed << 19 ) | ( stats << 10 ) | itemnum; +} + +unsigned short CItem::GetPakHeader( ) +{ + if ( count == 0 ) Clear( ); + return ( ( itemnum << 5 ) | ( itemtype ) ); +} + +unsigned CItem::GetPakData( ) +{ + unsigned retval = 0; + if ( count == 0 ) Clear( ); + if ( itemtype >= 10 && itemtype <= 13 || itemtype == 0x1f ) { + retval = count; + } else { + retval |= (refine>>4) << 28; + retval |= (appraised&1) << 27; + retval |= (socketed&1) << 26; + retval |= (lifespan*10) << 16; + retval |= durability << 9; + retval |= stats; + } + return retval; +} + +void CItem::Clear( ) +{ + itemnum = 0; + itemtype = 0; + count = 0; + refine = 0; + appraised = 0; + socketed = 0; + lifespan = 0; + durability = 0; + stats = 0; +} diff --git a/Src/World Server/ltbhandler.cpp b/Src/World Server/ltbhandler.cpp new file mode 100644 index 0000000..81627f4 --- /dev/null +++ b/Src/World Server/ltbhandler.cpp @@ -0,0 +1,168 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007,2008 OSRose Team http://www.dev-osrose.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +//lmame: LTB file reader. + +#include "ltbhandler.h" + + +void LTBStoreData(char* filename, LTBData* data) +{ + long int first_data = 0; + long int cur_offset =0; + DWORD nb_box = 0; + WORD nb_dial_box = 0; + int nb_dialogs = 0; + char templma[2]; + //char tempfield[1024]; + + + FILE* fh = fopen( filename, "rb" ); + if ( fh == NULL ) + { + Log(MSG_WARNING,"Could not load LTB %s",filename); + return; + } + + //How many dialogs? + fread( &nb_box, 4, 1, fh); + fread(&nb_dial_box,2,1,fh); + nb_dialogs = nb_box*nb_dial_box; + + data->nb_sentence = nb_box; + + //reading location of first data. + fseek(fh,8,SEEK_SET); + fread(&first_data,4,1,fh); + cur_offset=8; + fseek(fh,cur_offset,SEEK_SET); + + + //making the offset list. + long int cpt = 0; + map list_dialog; + + while(cur_offset < first_data) + { + Dialog temp_dial; + + //dialog. + //offset is 4 bytes, lg is 2 bytes. + temp_dial.dialognb = cpt; + fread(&temp_dial.offsetd,4,1,fh); + fread(&temp_dial.lengthd,2,1,fh); + list_dialog[cpt] = temp_dial; //I know, it's faster with .insert... + cur_offset += 6; + cpt++; + + if(temp_dial.offsetd == 0 && temp_dial.lengthd == 0) + { + continue; + } + + } + + //Getting dialogs... + int lang=0; + int no_block=0; + NPCLTB templtb; + string korean_save = ""; + + for (unsigned int k=0;k < list_dialog.size();k++) + { + lang = k%nb_box; + no_block = int(k/nb_box); + + if(lang == LTBINDEX) + { + templtb.name = "NoName"; + templtb.sentence = "NoSentence"; + korean_save = ""; + } + + if(list_dialog[k].offsetd == 0) + { + if (lang == LTBENGLISH) + { + //did we have Korean sentence? + if (korean_save != "") + { + templtb.sentence = korean_save; + } + + data->record.push_back(templtb); + } + + continue; + } + + //reading data + //lg*2 because of unicode :) + string temp; + fseek(fh,list_dialog[k].offsetd,SEEK_SET); + + if(lang!=LTBENGLISH&&lang!=LTBINDEX&&lang!=LTBKOREAN) + { + //LMA: jumping... + //fread(&tempfield,list_dialog[k].lengthd*2,1,fh); + fseek(fh,list_dialog[k].lengthd*2,SEEK_CUR); + continue; + } + + //unicode is NOT our friend :( + for(int j=0;j<(list_dialog[k].lengthd*2);j++) + { + fread(&templma,1,1,fh); + if (templma[0]=='\0') + continue; + temp+=templma[0]; + } + + //We save Korean as well... + switch (lang) + { + case LTBKOREAN: + { + korean_save = temp; + } + break; + case LTBENGLISH: + { + templtb.sentence = temp; + data->record.push_back(templtb); + } + break; + default: + { + templtb.name = temp; + } + break; + } + + } + + //closing the file... + fclose(fh); + + + return; +} + diff --git a/Src/World Server/ltbhandler.h b/Src/World Server/ltbhandler.h new file mode 100644 index 0000000..b1c7596 --- /dev/null +++ b/Src/World Server/ltbhandler.h @@ -0,0 +1,28 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +//lmame +#include "worldserver.h" +#include +using namespace std; + + +void LTBStoreData(char* filename, LTBData* data); diff --git a/Src/World Server/main.cpp b/Src/World Server/main.cpp new file mode 100644 index 0000000..5562319 --- /dev/null +++ b/Src/World Server/main.cpp @@ -0,0 +1,81 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#define REVISION 32 +#include "worldserver.h" +//#pragma comment(lib,"ws2_32.lib") + +CWorldServer* GServer; + +unsigned char LOG_THISSERVER; + +// Main server function +int main(int argc, char *argv[]) +{ + srand( (unsigned)time(NULL) ); + srand( rand()*time(NULL) ); + LOG_THISSERVER = LOG_WORLD_SERVER; + InitWinSocket( ); + string fileconf = "worldserver.conf"; + if(argc>1) + { + fileconf = argv[1]; + } + CWorldServer *server = new (nothrow) CWorldServer( fileconf ); + if(server==NULL) + return -1; + pthread_attr_init(&server->at); + pthread_attr_setdetachstate(&server->at, PTHREAD_CREATE_JOINABLE); + // Connect mysql + MYSQL mysql; + server->DB = new CDatabase( server->Config.SQLServer.pcServer, + server->Config.SQLServer.pcUserName, + server->Config.SQLServer.pcPassword, + server->Config.SQLServer.pcDatabase, + server->Config.SQLServer.pcPort, &mysql ); + server->port = server->Config.CharPort; + if(server->Config.usethreads) + Log( MSG_INFO, "Using Threads" ); + // Start server + server->port = server->Config.WorldPort; + + if(server->DB->Connect( )==0) Log( MSG_INFO, "Connected to MySQL server" ); + else + { + delete server; + #ifdef _WIN32 + system("pause"); + #endif + return -1; + } + + server->LoadEncryption(); + + server->StartServer(); + // Close server + pthread_attr_destroy(&server->at); + server->DB->Disconnect( ); + CloseWinSocket( ); + Log(MSG_INFO, "Cleaning memory, please wait..." ); + delete server; + return EXIT_SUCCESS; +} + +// ----------------------------------------------------------------------------------------- diff --git a/Src/World Server/mapfunctions.cpp b/Src/World Server/mapfunctions.cpp new file mode 100644 index 0000000..376188a --- /dev/null +++ b/Src/World Server/mapfunctions.cpp @@ -0,0 +1,550 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +#include "worldmap.h" + +// convert a monster to other +CMonster* CMap::ConverToMonster( CMonster* monster, UINT newmontype, bool heal ) +{ + CNPCData* thisnpc = GServer->GetNPCDataByID( newmontype ); + if(thisnpc == NULL)// invalid type + return NULL; + monster->montype = newmontype; + monster->thisnpc = thisnpc; + monster->monAI = monster->thisnpc->AI; + monster->SetStats( ); + monster->Stats->MaxHP = monster->GetMaxHP( ); + monster->Stats->HP = monster->Stats->MaxHP; + monster->Stats->MaxMP = monster->GetMaxMP( ); + monster->Stats->MP = monster->Stats->MaxMP; + if(heal) + monster->Stats->HP = monster->Stats->MaxHP; + if(monster->owner == 0) + { + monster->MonsterDrop->mobdrop = GServer->GetDropData( monster->thisnpc->dropid ); + monster->MonsterDrop->mapdrop = GServer->GetDropData( id ); + } + BEGINPACKET( pak, 0x774 ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, newmontype ); + GServer->SendToVisible( &pak, monster ); + //monster->OnSpawn( false ); + return monster; +} + +// convert a npc to other [not working] +CMonster* CMap::ConverToMonster( CNPC* npc, UINT newmontype ) +{ + CMonster* monster = AddMonster( newmontype, npc->pos, 0, 0, 1 ); + if(monster == NULL) // invalid montype + return NULL; + GServer->ClearClientID( monster->clientid ); + monster->clientid = npc->clientid; + for(UINT i=0;iclientid ); + ADDWORD ( pak, newmontype ); + GServer->SendToVisible( &pak, monster ); + return monster; +} + +// Teleport player to this map and this coord. +bool CMap::TeleportPlayer( CPlayer* player, fPoint coord, bool TelePassenger ) +{ + GServer->MapList.Index[player->Position->Map]->RemovePlayer( player, false ); + player->Position->Map = id; + player->Position->current = coord; + player->Position->destiny = coord; + Log(MSG_DEBUG,"(TeleportPlayer) Destiny set to target position X: %f Y: %f.",player->Position->current.x,player->Position->current.y); + player->Session->inGame = false; + player->Position->lastMoveTime = clock(); + if(player->Stats->HP < 1)player->Stats->HP = player->Stats->MaxHP * 10 / 100; + if(!allowpat || !TelePassenger) + { + if(!allowpat) + player->Status->Stance = 0x03; + player->Ride->Drive = false; + player->Ride->charid= 0; + player->Ride->Ride = false; + } + AddPlayer( player ); + BEGINPACKET( pak, 0x07a8 ); + ADDWORD ( pak, player->clientid ); + ADDWORD ( pak, player->Position->Map ); + ADDFLOAT ( pak, player->Position->current.x * 100 ); + ADDFLOAT ( pak, player->Position->current.y * 100 ); + ADDWORD ( pak, (player->Status->Stance == 0x04?0x0201:0x0001) ); + player->client->SendPacket( &pak ); + if( player->Ride->Drive && player->Ride->charid != 0 ) + { + CPlayer* otherclient = GServer->GetClientByCID( player->Ride->charid ); + if( otherclient != NULL ) + { + if(TelePassenger) + { + TeleportPlayer( otherclient, coord ); + } + else + { + otherclient->Ride->Drive = false; + otherclient->Ride->charid = 0; + otherclient->Ride->Ride = false; + } + } + } + GServer->pakClearUser( player ); + GServer->ClearClientID( player->clientid ); + player->RestartPlayerVal( ); + return true; +} + +bool CMap::pakTeleport( CPlayer* player, unsigned int Map, fPoint coord, bool TelePassenger ) +{ + GServer->MapList.Index[player->Position->Map]->RemovePlayer( player, false ); + player->Position->Map = Map; + player->Position->current = coord; + player->Position->destiny = coord; + Log(MSG_DEBUG,"(pakTeleport) Destiny set to target position X: %f Y: %f.",player->Position->current.x,player->Position->current.y); + player->Session->inGame = false; + player->Position->lastMoveTime = clock(); + if(!allowpat || !TelePassenger) + { + if(!allowpat) + player->Status->Stance=0x03; + player->Ride->Drive = false; + player->Ride->charid= 0; + player->Ride->Ride = false; + } + AddPlayer( player ); + BEGINPACKET( pak, 0x07a8 ); + ADDWORD ( pak, player->clientid ); + ADDWORD ( pak, player->Position->Map ); + ADDFLOAT ( pak, player->Position->current.x*100 ); + ADDFLOAT ( pak, player->Position->current.y*100 ); + ADDWORD ( pak, (player->Status->Stance == 0x04?0x0201:0x0001) ); + player->client->SendPacket( &pak ); + if( player->Ride->Drive && player->Ride->charid!=0 ) + { + CPlayer* otherclient = GServer->GetClientByCID( player->Ride->charid ); + if( otherclient != NULL ) + { + if(TelePassenger) + { + TeleportPlayer( otherclient, coord ); + } + else + { + otherclient->Ride->Drive = false; + otherclient->Ride->charid= 0; + otherclient->Ride->Ride = false; + } + } + } + GServer->pakClearUser( player ); + GServer->ClearClientID( player->clientid ); + player->RestartPlayerVal( ); + return true; +} + +// return near respawn [NULL if there are no respawn] +CRespawnPoint* CMap::GetNearRespawn( CPlayer* player ) +{ + CRespawnPoint* respawn = NULL; + float distance = 0xffff; + for(UINT i=0;idistance( player->Position->current, RespawnList.at(i)->dest ); + if( resdist < distance ) + { + respawn = RespawnList.at(i); + distance = resdist; + } + } + return respawn; +} + +// Return "start" respawn +CRespawnPoint* CMap::GetStartRespawn( ) +{ + for( UINT i = 0; i < RespawnList.size(); i++ ) + { + if( RespawnList.at(i)->masterdest ) + return RespawnList.at(i); + } + return NULL; +} + +// Return first respawn +CRespawnPoint* CMap::GetFirstRespawn( ) +{ + CRespawnPoint* respawn = NULL; + for(UINT i=0;iclientid == id ) + return PlayerList.at(i); + } + return NULL; +} + +// Search a client by charid +CPlayer* CMap::GetCharIDInMap( UINT id ) +{ + for(UINT i=0;iCharInfo->charid == id ) + return PlayerList.at(i); + } + return NULL; +} + +// Search a monster by clientid +CMonster* CMap::GetMonsterInMap( UINT id ) +{ + for(UINT i=0;iclientid == id ) + return MonsterList.at(i); + } + return NULL; +} + +CDrop* CMap::GetDropInMap( UINT id ) +{ + for(UINT i=0;iclientid == id ) + return DropsList.at(i); + } + return NULL; +} + +CNPC* CMap::GetNPCInMap( UINT id ) +{ + for(UINT i=0;iclientid == id ) + return NPCList.at(i); + } + return NULL; +} + +void CMap::UpdateTime( ) +{ + if(id == 0) return; + UINT etime = (UINT)GServer->round((clock() - LastUpdate)/1000); + if(etime < 10)return; + MapTime += (UINT)floor((double)(etime/10)); + int Hourlength = int(0xffffffff / 24); + int Minutelength = int(Hourlength / 60); + if(MapTime == 0xffffffff) + { + MapTime = 0; + Date ++; + } + ZoneTime = int(MapTime / 17895697); + + if(dayperiod == 0) + dayperiod = 1; + UINT ctime = MapTime%dayperiod; + if(ctime >= morningtime && ctime < daytime) + { + CurrentTime = MORNING; + } + else + if(ctime >= daytime && ctime < eveningtime) + { + CurrentTime = DAY; + } + else + if(ctime >= eveningtime && ctime < nighttime) + { + CurrentTime = EVENING; + } + else + if(ctime >= nighttime) + { + CurrentTime = NIGHT; + } + hour = int(MapTime / Hourlength); + minute = int((MapTime - (hour * Hourlength)) / Minutelength); + LastUpdate = clock(); +} + +// Clean Drops +void CMap::CleanDrops( ) +{ + for(UINT j=0;jdroptime >= 50 ) + DeleteDrop( thisdrop ); + } +} + +// Respawn a Monster +void CMap::RespawnMonster( ) +{ + if(GServer->Config.SpawnType == 1) + { + /*for(UINT k=0;krespawntime<2)thisspawn->respawntime=2; + clock_t rtime = clock() - thisspawn->lastRespawnTime; + if((rtime > thisspawn->respawntime*CLOCKS_PER_SEC && thisspawn->amonmax)|| thisspawn->amonmin ) + { + fPoint position = GServer->RandInPoly( thisspawn->points, thisspawn->pcount ); + AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id ); + thisspawn->lastRespawnTime = clock(); + } + }*/ + //PY new spawn code + for(UINT k=0;klastRespawnTime; + if(thisspawn->limit == 1 && IsNight()) //spawn limit is set to day only + continue; + if(thisspawn->RefVar != 0 && thisspawn->RefVal != 0) //only allow this spawn if the correct ObjVar is set to non zero + { + if(GServer->ObjVar[thisspawn->RefVar][0] == thisspawn->RefVal) + continue; + } + if(thisspawn->limit == 2 && !IsNight()) //spawn limit is set to night only + continue; + switch (thisspawn->type) + { + case 0: + // no spawn. this spawn area is inactive + break; + case 1: //normal spawn + if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max) + { + fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + thisspawn->lastRespawnTime = clock(); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + thisspawn->triggercount = 0; + } + break; + case 2: //normal but fill the spawn + if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max) + { + for(int i=1;imax;i++) + { + fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + } + } + break; + case 3: //cascade spawn + if(thisspawn->triggercount > thisspawn->triggeramount && thisspawn->amon < thisspawn->max) + { + fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + thisspawn->lastRespawnTime = clock(); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + thisspawn->triggercount = 0; + } + break; + case 4: //cascade to max + if(thisspawn->triggercount > thisspawn->triggeramount && thisspawn->amon < thisspawn->max) + { + for(int i=1;imax;i++) + { + fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + } + thisspawn->lastRespawnTime = clock(); + thisspawn->triggercount = 0; + } + break; + case 5: //sub spawns. needs to trigger from a specific montype + if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max) + { + //find if any of the trigger mobs exist + for(UINT j=0;jmontype == thisspawn->triggertype && (thisspawn->spawnkey == 4 || thisspawn->triggercount >= thisspawn->triggeramount)) + if(monster->montype == thisspawn->triggertype) + { + //found the right monster. now spawn the servant mob in a circle around it + fPoint position = GServer->RandInCircle(monster->Position->current,5); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + thisspawn->triggercount = 0; + thisspawn->lastRespawnTime = clock(); + } + } + } + break; + case 6: + if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max) + { + //find if any of the trigger mobs exist + for(UINT j=0;jmontype == thisspawn->triggertype && (thisspawn->spawnkey == 4 || thisspawn->triggercount >= thisspawn->triggeramount)) + if(monster->montype == thisspawn->triggertype) + { + //found the right monster. now spawn the servant mobs around it up to the spawn max + for(int i=thisspawn->amon;imax;i++) + { + fPoint position = GServer->RandInCircle(monster->Position->current,5); + UINT aggro = thisspawn->aggressive; + AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters + } + thisspawn->lastRespawnTime = clock(); + thisspawn->triggercount = 0; + } + } + } + break; + case 7: //TDEF spawn. Spawns thisspawn->max monsters one by one using triggercount to track them + if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->triggercount < thisspawn->max) + { + fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); + AddMonster( thisspawn->montype, position, 0, thisspawn->id, 0, true, 0 ); + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 3); // update the counters + thisspawn->lastRespawnTime = clock(); + } + break; + default: + break; + } + } + } + if(GServer->Config.SpawnType == 0) + { + for (UINT j = 0; j < MobGroupList.size(); j++) + { + CMobGroup* thisgroup = MobGroupList.at(j); + clock_t rtime = clock() - thisgroup->lastRespawnTime; + if (rtime < thisgroup->respawntime*CLOCKS_PER_SEC || thisgroup->active >= thisgroup->limit) + continue; + for (UINT k = thisgroup->active; k < thisgroup->limit; k++) + { + CMob *thismob; + if (thisgroup->tacMobs.size() > 0 && thisgroup->basicKills >= thisgroup->tacticalpoints) + { + thismob = thisgroup->tacMobs.at(thisgroup->curTac); + thisgroup->basicKills = 0; + thisgroup->curTac++; + if (thisgroup->curTac >= thisgroup->tacMobs.size()) thisgroup->curTac = 0; + } else { + thismob = thisgroup->basicMobs.at(thisgroup->curBasic); + thisgroup->curBasic++; + if (thisgroup->curBasic >= thisgroup->basicMobs.size()) thisgroup->curBasic = 0; + } + for (UINT i = 0; i < thismob->amount; i++) + { + fPoint position = GServer->RandInCircle( thisgroup->point, thisgroup->range ); + AddMonster( thismob->mobId, position, 0, thisgroup->id, 1 ); + } + } + } + } +} + +//update the trigger counts in cascade spawns +void CMap::UpdateSpawnCounter( UINT montype, UINT spawnid, UINT key ) +{ + if(key != 0) //kick back if key is undefined + { + for(UINT k=0;ktypeoftrigger) + { + case 0: //no trigger + continue; + break; + case 1: //at any location + //specific monster spawns or dies or is attacked anywhere in the current map + if(thisspawn->triggertype == montype && thisspawn->spawnkey == key) + { + thisspawn->triggercount++; + } + case 2: //at a specific spawn id + //specific monster spawns or dies or is attacked at this specific spawn area + if(thisspawn->triggertype == spawnid && thisspawn->spawnkey == key) + { + thisspawn->triggercount++; + } + break; + case 3: //towerdef + thisspawn->triggercount++; + break; + default: + break; + } + } + } +} + +// Return true if is night +bool CMap::IsNight( ) +{ + if(CurrentTime==NIGHT) + return true; + return false; +} + +CCharacter* CMap::GetCharInMap( unsigned int id ) +{ + CMonster* monster = GetMonsterInMap( id ); + //Log(MSG_DEBUG, "getcharinmap %i", id); + if(monster != NULL) + { + //Log(MSG_DEBUG, "Monster found type = %i", monster->montype); + return (CCharacter*) monster; + } + CPlayer* player = GetPlayerInMap( id ); + if(player != NULL) + return (CCharacter*) player; + return NULL; +} diff --git a/Src/World Server/monsterfunctions.cpp b/Src/World Server/monsterfunctions.cpp new file mode 100644 index 0000000..14befe1 --- /dev/null +++ b/Src/World Server/monsterfunctions.cpp @@ -0,0 +1,151 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldmonster.h" + +// check is a player is in range +bool CMonster::PlayerInRange() +{ + CMap* map = GServer->MapList.Index[Position->Map]; + for(int i=0; iPlayerList.size(); i++) + { + CPlayer* thisclient = map->PlayerList.at(i); + if(GServer->IsVisible(thisclient, this)) return true; + } + return false; +} + +// get the near player +CPlayer* CMonster::GetNearPlayer( UINT mdist ) +{ + CPlayer* thisplayer = NULL; + float distance = 0xffff; + for(UINT i=0;iMapList.Index[Position->Map]->PlayerList.size();i++) + { + CPlayer* thisclient = GServer->MapList.Index[Position->Map]->PlayerList.at(i); + float tempdist = GServer->distance( Position->current, thisclient->Position->current ); + if(tempdistmdist) + return NULL; // near player is too far + return thisplayer; +} + +// return true if is a ghost +bool CMonster::IsGhost( ) +{ + // candle host, elec ghost, yigore ghost + if((montype>710 && montype<751) || (montype>679 && montype<693)) + return true; + return false; +} + +// return true if is a ghost seed +bool CMonster::IsGhostSeed( ) +{ + if((montype>700 && montype<711) && montype!=703) + return true; + return false; +} + +// return true if current monster can move +bool CMonster::CanMove( ) +{ + if((montype<41 || montype>45) && (montype<325 || montype>329) && montype!=659 && (montype<771 || montype>810 ) && montype!=992 && (montype<1474 || montype>1489)) + return true; + return false; +} + +// return true if this monster is ready to move +bool CMonster::ReadyToMove( ) +{ + return true; +} + +// return a player pointers +CPlayer* CMonster::GetOwner( ) +{ + if(!IsSummon( )) return NULL; + CPlayer* ownerclient = GServer->GetClientByID( owner, Position->Map ); + return ownerclient; +} + +bool CMonster::IsMonster( ) +{ + return true; +} + +void CMonster::AddDamage( CCharacter* enemy, long int hitpower) +{ + int MaxDamage = Stats->MaxHP * 2; + //Log(MSG_DEBUG,"Max Damage = %i",MaxDamage); + CPlayer* player = NULL; + if(enemy->IsMonster( )) + { + CMonster* monster = (CMonster*) enemy; + if(!enemy->IsSummon( )) + return; + player = monster->GetOwner( ); + if(player == NULL) + return; + } + else + { + player = (CPlayer*) enemy; + } + if(MonsterDrop->firsthit == 0 && hitpower > 0) + { + MonsterDrop->firsthit = player->CharInfo->charid; + MonsterDrop->firstlevel = player->Stats->Level; + if( player->Party->party != NULL ) + thisparty = player->Party->party; + else + thisparty = NULL; + } + for(UINT i=0;icharid == player->CharInfo->charid) + { + if(hitpower > MaxDamage) + hitpower = MaxDamage; + thisplayer->damage += hitpower; + if(thisplayer->damage > MaxDamage) + thisplayer->damage = MaxDamage; + return; + } + } + MonsterDamage* newplayer = new MonsterDamage; + newplayer->charid = player->CharInfo->charid; + if( hitpower > MaxDamage ) + hitpower = MaxDamage; + newplayer->damage = hitpower; + PlayersDamage.push_back( newplayer ); + //Log(MSG_DEBUG,"Player Damage added= %i",hitpower); +} + +CDrop* CMonster::GetDrop( ) +{ + return GServer->GetNewDrop( this ); +} diff --git a/Src/World Server/party.h b/Src/World Server/party.h new file mode 100644 index 0000000..f0084e3 --- /dev/null +++ b/Src/World Server/party.h @@ -0,0 +1,52 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef __ROSE_PARY__ +#define __ROSE_PARY__ +#include "worldserver.h" +#include "datatypes.h" + +class CParty +{ + public: + CParty( ); + ~CParty( ); + + CPlayer* GetMember( UINT id ); + CPlayer* GetMemberByCharID( UINT charid ); + void SendToMembers( CPacket* pak, CPlayer* exclient=NULL ); + void SendToVisible( CPacket* pak, CPlayer* thisclient ); + bool AddPlayer( CPlayer* player ); + bool RemovePlayer( CPlayer* player ); + bool counted; //flag to help assign party exp + + UINT Pending_Exp; //temporary value to hold exp during calculations + UINT PartyLevel; + UINT Exp; + UINT Option; + UINT LastItem; + UINT LastETC; + UINT LastZulies; + UINT Capacity; + vector Members; + CPartyExp* PartyExp; +}; + +#endif diff --git a/Src/World Server/partyfunctions.cpp b/Src/World Server/partyfunctions.cpp new file mode 100644 index 0000000..d2ff0bc --- /dev/null +++ b/Src/World Server/partyfunctions.cpp @@ -0,0 +1,120 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +#include "party.h" + +CParty::CParty( ) +{ + PartyLevel = 1; + Exp = 0; + Option =0; + LastItem = 0; + LastETC = 0; + LastZulies = 0; + Capacity = 5; + Members.clear(); + PartyExp = new CPartyExp; +} + +CParty::~CParty( ) +{ + delete PartyExp; +} + +CPlayer* CParty::GetMember( UINT id ) +{ + for(UINT i=0;iclientid == id) + return member; + } + return NULL; +} + +CPlayer* CParty::GetMemberByCharID( UINT id ) +{ + for(UINT i=0;iCharInfo->charid == id) + return member; + } + return NULL; +} + +void CParty::SendToMembers( CPacket* pak, CPlayer* explayer ) +{ + for(UINT i=0;iclient->SendPacketCpy( pak ); + } +} + +void CParty::SendToVisible( CPacket* pak, CPlayer* thisclient ) +{ + for(UINT i=0;iIsVisible( thisclient, member )) + member->client->SendPacketCpy( pak ); + } +} + +bool CParty::AddPlayer( CPlayer* player ) +{ + player->Party->party = this; + Members.push_back( player ); + return true; +} + +bool CParty::RemovePlayer( CPlayer* player ) +{ + for(UINT i=0;iParty->IsMaster = true; + player->Party->party = NULL; + return true; + } + } + return false; +} + + + + + diff --git a/Src/World Server/player.cpp b/Src/World Server/player.cpp new file mode 100644 index 0000000..6b6d272 --- /dev/null +++ b/Src/World Server/player.cpp @@ -0,0 +1,519 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "player.h" +#include "worldserver.h" + +CPlayer::CPlayer( CClientSocket* CLIENT ) +{ + client = CLIENT; + // USED ITEM + UsedItem = new USEDITEM; + assert(UsedItem); + UsedItem->lastRegTime = 0; + UsedItem->usevalue = 0; + UsedItem->usetype = 0; + UsedItem->userate = 0; + UsedItem->used = 0; + // CHARINFO + CharInfo = new INFO; + assert(CharInfo); + memset( &CharInfo->charname, '\0', 17 ); + CharInfo->charid = 0; + CharInfo->Sex = 0; + CharInfo->Face = 0; + CharInfo->Hair = 0; + CharInfo->Exp = 0; + CharInfo->Job = 0; + CharInfo->Zulies = 0; + CharInfo->Storage_Zulies = 0; + CharInfo->LastGlobal = 0; + CharInfo->StatPoints = 0; + CharInfo->SkillPoints = 0; + CharInfo->stamina = 0; + + // RIDE + Ride = new RIDE; + assert(Ride); + Ride->Drive = false; + Ride->Ride = false; + Ride->charid = 0; + // TRADE + Trade = new TRADE; + assert(Trade); + for(int i=0;i<10;i++) + Trade->trade_itemid[i] = 0; + for(int i=0;i<11;i++) + Trade->trade_count[i] = 0; + Trade->trade_status = 0; + Trade->trade_target = 0; + //PARTY + Party = new PARTY; + assert(Party); + Party->party = NULL; + Party->IsMaster = true; + // SHOP + Shop = new SHOP; + assert(Shop); + Shop->open = false; + memset( &Shop->name, '\0',64 ); + for(int i=0;i<30;i++) + { + ClearItem(Shop->BuyingList[i].item); + Shop->BuyingList[i].slot = 0; + Shop->BuyingList[i].count = 0; + Shop->BuyingList[i].price = 0; + Shop->SellingList[i].slot = 0; + Shop->SellingList[i].count = 0; + Shop->SellingList[i].price = 0; + } + Shop->Buying = 0; + Shop->Selling = 0; + Shop->ShopType = 0; + // SESSION + Session = new SESSION; + assert(Session); + Session->userid = 0; + memset( &Session->username, '\0', 17 ); + memset( &Session->password, '\0', 33 ); + Session->accesslevel = 0; + Session->isLoggedIn = false; + Session->inGame = false; + // Inventory / storage + for(unsigned int i=0;iclanid = 0; + Clan->clanrank = 0; + Clan->grade = 0; + Clan->CP = 0; + Clan->logo = 0; + Clan->back = 0; + memset( &Clan->clanname, '\0', 17 ); + //union + Union_s = new UNION_s; + // ATTRIBUTES + Attr = new ATTRIBUTES; + assert(Attr); + Attr->Str = 0; + Attr->Dex = 0; + Attr->Int = 0; + Attr->Con = 0; + Attr->Cha = 0; + Attr->Sen = 0; + Attr->Estr = 0; + Attr->Edex = 0; + Attr->Eint = 0; + Attr->Econ = 0; + Attr->Echa = 0; + Attr->Esen = 0; + + CharType = TPLAYER; + questdebug = false; + Session->codedebug = false; + Saved = false; + isInvisibleMode = false; + Fairy = false; + //FairyTime = 0; + nstorageitems = 0; + p_skills = 0; + + for(int i=0;iRide && !Ride->Drive ) //I'm a passenger in somebody else's cart + { + CPlayer* otherclient = GServer->GetClientByCID( Ride->charid, Position->Map ); //find the cart's driver + if( otherclient != NULL ) // found the driver + { + if( otherclient->Status->Stance != 0x04 ) //not driving any more + { + otherclient->Ride->Drive = false; + otherclient->Ride->Ride = false; + otherclient->Ride->charid = 0; + Ride->Drive = false; + Ride->Ride = false; + Ride->charid = 0; + Position->destiny = Position->current; + Log(MSG_DEBUG,"(UpdateValues) not in cart any more. Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + } + else //still driving + { + Position->current = otherclient->Position->current; + Position->destiny = otherclient->Position->destiny; + Log(MSG_DEBUG,"(UpdateValues) Passenger in Cart Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + Position->lastMoveTime = otherclient->Position->lastMoveTime; + return false; // will not update the player position + } + } + else //no driver found so reset values to NOT be a passenger any more + { + Ride->Drive = false; + Ride->Ride = false; + Ride->charid = 0; + Position->destiny = Position->current; + Log(MSG_DEBUG,"(UpdateValues) No other client. Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y); + } + } + return true; +} + +bool CPlayer::RespawnPlayer( CPlayer* thisclient ) +{ + CMap* map = GServer->MapList.Index[thisclient->Position->Map]; + CRespawnPoint* thisrespawn = NULL; + thisclient->Stats->HP = thisclient->Stats->MaxHP * 10 / 100; + byte respawn = thisclient->Session->RespawnChoice; + Log( MSG_INFO, "RespawnZones choice: %i", respawn ); + //1 - Current / 2 - save point/dungeon entrance + if(respawn == 1 || map->dungeon) + { + if (respawn != 1) // This is a dungeon map, spawn at start + thisrespawn = map->GetStartRespawn( ); + else + thisrespawn = map->GetNearRespawn( thisclient ); + if(thisrespawn!=NULL) + { + map->TeleportPlayer( thisclient, thisrespawn->dest, false ); + } + } + else // This is a field, spawn in save town + { + thisrespawn = GServer->GetRespawnByMap( thisclient->Position->saved ); + if(thisrespawn != NULL) + { + fPoint coord = thisrespawn->dest; + GServer->MapList.Index[thisclient->Position->saved]->TeleportPlayer( thisclient, coord, false ); + } + } + + if(thisrespawn == NULL) + { + fPoint coord; + coord.x = 5200; + coord.y = 5200; + GServer->MapList.Index[2]->TeleportPlayer( thisclient, coord, false ); + } + Log( MSG_INFO, "Player teleported successfully" ); + for(unsigned int i=0;i<30;i++) + { // Clean Buffs + thisclient->MagicStatus[i].Duration = 0; + } + Log( MSG_INFO, "Buffs Cleaned" ); + return true; +} + +// Spawn Another User on the Screen +bool CPlayer::SpawnToPlayer( CPlayer* player, CPlayer* otherclient ) +{ +/* if( Party->party!=NULL && Party->party == player->Party->party) + { + BEGINPACKET( pak, 0x7d5 ); + ADDDWORD ( pak, CharInfo->charid ); + ADDWORD ( pak, clientid ); + ADDWORD ( pak, GetMaxHP( ) ); + ADDWORD ( pak, Stats->HP ); + ADDDWORD ( pak, 0x01000000 ); + ADDDWORD ( pak, 0x0000000f ); + ADDWORD ( pak, 0x1388 ); + player->client->SendPacket( &pak ); + } +*/ +/* + if( Ride->Ride ) + { + CPlayer* rideclient = GServer->GetClientByCID( Ride->charid, Position->Map ); + if(rideclient==NULL) + { + Ride->Ride = false; + Ride->Drive= false; + Ride->charid = 0; + return true; + } + if( GServer->IsVisible( player, rideclient ) || player->CharInfo->charid == rideclient->CharInfo->charid ) + { + BEGINPACKET( pak, 0x796 ); + if( Ride->Drive ) + { + ADDWORD ( pak, rideclient->clientid ); + ADDFLOAT ( pak, rideclient->Position->current.x*100 ); + ADDFLOAT ( pak, rideclient->Position->current.y*100 ); + } + else + { + ADDWORD ( pak, clientid ); + ADDFLOAT ( pak, Position->current.x*100 ); + ADDFLOAT ( pak, Position->current.y*100 ); + } + ADDWORD ( pak, 0x0000 ); + player->client->SendPacket( &pak ); + + RESETPACKET( pak, 0x7dd ); + ADDBYTE ( pak, 0x02 ); + if( rideclient->Ride->Drive ) + { + ADDWORD ( pak, rideclient->clientid ); + ADDWORD ( pak, clientid ); + } + else + { + ADDWORD ( pak, clientid ); + ADDWORD ( pak, rideclient->clientid ); + } + player->client->SendPacket( &pak ); + } + } + return true; +*/ + + BEGINPACKET( pak, 0x793 ); + ADDWORD( pak, clientid); // USER ID + ADDFLOAT( pak, Position->current.x*100 ); // POS X + ADDFLOAT( pak, Position->current.y*100 ); // POS Y + ADDFLOAT( pak, Position->destiny.x*100 ); // GOING TO X + ADDFLOAT( pak, Position->destiny.y*100 ); // GOINT TO Y + if(Shop->open) + { + ADDWORD( pak, 0x000b ); + ADDWORD( pak, 0x0000 ); + } + else + if(Stats->HP <= 0) + { + ADDWORD( pak, 0x0003 ); + ADDWORD( pak, 0x0000 ); + } + else + // if(Status->Stance == SITTING) + // { + // ADDWORD( pak, 0x000a ); + // ADDWORD( pak, 0x0000 ); + // } + // else + if (Battle->atktarget!=0) + { + ADDWORD( pak, 0x0002 ); + ADDWORD( pak, Battle->atktarget ); + } + else + if(Position->destiny.x != Position->current.x || Position->destiny.y != Position->current.y) + { + ADDWORD( pak, 0x0001 ); + ADDWORD( pak, 0x0000 ); + } + else + { + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + } + ADDBYTE( pak, Ride->Drive == true?2:1); + ADDWORD( pak, Stats->HP); + ADDBYTE( pak, 0x00); + //ADDWORD( pak, 0x6401 ); //?? + //ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0200 ); + ADDWORD( pak, 0x0000 ); + ADDBYTE( pak, 0x00 ); + ADDDWORD( pak, GServer->BuildBuffs( this ) ); // BUFFS + ADDBYTE( pak, CharInfo->Sex ); // GENDER + ADDWORD( pak, Stats->Mspd_base ); // WALK SPEED MAYBE? + ADDBYTE( pak, 0x00 ); // ?? + ADDBYTE( pak, 0x00 ); // ?? + ADDBYTE( pak, 0x01 ); // ?? + ADDDWORD( pak, CharInfo->Face ); // FACE TYPE + ADDDWORD( pak, CharInfo->Hair ); // HAIR TYPE + ADDDWORD( pak, GServer->BuildItemShow( items[2] )); // CAP + ADDDWORD( pak, GServer->BuildItemShow( items[3] )); // BODY + ADDDWORD( pak, GServer->BuildItemShow( items[5] )); // GLOVES + ADDDWORD( pak, GServer->BuildItemShow( items[6] )); // BOOTS + ADDDWORD( pak, GServer->BuildItemShow( items[1] )); // FACE + ADDDWORD( pak, GServer->BuildItemShow( items[4] )); // BACK + ADDDWORD( pak, GServer->BuildItemShow( items[7] )); // WEAPON + ADDDWORD( pak, GServer->BuildItemShow( items[8] )); // SUBWEAPON + ADDWORD( pak, GServer->BuildItemHead(items[132] )); + ADDWORD( pak, GServer->BuildItemHead(items[133] )); + ADDWORD( pak, GServer->BuildItemHead(items[134] )); + ADDWORD( pak, CharInfo->Job ); + ADDBYTE( pak, Stats->Level); + if(items[135].itemnum>0){ + ADDWORD( pak, (items[135].itemnum +1024));} + else{ + ADDWORD( pak, 0x0000 );} + ADDWORD( pak, 0x0000 ); + if(items[136].itemnum>0){ + ADDWORD( pak, (items[136].itemnum +1024));} + else{ + ADDWORD( pak, 0x0000 );} + ADDWORD( pak, 0x0000 ); + if(items[137].itemnum>0){ + ADDWORD( pak, (items[137].itemnum +1024));} + else{ + ADDWORD( pak, 0x0000 );} + ADDWORD( pak, 0x0000 ); + if(items[138].itemnum>0){ + ADDWORD( pak, (items[138].itemnum +1024));} + else{ + ADDWORD( pak, 0x0000 );} + ADDWORD( pak, 0x0000 ); + + ADDWORD( pak, (Stats->HP<=0)?0x0:0xCDCD ); + if(Shop->open) + { + ADDWORD( pak, 0x0002 ); + } + else + { + ADDWORD( pak, 0x0000 ); + } + ADDWORD( pak, 0x0000 ); + ADDDWORD( pak, 0x00000000 ); + ADDSTRING( pak, CharInfo->charname ); + ADDBYTE( pak, 0x00 ); + if(Status->Dash_up != 0xff) + ADDWORD ( pak, (MagicStatus[Status->Dash_up].Value)); + if(Status->Haste_up!= 0xff) + ADDWORD ( pak, (MagicStatus[Status->Haste_up].Value)); + if(Shop->open) + { + ADDBYTE( pak, Shop->ShopType); + ADDBYTE( pak, 0x00); + ADDSTRING(pak, Shop->name); + ADDBYTE( pak, 0x00); + } + if(Clan->clanid!=0) + { + ADDWORD ( pak, Clan->clanid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, Clan->back ); + ADDWORD ( pak, Clan->logo ); + ADDBYTE ( pak, Clan->grade ); + ADDBYTE ( pak, Clan->clanrank ); + ADDSTRING ( pak, Clan->clanname ); + } + ADDWORD( pak, 0x0000 ); + player->client->SendPacket(&pak); + return true; +} +// ----------------------------------------------------------------------------------------- +// Returns new skill slot //bskill askill pskill +// ----------------------------------------------------------------------------------------- + +int CPlayer::GetNewSkillSlot(char skilltype) +{ + switch (skilltype) + { + case 0: + for(char s=0;s<30;s++) + { + if(bskills[s]==0)return s; + } + case 1: + for(char t=0;t<30;t++) + { + if(askill[t]==0)return t; + } + case 2: + for(char u=0;u<30;u++) + { + if(pskill[u]==0)return u; + } + default: + break; + } + return 0xffff; +} + +void CPlayer::onCheckForMonsterQuests_event (CMonster * Monster) +{ + // We had First hit, and the monster was killed. + for (int q = 0; q < 10; q++) + { + // Give Quest Item + if (this->quest.quests[q].QuestID != 0) + { + //Log(MSG_DEBUG,"Giving quest reward item for quest %i Killed monster type %i",thisclient->quest.quests[q].QuestID,thismon->montype); + + //PY: thismon->thisnpc->die_quest contains the hash needed to complete the trigger + int success = this->ExecuteQuestTrigger (Monster->thisnpc->die_quest); + if (success == QUEST_SUCCESS) + { + CPacket pakout (0x730); + pakout.AddByte ((byte)success); + pakout.AddByte (0); + pakout.AddDWord (Monster->thisnpc->die_quest); + this->client->SendPacket (&pakout); + } + break; + } + } +} + diff --git a/Src/World Server/player.h b/Src/World Server/player.h new file mode 100644 index 0000000..a328103 --- /dev/null +++ b/Src/World Server/player.h @@ -0,0 +1,170 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef _ROSE_PLAYER_ +#define _ROSE_PLAYER_ +#include "character.h" + +/// Forward Declarations +class CMonster; // Forward declare +class CPlayer: public CCharacter +{ + + public: + CPlayer( CClientSocket* ); + ~CPlayer( ); + + // + CClientSocket* client; + bool questdebug;// Send PM's with quest information + bool Saved;// already save this player?(used ondisconnect/returnchar) + bool isInvisibleMode; + + // Fairy + bool Fairy; + UINT FairyListIndex; + + // variables + USEDITEM* UsedItem; + INFO* CharInfo; + RIDE* Ride; + TRADE* Trade; + PARTY* Party; + SHOP* Shop; + SESSION* Session; + CLAN* Clan; + UNION_s* Union_s; + ATTRIBUTES* Attr; + CRespawnPoints GMRespawnPoints; + + // Inventory/storage + CItem storageitems[MAX_STORAGE]; + unsigned int nstorageitems; + CItem items[MAX_INVENTORY]; + + // skills/quickbar + SKILLS cskills[MAX_SKILL]; + UINT bskills[MAX_BASICSKILL]; + UINT quickbar[MAX_QUICKBAR]; + unsigned short askill[30]; + unsigned char askilllvl[30]; + unsigned short pskill[30]; + unsigned char pskilllvl[30]; + int p_skills; + + // ExJam Quest Code - Info + DWORD ActiveQuest; //active quest # + int CheckQuest; // The currently checked quest. + strings TriggerName[10]; + SQuestData quest; // Character's quest information + int NPCvar; // holds reference value for ObjVar global structure + int RefVar; // holds reference value for second part of ObjVar structure + + // Time + clock_t lastRegenTime; + clock_t lastSaveTime; + clock_t lastGG; + + // Visible Lists + vector VisiblePlayers; // Visible players + vector VisibleDrops; // Visible drops + vector VisibleMonsters; // Visible monsters + vector VisibleNPCs; // Visible npcs + + // Battle Events + void onCheckForMonsterQuests_event (CMonster* Monster); // Called if the character had first hit and monster was killed. + + // Functions + bool CheckPlayerLevelUP( ); + bool canUseSkill(CSkills* skill); + void SetStats( ); + bool GetPlayerInfo( ); + bool IsMonster( ); + bool CleanPlayerVector( ); + bool loaddata( ); + void savedata( ); + void quicksave( ); + void saveinventory( ); + void loadinventory( ); + void savestorage( ); + void SaveBackupStorage( int logstate ); + void loadstorage( ); + UINT GetNewStorageItemSlot( CItem thisitem ); + UINT GetNewItemSlot( CItem thisitem ); + bool ClearObject( unsigned int otherclientid ); + void RestartPlayerVal( ); + bool Regeneration( ); + bool PlayerHeal( ); + bool VisiblityList( ); + bool ChangeDialog( unsigned int npc, unsigned int dialog, unsigned int eventid = 0 ); + bool SpawnToPlayer( CPlayer* player, CPlayer* otherclient ); + bool RespawnPlayer(CPlayer* thisclient); + bool UpdateValues( ); + void ReduceABC( ); + CParty* GetParty( ); + CLAN* GetClan( ); + unsigned int AddItem( CItem item ); + unsigned int Getnstorageitems( CPlayer* thisclient ); + void UpdateInventory(unsigned int slot1, unsigned int slot2=0xffff ); + + // ExJam Quest Code + void savequests( CPlayer* thisclient ); + SQuest* GetActiveQuest( ); + int GetQuestVar(short nVarType, short nVarNO); + void SetQuestVar(short nVarType, short nVarNO, short nValue); + int ExecuteQuestTrigger(dword hash); + //PY Quest stuff + void SetQuestFlag(dword flagid, bool value); + void GetQuestFlag(dword falgid); + + + // Player Stats + unsigned int GetAttackPower( ); + unsigned int GetDefense( ); + unsigned int GetDodge( ); + unsigned int GetXPRate( ); + unsigned int GetItemDropRate( ); + unsigned int GetItemDropCountRate( ); + unsigned int GetZulyDropRate( ); + unsigned int GetGreyDrops( ); + unsigned int GetAccury( ); + unsigned int GetMagicDefense( ); + unsigned int GetMagicAttack( ); + unsigned int GetCritical( ); + unsigned int GetAttackSpeed( ); + unsigned int GetMoveSpeed( ); + unsigned int GetMaxHP( ); + unsigned int GetMaxMP( ); + float GetAttackDistance( ); + unsigned int GetHPRegenAmount( ); + unsigned int GetMPRegenAmount( ); + unsigned int GetMaxWeight( ); + unsigned int GetCurrentWeight( ); + unsigned int GetMPReduction( ); + unsigned int GetMaxSummonGauge( ); + void GetExtraStats( ); + unsigned int GetLevelEXP( ); + unsigned int GetInt( ); + // Skill + int GetNewSkillSlot(char skilltype); + void takeSkillCost( CSkills* skill ); +}; + +#endif diff --git a/Src/World Server/playerdata.cpp b/Src/World Server/playerdata.cpp new file mode 100644 index 0000000..a8ee690 --- /dev/null +++ b/Src/World Server/playerdata.cpp @@ -0,0 +1,1379 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "player.h" +#include "worldserver.h" + +unsigned char GetCharVal( char mychar ) +{ + switch( mychar ) + { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + case 'A': return 10; + case 'B': return 11; + case 'C': return 12; + case 'D': return 13; + case 'E': return 14; + case 'F': return 15; + default: + break; + } + return 0; +} + +void EncodeBinary( char* encoded, unsigned char* data, unsigned datalen ) +{ + for( unsigned short i = 0, j = 0; i < datalen; i++ ) + { + encoded[ j++ ] = "0123456789ABCDEF"[ data[ i ] >> 4 ]; + encoded[ j++ ] = "0123456789ABCDEF"[ data[ i ] & 0xf ]; + } + encoded[ datalen * 2 ] = 0; +} + +void DecodeBinary( char* encoded, unsigned char* data ) +{ + unsigned curbit = 0; + for( unsigned i = 0; i < (unsigned)strlen(encoded); i+=2 ) + { + data[curbit] = GetCharVal( encoded[i] ) << 4; + data[curbit] += GetCharVal( encoded[i+1] ); + curbit++; + } +} + +// Load this characters userdata from the database +bool CPlayer::loaddata( ) +{ + //Log(MSG_INFO,"Loading character data"); + MYSQL_ROW row; + MYSQL_RES *result = GServer->DB->QStore("SELECT level,face,hairStyle,sex,classid,zuly,str,dex,_int,con,cha,sen,curHp,curMp,id,statp,skillp,exp,stamina,quickbar,basic_skills,askill,askilllvl,pskill,pskilllvl,respawnid,clanid,clan_rank,townid,isgm,isdev FROM characters WHERE char_name='%s'", CharInfo->charname); + if(result==NULL) return false; + if(mysql_num_rows(result)!=1) + { + Log( MSG_WARNING, "Number of user with charname '%s' is %i", CharInfo->charname,mysql_num_rows(result)); + return false; + } + row = mysql_fetch_row(result); + Stats->Level = atoi(row[0]); + CharInfo->Face = atoi(row[1]); + CharInfo->Hair = atoi(row[2]); + CharInfo->Sex = atoi(row[3]); + CharInfo->Job = atoi(row[4]); + CharInfo->Zulies = atol(row[5]); + Attr->Str = atoi(row[6]); + Attr->Dex = atoi(row[7]); + Attr->Int = atoi(row[8]); + Attr->Con = atoi(row[9]); + Attr->Cha = atoi(row[10]); + Attr->Sen = atoi(row[11]); + Stats->HP = atoi(row[12]); + Stats->MP = atoi(row[13]); + CharInfo->charid = atoi(row[14]); + CharInfo->StatPoints = atoi(row[15]); + CharInfo->SkillPoints = atoi(row[16]); + CharInfo->Exp = atoi(row[17]); + CharInfo->stamina = atoi(row[18]); + Position->respawn = atoi(row[25]); + Clan->clanid = atoi(row[26]); + Clan->clanrank = atoi(row[27]); + Position->saved = atoi(row[28]); + Stats->xprate = GetXPRate(); + CharInfo->IsGM = atoi(row[29]); + CharInfo->IsDev = atoi(row[30]); + Log(MSG_DEBUG,"Selected Character %s Class %i GM status: %i DEV status %i", CharInfo->charname, CharInfo->Job, CharInfo->IsGM, CharInfo->IsDev); + p_skills = 0; + for(char i=0;i<32;i++) + { + char* tmp=strtok((i==0?row[19]:NULL), ","); + if (tmp!=NULL) quickbar[i]=atoi(tmp); + else + quickbar[i]=0; + } + + for(char i=0;i<30;i++) + { + char* tmp=strtok((i==0?row[20]:NULL), ","); + if (tmp!=NULL) bskills[i]=atoi(tmp); + else + bskills[i]=0; + + } + for(char i=0;i<30;i++) + { + char* tmp=strtok((i==0?row[21]:NULL), ","); + if (tmp!=NULL) askill[i]=atoi(tmp); + else + askill[i]=0; + } + for(char i=0;i<30;i++) + { + char* tmp=strtok((i==0?row[22]:NULL), ","); + if (tmp!=NULL) askilllvl[i]=atoi(tmp); + else + askilllvl[i]=0; + } + for(char i=0;i<30;i++) + { + char* tmp=strtok((i==0?row[23]:NULL), ","); + if (tmp!=NULL) pskill[i]=atoi(tmp); + else + pskill[i]=0; + } + for(char i=0;i<30;i++) + { + char* tmp=strtok((i==0?row[24]:NULL), ","); + if (tmp!=NULL) pskilllvl[i]=atoi(tmp); + else + pskilllvl[i]=0; + } + + GServer->DB->QFree( ); + CharInfo->Pending_Exp = 0; + Log(MSG_INFO,"Loading union data"); + result = GServer->DB->QStore("SELECT unionid,union1points,union2points,union3points,union4points,union5points,union6points,union7points,union8points,union9points,union10points FROM characters WHERE char_name='%s'", CharInfo->charname); + if(result == NULL) return false; + if(mysql_num_rows(result)!=1) + { + Log( MSG_WARNING, "Number of user with charname '%s' is %i", CharInfo->charname,mysql_num_rows(result)); + return false; + } + row = mysql_fetch_row(result); + for (char points=0; points<11;points++) + { + if(points == 9) + { + Union_s->unionvar[points] = Session->KTPoints; + } + else + { + Union_s->unionvar[points] = atoi(row[points]); + } + } + GServer->DB->QFree( ); + // code to reset players back to the correct faction after using the KTRose shop + if(Union_s->unionvar[0] == 9) + { + //Log(MSG_DEBUG, "UnionVar 7 used to reset player Union from %i to %i",Union_s->unionvar[0],Union_s->unionvar[7] ); + Union_s->unionvar[0] = Union_s->unionvar[7]; + Union_s->unionvar[7] = 0; + } + // PY loadup code + //Log(MSG_INFO,"Loading inventory data"); + result = GServer->DB->QStore("SELECT itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,trans_action FROM charitems WHERE owner=%i", CharInfo->charid); + if(result == NULL) + { + Log(MSG_WARNING, "Something a bit wrong with this query"); + return false; + } + const char *tmpid; + const char *itmp; + int itemid; + while( row = mysql_fetch_row(result) ) + { + tmpid = strtok( row[0] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].itemnum = atoi(itmp); + } + tmpid = strtok( row[1] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].itemtype = atoi(itmp); + } + tmpid = strtok( row[2] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].refine = atoi(itmp); + } + tmpid = strtok( row[3] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].durability = atoi(itmp); + } + tmpid = strtok( row[4] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].lifespan = atoi(itmp); + } + tmpid = strtok( row[5] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + { + items[i].count = atoi(itmp); + //Log(MSG_INFO,"slot: %i itemid: %i type: %i count: %i",i, items[i].itemnum,items[i].itemtype,items[i].count); //PAT items seem to load ok here + } + } + tmpid = strtok( row[6] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + { + items[i].stats = atoi(itmp); + if(items[i].stats >300) + { + items[i].gem = items[i].stats; + } + else + items[i].gem = 0; + } + } + tmpid = strtok( row[7] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].socketed = (atoi(itmp)==1)?true:false; + //just in case there is a discrepency in the database this will fix sockets. + if(items[i].gem != 0) + items[i].socketed = true; + } + tmpid = strtok( row[8] , "|"); + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + items[i].appraised = (atoi(itmp)==1)?true:false; + } + for(unsigned int i=1;i<=MAX_INVENTORY; i++) + { + // first delete bad items + if(items[i].itemnum > GServer->EquipList[items[i].itemtype].STBMax) + { + Log(MSG_INFO,"deleting item in slot: %i itemid: %i type: %i count: %i",i, items[i].itemnum,items[i].itemtype,items[i].count); + //Item number too high + items[i].itemnum = 0; + items[i].itemtype = 0; + items[i].count = 0; + items[i].durability = 0; + + } + // find item type for crafting + if(items[i].itemtype > 0 && items[i].itemtype < 10) + items[i].type = GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->type; + if(items[i].itemtype == 10) + items[i].type = GServer->UseList.Index[items[i].itemnum]->type; + if(items[i].itemtype == 11) + items[i].type = GServer->JemList.Index[items[i].itemnum]->type; + if(items[i].itemtype == 12) + items[i].type = GServer->NaturalList.Index[items[i].itemnum]->type; + if(items[i].itemtype == 14) + items[i].type = GServer->PatList.Index[items[i].itemnum]->type; + //Log(MSG_INFO,"slot: %i itemid: %i type: %i count: %i",i, items[i].itemnum,items[i].itemtype,items[i].count); + } + + } + //PY end + /* + result = GServer->DB->QStore("SELECT itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem FROM items WHERE owner=%i", CharInfo->charid); + if(result==NULL) return false; + while(row = mysql_fetch_row(result)) + { + if(!GServer->IsValidItem( atoi(row[1]), atoi(row[0]) ) || atoi(row[6])==0) + { + Log(MSG_WARNING, "char %s have a invalid or empty item in inventory: %i-%i [%i], this item will be deleted", CharInfo->charname, atoi(row[1]), atoi(row[0]), atoi(row[6]) ); + continue; + } + UINT itemnum = atoi(row[5]); + items[itemnum].itemnum = atoi(row[0]); + items[itemnum].itemtype = atoi(row[1]); + items[itemnum].refine = atoi(row[2]); + items[itemnum].durability = atoi(row[3]); + items[itemnum].lifespan = atoi(row[4]); + items[itemnum].count = atoi(row[6]); + items[itemnum].stats = atoi(row[7]); + items[itemnum].socketed = (atoi(row[8])==1)?true:false; + items[itemnum].appraised = (atoi(row[9])==1)?true:false; + items[itemnum].gem = atoi(row[10])>3999?0:atoi(row[10]); + //extra code to fix bugged gem items + if(items[itemnum].gem > 0) + { + items[itemnum].stats = items[itemnum].gem; + } + // find item type for crafting + if(items[itemnum].itemtype < 10) + items[itemnum].type = GServer->EquipList[items[itemnum].itemtype].Index[items[itemnum].itemnum]->type; + if(items[itemnum].itemtype == 10) + items[itemnum].type = GServer->UseList.Index[items[itemnum].itemnum]->type; + if(items[itemnum].itemtype == 11) + items[itemnum].type = GServer->JemList.Index[items[itemnum].itemnum]->type; + if(items[itemnum].itemtype == 12) + items[itemnum].type = GServer->NaturalList.Index[items[itemnum].itemnum]->type; + if(items[itemnum].itemtype == 14) + items[itemnum].type = GServer->PatList.Index[items[itemnum].itemnum]->type; + + }*/ + //Log( MSG_DEBUG, "loaded inventory info"); + GServer->DB->QFree( ); + + // PY loadup code for storage + //Log(MSG_INFO,"Loading storage data"); + result = GServer->DB->QStore("SELECT itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,trans_action FROM charstorage WHERE owner=%i", Session->userid); + if(result == NULL) + { + Log(MSG_WARNING, "Something a bit wrong with this query"); + return false; + } + tmpid = ""; + itmp = 0; + while( row = mysql_fetch_row(result) ) + { + tmpid = strtok( row[0] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].itemnum = atoi(itmp); + //if (Session->userid == 8) //test code delete later + //{ + // if(i < 4) + // Log(MSG_DEBUG,"Storage item %i loaded. Value = %i",i,atoi(itmp)); + //} + } + tmpid = strtok( row[1] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].itemtype = atoi(itmp); + } + tmpid = strtok( row[2] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].refine = atoi(itmp); + } + tmpid = strtok( row[3] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].durability = atoi(itmp); + } + tmpid = strtok( row[4] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].lifespan = atoi(itmp); + } + tmpid = strtok( row[5] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].count = atoi(itmp); + } + tmpid = strtok( row[6] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + { + storageitems[i].stats = atoi(itmp); + if(storageitems[i].stats > 300) + { + storageitems[i].gem = items[i].stats; + } + else + storageitems[i].gem = 0; + } + } + tmpid = strtok( row[7] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].socketed = (atoi(itmp)==1)?true:false; + //just in case there is a discrepency in the database this will fix sockets. + if(storageitems[i].gem != 0) + storageitems[i].socketed = true; + } + tmpid = strtok( row[8] , "|"); + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + if((itmp = strtok( NULL , "|"))!= NULL) + storageitems[i].appraised = (atoi(itmp)==1)?true:false; + } + for(unsigned int i=0;i<=MAX_STORAGE; i++) + { + // find item type for crafting + if(storageitems[i].itemtype > 0 && storageitems[i].itemtype < 10) + storageitems[i].type = GServer->EquipList[storageitems[i].itemtype].Index[storageitems[i].itemnum]->type; + if(storageitems[i].itemtype == 10) + storageitems[i].type = GServer->UseList.Index[storageitems[i].itemnum]->type; + if(storageitems[i].itemtype == 11) + storageitems[i].type = GServer->JemList.Index[storageitems[i].itemnum]->type; + if(storageitems[i].itemtype == 12) + storageitems[i].type = GServer->NaturalList.Index[storageitems[i].itemnum]->type; + if(storageitems[i].itemtype == 14) + storageitems[i].type = GServer->PatList.Index[storageitems[i].itemnum]->type; + } + + } + //PY end + + /* + //original storage load code + GServer->DB->QFree( ); + Log(MSG_INFO,"Loading storage data"); + result = GServer->DB->QStore("SELECT itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem FROM storage WHERE owner=%i", Session->userid); + if(result == NULL) return false; + nstorageitems = mysql_num_rows(result); + while(row = mysql_fetch_row(result)) + { + if(!GServer->IsValidItem( atoi(row[1]), atoi(row[0]) ) || atoi(row[6])==0) + { + Log(MSG_WARNING, "char %s have a invalid or empty item in storage: %i%i [%i], this item will be deleted", CharInfo->charname, atoi(row[1]), atoi(row[0]), atoi(row[6]) ); + continue; + } + UINT itemnum = atoi(row[5]); + storageitems[itemnum].itemnum = atoi(row[0]); + storageitems[itemnum].itemtype = atoi(row[1]); + storageitems[itemnum].refine = atoi(row[2]); + storageitems[itemnum].durability = atoi(row[3]); + storageitems[itemnum].lifespan = atoi(row[4]); + storageitems[itemnum].count = atoi(row[6]); + storageitems[itemnum].stats = atoi(row[7]); + storageitems[itemnum].socketed = (atoi(row[8])==1)?true:false; + storageitems[itemnum].appraised = (atoi(row[9])==1)?true:false; + storageitems[itemnum].gem = atoi(row[10]); + //extra code to fix bugged gem items + if(storageitems[itemnum].gem > 0) + { + storageitems[itemnum].stats = storageitems[itemnum].gem; + } + // find item type for crafting + if(storageitems[itemnum].itemtype < 10) + storageitems[itemnum].type = GServer->EquipList[items[itemnum].itemtype].Index[items[itemnum].itemnum]->type; + if(storageitems[itemnum].itemtype == 10) + storageitems[itemnum].type = GServer->UseList.Index[items[itemnum].itemnum]->type; + if(storageitems[itemnum].itemtype == 11) + storageitems[itemnum].type = GServer->JemList.Index[items[itemnum].itemnum]->type; + if(storageitems[itemnum].itemtype == 12) + storageitems[itemnum].type = GServer->NaturalList.Index[items[itemnum].itemnum]->type; + if(storageitems[itemnum].itemtype == 14) + storageitems[itemnum].type = GServer->PatList.Index[items[itemnum].itemnum]->type; + + } + */ + //End of original storage load code. + + + GServer->DB->QFree( ); + //Log(MSG_INFO,"saving backup storage data."); + SaveBackupStorage(1); // makes a copy of the account's storage at the moment of login + //Log( MSG_DEBUG, "Saved backup storage data"); + if(Clan->clanid!=0) + { + result = GServer->DB->QStore("SELECT logo,back,name,cp,grade FROM list_clan where id=%i", Clan->clanid); + if(result==NULL) return false; + if(mysql_num_rows(result)!=1) + { + Log(MSG_WARNING, "There are %i clan(s) with id %i", mysql_num_rows(result), Clan->clanid ); + Clan->clanid=0; + } + else + { + row = mysql_fetch_row(result); + Clan->logo = atoi(row[0]); + Clan->back = atoi(row[1]); + strcpy(Clan->clanname,row[2]); + Clan->CP = atoi(row[3]); + Clan->grade = atoi(row[4]); + } + GServer->DB->QFree( ); + } + memset(&quest, 0, sizeof(SQuestData)); + //Log( MSG_DEBUG, "loaded clan info"); + UINT QuestToggle = 0; + UINT rowcount = 0; + /* + result = GServer->DB->QStore("SELECT quests from list_quest where owner=%i",CharInfo->charid ); + if(result!=NULL) + { + while(row = mysql_fetch_row(result)) + { + if( row[0] != NULL ) + { + memcpy(&quest, row[0], sizeof(SQuestData)); + rowcount++; + } + } + if(rowcount == 0) //no data detected + { + //Log( MSG_DEBUG, "No quest data detected with Exjam method. using PY method instead"); + QuestToggle = 1; // set toggle to use PY method + GServer->DB->QFree( ); + } + if(QuestToggle == 0) + { + //delete the old quest entry after loading it. + //GServer->DB->QExecute( "DELETE FROM list_quest WHERE owner=%i",thisclient->CharInfo->charid ); + //now convert quest flags to new system + + for(int flagid=0;flagid<512;flagid++) + { + dword byteid = flagid / 8; + dword bitid = flagid % 8; + if (((quest.flags[byteid] >> bitid) & 1) == 1) + quest.Qflags[byteid][bitid] = 1; + else + quest.Qflags[byteid][bitid] = 0; + } + GServer->DB->QFree( ); + Log( MSG_DEBUG, "loaded quests via Exjam method"); + savequests(this); + Log( MSG_DEBUG, "Converted flags and re-saved quests info"); + } + } + else + { + GServer->DB->QFree( ); + } + if(QuestToggle = 1) + */ + { // PY quest loading. Only performed if no old quests are found. Won't be any since there are no old characters + Log( MSG_DEBUG, "starting PY quest load"); + result = GServer->DB->QStore("SELECT questid,starttime,vbl,switches,items,count,episodevar,jobvar,planetvar,unionvar from player_quests where owner=%i",CharInfo->charid ); + //Log( MSG_DEBUG, "ran the query"); + tmpid = ""; + itmp = ""; + if(result != NULL) + { + UINT Index = 0; + while(row = mysql_fetch_row(result)) + { + quest.quests[Index].QuestID = atoi(row[0]); + quest.quests[Index].StartTime = atoi(row[1]); + + tmpid = strtok( row[2] , "|"); + for(unsigned int j=0;j<=10;j++) //quest variables + { + if(tmpid != NULL) + quest.quests[Index].Variables[j] = atoi(tmpid); + else + quest.quests[Index].Variables[j] = 0; + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest Var[%i][%i] = %i",Index ,j ,quest.quests[Index].Variables[j]); + } + tmpid = strtok( row[3] , "|"); //quest switches + for(unsigned int j=0;j<=4;j++) + { + if(tmpid != NULL) + quest.quests[Index].Switches[j] = atoi(tmpid); + else + quest.quests[Index].Switches[j] = 0; + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest Switch[%i][%i] = %i",Index ,j ,quest.quests[Index].Switches[j]); + } + tmpid = strtok( row[4] , "|"); //quest items + for(unsigned int j=0;j<=5;j++) + { + if(tmpid != NULL) + { + itemid = atoi(tmpid); + quest.quests[Index].Items[j].itemnum = itemid % 1000; + quest.quests[Index].Items[j].itemtype = itemid / 1000; + } + else + { + quest.quests[Index].Items[j].itemnum = 0; + quest.quests[Index].Items[j].itemtype = 0; + } + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest Item[%i][%i] = %i",Index , j ,quest.quests[Index].Items[j].itemnum); + } + tmpid = strtok( row[5] , "|"); //quest item count + for(unsigned int j=0;j<=5;j++) + { + if(tmpid != NULL) + quest.quests[Index].Items[j].count = atoi(tmpid); + else + quest.quests[Index].Items[j].count = 0; + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest Item Count[%i][%i] = %i",Index ,j ,quest.quests[Index].Items[j].count); + } + tmpid = strtok( row[6] , "|"); + for(unsigned int j=0;j<=5;j++) //episode variables + { + if(tmpid != NULL) + quest.EpisodeVar[j] = atoi(tmpid); + else + quest.EpisodeVar[j] = 0; + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest EpisodeVar[%i][%i] = %i",Index ,j ,quest.EpisodeVar[j]); + } + tmpid = strtok( row[7] , "|"); //job variables + for(unsigned int j=0;j<=3;j++) + { + if(tmpid != NULL) + quest.JobVar[j] = atoi(tmpid); + else + quest.JobVar[j] = 0; + tmpid = strtok(NULL, ",|"); + //Log( MSG_DEBUG, "Quest JobVar[%i][%i] = %i",Index ,j ,quest.JobVar[j]); + } + tmpid = strtok( row[8] , "|"); //Planet variables + for(unsigned int j=0;j<=7;j++) + { + if(tmpid != NULL) + quest.PlanetVar[j] = atoi(tmpid); + else + quest.PlanetVar[j] = 0; + tmpid = strtok(NULL, ",|"); + } + tmpid = strtok( row[9] , "|"); //Planet variables + for(unsigned int j=0;j<=10;j++) + { + if(tmpid != NULL) + quest.UnionVar[j] = atoi(tmpid); + else + quest.UnionVar[j] = 0; + tmpid = strtok(NULL, ",|"); + } + Index ++; + } + } + //Log( MSG_DEBUG, "loaded the regular quest variables"); + GServer->DB->QFree( ); + // PY method load quest flags + //Log( MSG_DEBUG, "starting PY flag load"); + result = GServer->DB->QStore("SELECT flags from questflags where owner=%i",CharInfo->charid ); + //Log( MSG_DEBUG, "ran the query"); + if(result != NULL) + { + tmpid = ""; + itmp = 0; + UINT Index = 0; + UINT flagCount = 0; + //first set all quest flags to zero + for(int i=0;i<64;i++) + { + for(int j=0;j<8;j++) + quest.Qflags[i][j] = 0; + } + while(row = mysql_fetch_row(result)) + { + int flagid = atoi(row[0]); + dword byteid = flagid / 8; + dword bitid = flagid % 8; + quest.Qflags[byteid][bitid] = 1; + flagCount++; + } + //Log( MSG_DEBUG, "Flag count = %i",flagCount); + } + GServer->DB->QFree( ); + //Log( MSG_DEBUG, "loaded quests info via PY method"); + } + Session->isLoggedIn = true; + + + + + // Timestamp for last login + time_t rtime; + time(&rtime); + char *timestamp = ctime(&rtime); + timestamp[ strlen(timestamp)-1 ] = ' '; + //Log(MSG_DEBUG,"setting time stamp fo login: %s for userid: %u",timestamp,Session->userid ); + GServer->DB->QExecute("UPDATE accounts SET online=1, lastlogin='%s' where id=%u", timestamp,Session->userid ); + Session->LogTime = clock(); + //Log(MSG_INFO,"set datestamp"); + + // Calculate how many fairies are available online, according to amount of players + int oldFairyMax = GServer->Config.FairyMax; + GServer->Config.FairyMax = (int)ceil((float)GServer->ClientList.size() / 50.0) + GServer->Config.ScrollFairy; //1 fairy for every 50 player + the number of fairies summoned by scrolls + if( oldFairyMax < GServer->Config.FairyMax ){ + CFairy* thisfairy = new (nothrow) CFairy; + thisfairy->LastTime = clock(); + thisfairy->assigned = 0; + thisfairy->ListIndex = 0; + thisfairy->WaitTime = GServer->Config.FairyWait; + GServer->FairyList.push_back( thisfairy ); + } + //die_quest = 0; + + return true; +} + +// Trying a new save method PY (nothing changed yet. just made a copy of savedata for safety +void CPlayer::savedata( ) +{ + lastSaveTime = clock(); + if(Session->userid!=0) + { + CMap* map = GServer->MapList.Index[Position->Map]; + CRespawnPoint* thisrespawn = map->GetNearRespawn( this ); + CPlayer* thisclient = GServer->GetClientByID( clientid ); + if(thisrespawn == NULL) + Position->respawn = 4; + else + Position->respawn = thisrespawn->id; + char quick[1024]; + char basic[1024]; + //char sclass[1024]; + //char slevel[1024]; + char active[1024]; + char activelvl[1024]; + char pasive[1024]; + char pasivelvl[1024]; + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&basic[i], "%i",bskills[i]); + else + sprintf(&basic[strlen(basic)], ",%i",bskills[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&active[i], "%i",askill[i]); + else + sprintf(&active[strlen(active)], ",%i",askill[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&activelvl[i], "%i",askilllvl[i]); + else + sprintf(&activelvl[strlen(activelvl)], ",%i",askilllvl[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&pasive[i], "%i",pskill[i]); + else + sprintf(&pasive[strlen(pasive)], ",%i",pskill[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&pasivelvl[i], "%i",pskilllvl[i]); + else + sprintf(&pasivelvl[strlen(pasivelvl)], ",%i",pskilllvl[i]); + } + for(UINT i=0;i<32;i++) + { + if(i==0) + sprintf(&quick[i], "%i",quickbar[i]); + else + sprintf(&quick[strlen(quick)], ",%i",quickbar[i]); + } + long int hp = Stats->HP; + if(hp<=0) + hp=Stats->MaxHP * 10 / 100; + if(Stats->MP<0) + Stats->MP=0; + + GServer->DB->QExecute("UPDATE characters SET level=%i,face=%i,hairStyle=%i,sex=%i,classid=%i,zuly=%i,str=%i,dex=%i,_int=%i, con=%i,cha=%i,sen=%i,curHp=%i,curMp=%i,id=%i,statp=%i,skillp=%i,exp=%i,stamina=%i,quickbar='%s',basic_skills='%s',askill='%s',askilllvl='%s',pskill='%s',pskilllvl='%s',respawnid=%i,clanid=%i,clan_rank=%i,townid=%i WHERE char_name='%s'", + Stats->Level, CharInfo->Face, CharInfo->Hair, CharInfo->Sex, CharInfo->Job, CharInfo->Zulies, Attr->Str, Attr->Dex, Attr->Int, Attr->Con, Attr->Cha, Attr->Sen, hp, Stats->MP, CharInfo->charid, CharInfo->StatPoints, CharInfo->SkillPoints, + CharInfo->Exp, CharInfo->stamina, quick, basic, active, activelvl, pasive, pasivelvl, Position->respawn, Clan->clanid, Clan->clanrank, Position->saved, CharInfo->charname ); + + GServer->DB->QExecute("UPDATE characters SET unionid=%i,union1points=%i,union2points=%i,union3points=%i,union4points=%i,union5points=%i,union6points=%i,union7points=%i,union8points=%i,union9points=%i,union10points=%i WHERE char_name='%s'", + Union_s->unionvar[0], Union_s->unionvar[1],Union_s->unionvar[2], Union_s->unionvar[3], Union_s->unionvar[4], Union_s->unionvar[5], Union_s->unionvar[6], Union_s->unionvar[7], Union_s->unionvar[8], Union_s->unionvar[9], Union_s->unionvar[10], CharInfo->charname ); + + //save the inventory + saveinventory(); + //save the storage items + savestorage(); + + //this lot to be removed later + //if(!GServer->DB->QExecute("DELETE FROM items WHERE owner=%i", CharInfo->charid)) return; + //for(UINT i=0;i 0) + // { + // GServer->DB->QExecute("INSERT INTO items (owner,itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem) VALUES(%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)", + // CharInfo->charid, items[i].itemnum, items[i].itemtype,items[i].refine, items[i].durability, + // items[i].lifespan, i, items[i].count, items[i].stats, (items[i].socketed?1:0), + // (items[i].appraised?1:0),items[i].gem ); + // } + //} + + + //Why is this still here and active? + //if savestorage is live and functional then this antiquated crap needs to be removed + + if(!GServer->DB->QExecute("DELETE FROM storage WHERE owner=%i", Session->userid)) return; + for(UINT i=0;i<160;i++) + { + if (storageitems[i].count > 0) + { + GServer->DB->QExecute("INSERT INTO storage (owner,itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem) VALUES(%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)", + Session->userid, storageitems[i].itemnum, storageitems[i].itemtype,storageitems[i].refine, storageitems[i].durability, + storageitems[i].lifespan, i, storageitems[i].count, storageitems[i].stats, (storageitems[i].socketed?1:0), + (storageitems[i].appraised?1:0), storageitems[i].gem ); + } + } + + savequests( this ); + GServer->DB->QExecute("UPDATE accounts SET zulystorage = %i, logtime = %i,ktpoints=%i WHERE id = %i", CharInfo->Storage_Zulies, Session->logtime,Session->KTPoints, Session->userid); + Log(MSG_INFO, "Data Saved for char '%s' ", CharInfo->charname ); + } +} + +//PY Character save +void CPlayer::quicksave( ) +{ + GServer->DB->QExecute("UPDATE characters SET level=%i,face=%i,hairStyle=%i,sex=%i,classid=%i,zuly=%i,str=%i,dex=%i,_int=%i, con=%i,cha=%i,sen=%i,curHp=%i,curMp=%i,id=%i,statp=%i,skillp=%i,exp=%i,stamina=%i,respawnid=%i,clanid=%i,clan_rank=%i,townid=%i WHERE char_name='%s'", + Stats->Level, CharInfo->Face, CharInfo->Hair, CharInfo->Sex, CharInfo->Job, CharInfo->Zulies, Attr->Str, Attr->Dex, Attr->Int, Attr->Con, Attr->Cha, Attr->Sen, Stats->HP, Stats->MP, CharInfo->charid, CharInfo->StatPoints, CharInfo->SkillPoints, + CharInfo->Exp, CharInfo->stamina, Position->respawn, Clan->clanid, Clan->clanrank, Position->saved, CharInfo->charname ); + Log(MSG_INFO, "Data Quick Saved for char '%s' ", CharInfo->charname ); +} + +//PY Inventory save +void CPlayer::saveinventory( ) +{ + //build the strings + char tmp1[1024] = ""; + char tmp2[1024] = ""; + char tmp3[1024] = ""; + char tmp4[1024] = ""; + char tmp5[1024] = ""; + char tmp6[1024] = ""; + char tmp7[1024] = ""; + char tmp8[1024] = ""; + char tmp9[1024] = ""; + char slot[1024] = ""; + long tran = 0; + for(unsigned int i=0;i<=MAX_INVENTORY; i++) //inventory holds 140 + { + int socketed = 0; + int appraised = 0; + tran += items[i].itemnum + items[i].itemtype + items[i].refine; + //tran += items[i].durability + items[i].lifespan + items[i].count + items[i].stats; + //Log(MSG_INFO,"slot: %i itemid: %i type: %i count: %i",i, items[i].itemnum,items[i].itemtype,items[i].count); + if(i == 0) + { + if(items[i].count == 0) + { + sprintf(&tmp1[strlen(tmp1)], "0"); + sprintf(&tmp2[strlen(tmp2)], "0"); + sprintf(&tmp3[strlen(tmp3)], "0"); + sprintf(&tmp4[strlen(tmp4)], "0"); + sprintf(&tmp5[strlen(tmp5)], "0"); + sprintf(&tmp6[strlen(tmp6)], "0"); + sprintf(&tmp7[strlen(tmp7)], "0"); + sprintf(&tmp8[strlen(tmp8)], "0"); + sprintf(&tmp9[strlen(tmp9)], "0"); + sprintf(&slot[strlen(slot)], "%i",i); + } + else + { + if(items[i].socketed == true) + socketed = 1; + if(items[i].appraised == true) + appraised = 1; + sprintf(&tmp1[strlen(tmp1)], "%i",items[i].itemnum); + sprintf(&tmp2[strlen(tmp2)], "%i",items[i].itemtype); + sprintf(&tmp3[strlen(tmp3)], "%i",items[i].refine); + sprintf(&tmp4[strlen(tmp4)], "%i",items[i].durability); + sprintf(&tmp5[strlen(tmp5)], "%i",items[i].lifespan); + sprintf(&tmp6[strlen(tmp6)], "%i",items[i].count); + sprintf(&tmp7[strlen(tmp7)], "%i",items[i].stats); + sprintf(&tmp8[strlen(tmp8)], "%i",socketed); + sprintf(&tmp9[strlen(tmp9)], "%i",appraised); + sprintf(&slot[strlen(slot)], "%i",i); + } + } + else + { + if(items[i].count == 0) + { + sprintf(&tmp1[strlen(tmp1)], "|0"); + sprintf(&tmp2[strlen(tmp2)], "|0"); + sprintf(&tmp3[strlen(tmp3)], "|0"); + sprintf(&tmp4[strlen(tmp4)], "|0"); + sprintf(&tmp5[strlen(tmp5)], "|0"); + sprintf(&tmp6[strlen(tmp6)], "|0"); + sprintf(&tmp7[strlen(tmp7)], "|0"); + sprintf(&tmp8[strlen(tmp8)], "|0"); + sprintf(&tmp9[strlen(tmp9)], "|0"); + sprintf(&slot[strlen(slot)], "|%i",i); + } + else + { + if(items[i].socketed == true) + socketed = 1; + if(items[i].appraised == true) + appraised = 1; + if(items[i].gem != 0 && items[i].gem != items[i].stats) + items[i].stats = items[i].gem; + sprintf(&tmp1[strlen(tmp1)], "|%i",items[i].itemnum); + sprintf(&tmp2[strlen(tmp2)], "|%i",items[i].itemtype); + sprintf(&tmp3[strlen(tmp3)], "|%i",items[i].refine); + sprintf(&tmp4[strlen(tmp4)], "|%i",items[i].durability); + sprintf(&tmp5[strlen(tmp5)], "|%i",items[i].lifespan); + sprintf(&tmp6[strlen(tmp6)], "|%i",items[i].count); + sprintf(&tmp7[strlen(tmp7)], "|%i",items[i].stats); + sprintf(&tmp8[strlen(tmp8)], "|%i",socketed); + sprintf(&tmp9[strlen(tmp9)], "|%i",appraised); + sprintf(&slot[strlen(slot)], "|%i",i); + } + } + } + //Log(MSG_INFO,"Save string1 = %s ", tmp1); + //Log(MSG_INFO,"Save string2 = %s ", tmp2); + //Log(MSG_INFO,"Save string3 = %s ", tmp3); + //Log(MSG_INFO,"Save string4 = %s ", tmp4); + //Log(MSG_INFO,"Save string5 = %s ", tmp5); + //Log(MSG_INFO,"Save string6 = %s ", tmp6); + //Log(MSG_INFO,"Save string7 = %s ", tmp7); + //Log(MSG_INFO,"Save string8 = %s ", tmp8); + //Log(MSG_INFO,"Save string9 = %s ", tmp9); + //check if a record exists for this player + MYSQL_RES *result = GServer->DB->QStore("SELECT itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,slot,trans_action FROM charitems WHERE owner=%i", CharInfo->charid); + MYSQL_ROW row; + bool doesexist = false; + while(row = mysql_fetch_row(result)) + { + doesexist = true; + } + GServer->DB->QFree( ); + if(doesexist == true) + { + //record exists so update it + //Log(MSG_INFO,"record exists.... updating"); + GServer->DB->QExecute("UPDATE charitems SET itemid='%s',itemtype='%s',refine='%s',durability='%s',lifespan='%s',count='%s',stats='%s',socketed='%s',appraised='%s',slot='%s',trans_action=%i WHERE owner='%i'", + tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,slot,tran,CharInfo->charid); + } + else + { + //record does not exist so make a new one + //Log(MSG_INFO,"record does not exist.... creating new record"); + //GServer->DB->QExecute("INSERT INTO charitems (owner,trans_action) VALUES ('%i','%i')",CharInfo->charid,tran); + GServer->DB->QExecute("INSERT INTO charitems (owner,itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,trans_action) VALUES('%i','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%i')", + CharInfo->charid,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,slot,tran); + } + //save zuly + GServer->DB->QExecute("UPDATE characters SET zuly=%i WHERE char_name='%s'", + CharInfo->Zulies, CharInfo->charname ); + //Log(MSG_INFO,"Inventory saved"); + return; +} + +//PY Storage save +void CPlayer::savestorage( ) +{ + //build the strings. As we need a slot zero for storage we are going to have to add a buffer entry first + //for some bizarre reason when we load teh storage it ignores the leading zero so stuff gets shifted left without this + char tmp1[1024] = "0"; + char tmp2[1024] = "0"; + char tmp3[1024] = "0"; + char tmp4[1024] = "0"; + char tmp5[1024] = "0"; + char tmp6[1024] = "0"; + char tmp7[1024] = "0"; + char tmp8[1024] = "0"; + char tmp9[1024] = "0"; + char slot[1024] = "0"; + long tran = 0; + for(unsigned int i=0;i<=MAX_STORAGE; i++) //Storage holds 160 + { + int socketed = 0; + int appraised = 0; + tran += storageitems[i].itemnum + storageitems[i].itemtype + storageitems[i].refine; + tran += storageitems[i].durability + storageitems[i].lifespan + storageitems[i].count + storageitems[i].stats; + + if(storageitems[i].count == 0) + { + sprintf(&tmp1[strlen(tmp1)], "|0"); + sprintf(&tmp2[strlen(tmp2)], "|0"); + sprintf(&tmp3[strlen(tmp3)], "|0"); + sprintf(&tmp4[strlen(tmp4)], "|0"); + sprintf(&tmp5[strlen(tmp5)], "|0"); + sprintf(&tmp6[strlen(tmp6)], "|0"); + sprintf(&tmp7[strlen(tmp7)], "|0"); + sprintf(&tmp8[strlen(tmp8)], "|0"); + sprintf(&tmp9[strlen(tmp9)], "|0"); + if(i != 0) //don't write the zero value for slot since we already started the string with a leading zero + sprintf(&slot[strlen(slot)], "|%i",i); + } + else + { + if(storageitems[i].socketed == true) + socketed = 1; + if(storageitems[i].appraised == true) + appraised = 1; + sprintf(&tmp1[strlen(tmp1)], "|%i",storageitems[i].itemnum); + sprintf(&tmp2[strlen(tmp2)], "|%i",storageitems[i].itemtype); + sprintf(&tmp3[strlen(tmp3)], "|%i",storageitems[i].refine); + sprintf(&tmp4[strlen(tmp4)], "|%i",storageitems[i].durability); + sprintf(&tmp5[strlen(tmp5)], "|%i",storageitems[i].lifespan); + sprintf(&tmp6[strlen(tmp6)], "|%i",storageitems[i].count); + sprintf(&tmp7[strlen(tmp7)], "|%i",storageitems[i].stats); + sprintf(&tmp8[strlen(tmp8)], "|%i",socketed); + sprintf(&tmp9[strlen(tmp9)], "|%i",appraised); + if(i != 0) //don't write the zero value for slot since we already started the string with a leading zero + sprintf(&slot[strlen(slot)], "|%i",i); + } + } + //check if a record exists for this player + MYSQL_RES *result = GServer->DB->QStore("SELECT itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,slot,trans_action FROM charstorage WHERE owner=%i", Session->userid); + MYSQL_ROW row; + bool doesexist = false; + while(row = mysql_fetch_row(result)) + { + doesexist = true; + } + GServer->DB->QFree( ); + if(doesexist == true) + { + //record exists so update it + //Log(MSG_INFO,"record exists.... updating"); + GServer->DB->QExecute("UPDATE charstorage SET itemid='%s',itemtype='%s',refine='%s',durability='%s',lifespan='%s',count='%s',stats='%s',socketed='%s',appraised='%s',slot='%s',trans_action=%i WHERE owner='%i'", + tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,slot,tran,Session->userid); + } + else + { + //record does not exist so make a new one + //Log(MSG_INFO,"record does not exist.... creating new record"); + //GServer->DB->QExecute("INSERT INTO charitems (owner,trans_action) VALUES ('%i','%i')",CharInfo->charid,tran); + GServer->DB->QExecute("INSERT INTO charstorage (owner,itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,slot,trans_action) VALUES('%i','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%i')", + Session->userid,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,slot,tran); + } + //save zuly storage + GServer->DB->QExecute("UPDATE accounts SET zulystorage = %i WHERE id = %i", CharInfo->Storage_Zulies, Session->userid); + //Log(MSG_INFO,"Storage saved"); + return; +} + +//PY Storage save +void CPlayer::SaveBackupStorage( int logstate ) +{ + //build the strings. As we need a slot zero for storage we are going to have to add a buffer entry first + //for some bizarre reason when we load the storage it ignores the leading zero so stuff gets shifted left without this + char tmp1[1024] = "0"; + char tmp2[1024] = "0"; + char tmp3[1024] = "0"; + char tmp4[1024] = "0"; + char tmp5[1024] = "0"; + char tmp6[1024] = "0"; + char tmp7[1024] = "0"; + char tmp8[1024] = "0"; + char tmp9[1024] = "0"; + char slot[1034] = "0"; + long tran = 0; + for(unsigned int i=0;i<=MAX_STORAGE; i++) //Storage holds 160 + { + int socketed = 0; + int appraised = 0; + if(storageitems[i].count == 0) + { + sprintf(&tmp1[strlen(tmp1)], "|0"); + sprintf(&tmp2[strlen(tmp2)], "|0"); + sprintf(&tmp3[strlen(tmp3)], "|0"); + sprintf(&tmp4[strlen(tmp4)], "|0"); + sprintf(&tmp5[strlen(tmp5)], "|0"); + sprintf(&tmp6[strlen(tmp6)], "|0"); + sprintf(&tmp7[strlen(tmp7)], "|0"); + sprintf(&tmp8[strlen(tmp8)], "|0"); + sprintf(&tmp9[strlen(tmp9)], "|0"); + if(i != 0) //don't write the zero value for slot since we already started the string with a leading zero + sprintf(&slot[strlen(slot)], "|%i",i); + } + else + { + if(storageitems[i].socketed == true) + socketed = 1; + if(storageitems[i].appraised == true) + appraised = 1; + sprintf(&tmp1[strlen(tmp1)], "|%i",storageitems[i].itemnum); + sprintf(&tmp2[strlen(tmp2)], "|%i",storageitems[i].itemtype); + sprintf(&tmp3[strlen(tmp3)], "|%i",storageitems[i].refine); + sprintf(&tmp4[strlen(tmp4)], "|%i",storageitems[i].durability); + sprintf(&tmp5[strlen(tmp5)], "|%i",storageitems[i].lifespan); + sprintf(&tmp6[strlen(tmp6)], "|%i",storageitems[i].count); + sprintf(&tmp7[strlen(tmp7)], "|%i",storageitems[i].stats); + sprintf(&tmp8[strlen(tmp8)], "|%i",socketed); + sprintf(&tmp9[strlen(tmp9)], "|%i",appraised); + if(i != 0) //don't write the zero value for slot since we already started the string with a leading zero + sprintf(&slot[strlen(slot)], "|%i",i); + } + } + MYSQL_RES *result = GServer->DB->QStore("SELECT thisrev FROM backupstorage WHERE owner=%i", Session->userid); + MYSQL_ROW row; + int thisrev = 0; + int tmprev = 0; + while(row = mysql_fetch_row(result)) + { + tmprev = atoi(row[0]); + if(tmprev > thisrev)thisrev = tmprev; + } + thisrev ++; // add 1 to the highest existing rev number for this user + GServer->DB->QFree( ); + // Timestamp + time_t rtime; + time(&rtime); + char *timestamp = ctime(&rtime); + timestamp[ strlen(timestamp)-1 ] = ' '; + GServer->DB->QExecute("INSERT INTO backupstorage (owner,itemid,itemtype,refine,durability,lifespan,count,stats,socketed,appraised,slot,trans_action,datestamp,thisrev) VALUES('%i','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%i','%s','%i')", + Session->userid,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9,slot,logstate,timestamp,thisrev); + //Log(MSG_INFO,"backup Storage saved for User %i revision number %i logstate %i",Session->userid, thisrev, logstate); + return; +} + + +// This is a REALLY bad way of saving the character data, but it works ^^ +/*void CPlayer::savedata( ) +{ + lastSaveTime = clock(); + if(Session->userid!=0) + { + CMap* map = GServer->MapList.Index[Position->Map]; + CRespawnPoint* thisrespawn = map->GetNearRespawn( this ); + CPlayer* thisclient = GServer->GetClientByID( clientid ); + if(thisrespawn == NULL) + Position->respawn = 4; + else + Position->respawn = thisrespawn->id; + char quick[1024]; + char basic[1024]; + //char sclass[1024]; + //char slevel[1024]; + char active[1024]; + char activelvl[1024]; + char pasive[1024]; + char pasivelvl[1024]; + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&basic[i], "%i",bskills[i]); + else + sprintf(&basic[strlen(basic)], ",%i",bskills[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&active[i], "%i",askill[i]); + else + sprintf(&active[strlen(active)], ",%i",askill[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&activelvl[i], "%i",askilllvl[i]); + else + sprintf(&activelvl[strlen(activelvl)], ",%i",askilllvl[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&pasive[i], "%i",pskill[i]); + else + sprintf(&pasive[strlen(pasive)], ",%i",pskill[i]); + } + for(UINT i=0;i<30;i++) + { + if(i==0) + sprintf(&pasivelvl[i], "%i",pskilllvl[i]); + else + sprintf(&pasivelvl[strlen(pasivelvl)], ",%i",pskilllvl[i]); + } + for(UINT i=0;i<32;i++) + { + if(i==0) + sprintf(&quick[i], "%i",quickbar[i]); + else + sprintf(&quick[strlen(quick)], ",%i",quickbar[i]); + } + long int hp = Stats->HP; + if(hp<=0) + hp=Stats->MaxHP * 10 / 100; + if(Stats->MP<0) + Stats->MP=0; + //Save data complete now?? (Pepu) + + GServer->DB->QExecute("UPDATE characters SET level=%i,face=%i,hairStyle=%i,sex=%i,classid=%i,zuly=%i,str=%i,dex=%i,_int=%i, con=%i,cha=%i,sen=%i,curHp=%i,curMp=%i,id=%i,statp=%i,skillp=%i,exp=%i,stamina=%i,quickbar='%s',basic_skills='%s',askill='%s',askilllvl='%s',pskill='%s',pskilllvl='%s',respawnid=%i,clanid=%i,clan_rank=%i,townid=%i WHERE char_name='%s'", + Stats->Level, CharInfo->Face, CharInfo->Hair, CharInfo->Sex, CharInfo->Job, CharInfo->Zulies, Attr->Str, Attr->Dex, Attr->Int, Attr->Con, Attr->Cha, Attr->Sen, hp, Stats->MP, CharInfo->charid, CharInfo->StatPoints, CharInfo->SkillPoints, + CharInfo->Exp, CharInfo->stamina, quick, basic, active, activelvl, pasive, pasivelvl, Position->respawn, Clan->clanid, Clan->clanrank, Position->saved, CharInfo->charname ); + + GServer->DB->QExecute("UPDATE characters SET unionid=%i,union1points=%i,union2points=%i,union3points=%i,union4points=%i,union5points=%i,union6points=%i,union7points=%i,union8points=%i,union9points=%i,union10points=%i WHERE char_name='%s'", + Union_s->unionvar[0], Union_s->unionvar[1],Union_s->unionvar[2], Union_s->unionvar[3], Union_s->unionvar[4], Union_s->unionvar[5], Union_s->unionvar[6], Union_s->unionvar[7], Union_s->unionvar[8], Union_s->unionvar[9], Union_s->unionvar[10], CharInfo->charname ); + + + if(!GServer->DB->QExecute("DELETE FROM items WHERE owner=%i", CharInfo->charid)) return; + for(UINT i=0;i 0) + { + GServer->DB->QExecute("INSERT INTO items (owner,itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem) VALUES(%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)", + CharInfo->charid, items[i].itemnum, items[i].itemtype,items[i].refine, items[i].durability, + items[i].lifespan, i, items[i].count, items[i].stats, (items[i].socketed?1:0), + (items[i].appraised?1:0),items[i].gem ); + } + } + if(!GServer->DB->QExecute("DELETE FROM storage WHERE owner=%i", Session->userid)) return; + for(UINT i=0;i<160;i++) + { + if (storageitems[i].count > 0) + { + GServer->DB->QExecute("INSERT INTO storage (owner,itemnum,itemtype,refine,durability,lifespan,slotnum,count,stats,socketed,appraised,gem) VALUES(%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i)", + Session->userid, storageitems[i].itemnum, storageitems[i].itemtype,storageitems[i].refine, storageitems[i].durability, + storageitems[i].lifespan, i, storageitems[i].count, storageitems[i].stats, (storageitems[i].socketed?1:0), + (storageitems[i].appraised?1:0), storageitems[i].gem ); + } + } + savequests( this ); + GServer->DB->QExecute("UPDATE accounts SET zulystorage = %i WHERE id = %i", CharInfo->Storage_Zulies, Session->userid); + Log(MSG_INFO, "Data Saved for char '%s' ", CharInfo->charname ); + } +}*/ + +void CPlayer::savequests( CPlayer* thisclient ) +{ + //char* questBuffer = new char[sizeof(SQuestData)*3 +1]; + //mysql_real_escape_string(GServer->DB->Mysql, questBuffer, (const char*)&quest, sizeof(SQuestData)); + //GServer->DB->QExecute( "DELETE FROM list_quest WHERE owner=%i",thisclient->CharInfo->charid ); + //GServer->DB->QExecute("INSERT INTO list_quest (owner, quests) VALUES(%i,'%s')", + // thisclient->CharInfo->charid, questBuffer); + //delete questBuffer; + + //PY retro method + //GServer->DB->QFree( ); + //Log(MSG_DEBUG,"Started SaveQuests"); + GServer->DB->QExecute( "DELETE FROM player_quests WHERE owner=%i",thisclient->CharInfo->charid ); + //Log(MSG_DEBUG,"Ran the player_quests deletion query"); + for(int i=0;i<10;i++) + { + if (thisclient->quest.quests[i].QuestID == 0) continue; + char vbl[100] = ""; //make the variables list + for(int j=0;j<10;j++) + { + sprintf(&vbl[strlen(vbl)], "%u",thisclient->quest.quests[i].Variables[j]); + if(j < 9) + sprintf(&vbl[strlen(vbl)],"|"); + } + char switches[10] = ""; //make the switches list + for(int j=0;j<4;j++) + { + sprintf(&switches[strlen(switches)], "%u",thisclient->quest.quests[i].Switches[j]); + if(j < 3) + sprintf(&switches[strlen(switches)],"|"); + } + char itemslist[100] = ""; //make the items list. Changed items to itemslist due to warning. + char itemcount[100] = ""; //item counts + for(int j=0;j<5;j++) + { + int ItemID = thisclient->quest.quests[i].Items[j].itemtype * 1000 + thisclient->quest.quests[i].Items[j].itemnum; + //sprintf(&items[strlen(items)], "%i",thisclient->quest.quests[i].Items[j].itemnum); + sprintf(&itemslist[strlen(itemslist)], "%i",ItemID); + sprintf(&itemcount[strlen(itemcount)], "%i",thisclient->quest.quests[i].Items[j].count); + if(j < 4) + { + sprintf(&itemslist[strlen(itemslist)],"|"); + sprintf(&itemcount[strlen(itemcount)],"|"); + } + } + char episode[50] = ""; //make the episode variable list + for(int j=0;j<5;j++) + { + sprintf(&episode[strlen(episode)], "%u",thisclient->quest.EpisodeVar[j]); + if(j < 4) + sprintf(&episode[strlen(episode)],"|"); + } + char jobvar[30] = ""; //make the job variable list + for(int j=0;j<3;j++) + { + sprintf(&jobvar[strlen(jobvar)], "%u",thisclient->quest.JobVar[j]); + if(j < 2) + sprintf(&jobvar[strlen(jobvar)],"|"); + } + char planetvar[70] = ""; //make the planet variable list + for(int j=0;j<7;j++) + { + sprintf(&planetvar[strlen(planetvar)], "%u",thisclient->quest.PlanetVar[j]); + if(j < 6) + sprintf(&planetvar[strlen(planetvar)],"|"); + } + char unionvar[100] = ""; //make the union variable list + for(int j=0;j<10;j++) + { + sprintf(&unionvar[strlen(unionvar)], "%u",thisclient->quest.UnionVar[j]); + if(j < 9) + sprintf(&unionvar[strlen(unionvar)],"|"); + } + GServer->DB->QExecute("INSERT INTO player_quests (owner, questid, starttime, vbl, switches, items, count, episodevar, jobvar, planetvar, unionvar) VALUES(%i,%i,%i,'%s','%s','%s','%s','%s','%s','%s','%s')", + thisclient->CharInfo->charid, thisclient->quest.quests[i].QuestID, thisclient->quest.quests[i].StartTime, + vbl, switches, itemslist, itemcount, episode, jobvar, planetvar, unionvar); + //Log(MSG_DEBUG,"Ran the player_quests insertion query"); + } + //GServer->DB->QFree( ); + //now the quest flags + GServer->DB->QExecute( "DELETE FROM questflags WHERE owner=%i",thisclient->CharInfo->charid ); + //Log(MSG_DEBUG,"Ran the questflag deletion query"); + char qflags[10000] = ""; + int flagCount = 0; + /* + for(int i=0;i<1000;i++) + { + if(thisclient->quest.Qflags[i] == 1) + { + if(sizeof(&qflags) > 995) + { + Log(MSG_WARNING,"Quest flag list too big for player %s",thisclient->CharInfo->charname); + i = 999; + continue; + } + if(flagCount == 0) + { + sprintf(&qflags[strlen(qflags)], "%i",i); + flagCount++; + } + else + { + sprintf(&qflags[strlen(qflags)],"|"); + sprintf(&qflags[strlen(qflags)], "%i",i); + flagCount++; + } + } + } + */ + + for(int i=0;i<0x40;i++) + { + for(int j=0;j<8;j++) + { + if(thisclient->quest.Qflags[i][j] != 0) + { + int flagid = (i*8)+j; + GServer->DB->QExecute("INSERT INTO questflags (owner, flags) VALUES(%i,%i)", + thisclient->CharInfo->charid, flagid); + //Log(MSG_DEBUG,"Ran the questflag deletion query"); + flagCount++; + } + } + } + //Log(MSG_DEBUG,"%i Quest flags detected during save process",flagCount); + +} diff --git a/Src/World Server/playerfunctions.cpp b/Src/World Server/playerfunctions.cpp new file mode 100644 index 0000000..6f49887 --- /dev/null +++ b/Src/World Server/playerfunctions.cpp @@ -0,0 +1,1055 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "player.h" +#include "worldserver.h" + +// Returns the amount of exp that is needed for the next level +UINT CPlayer::GetLevelEXP( ) +{ + if (Stats->Level <= 15) return (unsigned int)( ( Stats->Level + 10 ) * ( Stats->Level + 5 ) * ( Stats->Level + 3 ) * 0.7 ); + else if (Stats->Level <= 50) return (unsigned int)( ( Stats->Level - 5 ) * ( Stats->Level + 2 ) * ( Stats->Level + 2 ) * 2.2 ); + else if (Stats->Level <= 100) return (unsigned int)( ( Stats->Level - 38 ) * ( Stats->Level - 5 ) * ( Stats->Level + 2 ) * 9 ); + else if (Stats->Level <= 139) return (unsigned int)( ( Stats->Level + 220 ) * ( Stats->Level + 34 ) * ( Stats->Level + 27 ) ); + else return (unsigned int)( ( Stats->Level - 126 ) * ( Stats->Level - 15 ) * ( Stats->Level + 7 ) * 41 ); +} + +// check if player can level up +bool CPlayer::CheckPlayerLevelUP( ) +{ + //if(Session->Respawned == 2){Log( MSG_INFO, "Checking Levelup" );} + if (CharInfo->Exp >= GetLevelEXP()) + { + //if(Session->Respawned == 2){Log( MSG_INFO, "XP >= GetLevelXP" );} + CharInfo->Exp -= GetLevelEXP(); //exp is subtracted anyway whether we reached max or not. + //check if max level has been reached + if(Stats->Level >= GServer->Config.MaxLevel) + { + //posibly give extra bonuses here at some point rather than just subtracting exp and returning. + return true; // returning true should show the levelup animation still and also set HP and MP to max + } + Stats->Level++; + Stats->HP = GetMaxHP( ); + Stats->MP = GetMaxMP( ); + CharInfo->StatPoints += int((Stats->Level*0.8)+10); + // Proper skill point distribution for Pre-Evo + if(Stats->Level >= 100 && (Stats->Level % 2) == 0) + { + // 5 skill points every 2 levels from 100 + CharInfo->SkillPoints += 5; + } + else if(Stats->Level > 22 && ((Stats->Level - 22) % 4) == 0) + { + // 5 skill points every 4 levels from 22 + CharInfo->SkillPoints += 5; + } + else if(Stats->Level == 22) + { + CharInfo->SkillPoints += 4; + } + else if(Stats->Level == 18) + { + CharInfo->SkillPoints += 3; + } + else if(Stats->Level == 14 || Stats->Level == 10) + { + CharInfo->SkillPoints += 2; + } + + BEGINPACKET( pak, 0x79e ); + ADDWORD( pak, clientid ); + ADDWORD( pak, Stats->Level ); + ADDDWORD( pak, CharInfo->Exp ); + ADDWORD( pak, CharInfo->StatPoints ); + ADDWORD( pak, CharInfo->SkillPoints ); + client->SendPacket( &pak ); + + RESETPACKET( pak, 0x79e ); + ADDWORD( pak, clientid ); + GServer->SendToVisible( &pak, this ); + SetStats( ); + savedata( ); + //SendLevelUPtoChar(this); + return true; + } + //if(Session->Respawned == 2){Log( MSG_INFO, "XP < GetLevelXP" );} + return false; +} + +bool CPlayer::canUseSkill(CSkills* skill) +{ + + // Check for weapon requirements + int check = 5; + bool fail = true; + for (int i = 0; i < 5; i++) + { + if (skill->weapon[i] == 0) + { + check--; + continue; + } + for (int j = 1; j < 12; j++) { // I'm not sure if 1 - 12 is right for all equipment? + if (GServer->EquipList[items[j].itemtype].Index[items[j].itemnum]->type == skill->weapon[i]) + { + fail = false; + break; + } + } + if (!fail) break; + } + if (fail && check) + { + if(Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Failed skill check @ weapons. skill = %i", skill->id); + GServer->SendPM(this, buffer); + } + return false; + } + + // Check job requirement + fail = true; + check = 4; + for (char i = 0; i < 4; i++) + { + if (skill->c_class[i]>0) + { + switch(skill->c_class[i]) + { + case cSoldierJob: + if((CharInfo->Job)/100==1) fail = false; + break; + case cMuseJob: + if((CharInfo->Job)/100==2) fail = false; + break; + case cHawkerJob: + if((CharInfo->Job)/100==3) fail = false; + break; + case cDealerJob: + if((CharInfo->Job)/100==4) fail = false; + break; + case cKnightJob: + if(CharInfo->Job==121) fail = false; + break; + case cChampionJob: + if(CharInfo->Job==122) fail = false; + break; + case cMageJob: + if(CharInfo->Job==221) fail = false; + break; + case cClericJob: + if(CharInfo->Job==222) fail = false; + break; + case cRaiderJob: + if(CharInfo->Job==321) fail = false; + break; + case cScoutJob: + if(CharInfo->Job==322) fail = false; + break; + case cBourgeoisJob: + if(CharInfo->Job==421) fail = false; + break; + case cArtisanJob: + if(CharInfo->Job==422) fail = false; + break; + default: + break; + } + if (!fail) break; + } else + check--; + } + if (fail && check) { + if(Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Failed skill check @ job. job = %i skill = %i",CharInfo->Job, skill->id); + GServer->SendPM(this, buffer); + } + return false; + } + + // Check for stat requirements + fail = false; + for (char i = 0; i < 2; i++) + { + switch (skill->req[i]) + { + case sLevel: + if (skill->reqam[i] > Stats->Level) fail = true; + break; + case sStrength: + if (skill->reqam[i] > Attr->Str + Attr->Estr) fail = true; + break; + case sDexterity: + if (skill->reqam[i] > Attr->Dex + Attr->Edex) fail = true; + break; + case sIntelligence: + if (skill->reqam[i] > Attr->Int + Attr->Eint) fail = true; + break; + case sConcentration: + if (skill->reqam[i] > Attr->Con + Attr->Econ) fail = true; + break; + case sCharm: + if (skill->reqam[i] > Attr->Cha + Attr->Echa) fail = true; + break; + case sSensibility: + if (skill->reqam[i] > Attr->Sen + Attr->Esen) fail = true; + break; + default: + break; + } + } + if (fail) + { + if(Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Failed skill check @ stats. skill = %i", skill->id); + GServer->SendPM(this, buffer); + } + return false; + } + if(Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Skill check successful for skill = %i", skill->id); + GServer->SendPM(this, buffer); + } + // Passed all the tests, can cast skill + return true; +} + + +// Send a PM to client with user information +bool CPlayer::GetPlayerInfo( ) +{ + char text[50]; + sprintf(text,"Attack: %i | Critical: %i",Stats->Attack_Power, Stats->Critical ); + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"Defense: %i | Magic Defense: %i",Stats->Defense, Stats->Magic_Defense); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"Accury: %i | Dodge: %i",Stats->Accury,Stats->Dodge ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"aspeed: %i | mspeed: %i",Stats->Attack_Speed,Stats->Move_Speed ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"HP: %i/%i , MP: %i/%i",Stats->HP,Stats->MaxHP,Stats->MP,Stats->MaxMP); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"Position[%i]: (%.0f,%.0f)",Position->Map,Position->current.x,Position->current.y); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"ClientID: %u | CharID: %u", clientid, CharInfo->charid ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"inGame: %i | Logged: %i", Session->inGame, Session->isLoggedIn ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + sprintf(text,"ClanName[%u]: %s | ClanGrade: %i | ClanRank: %i", Clan->clanid, Clan->clanname, Clan->grade, Clan->clanrank ); + RESETPACKET( pak, 0x0784 ); + ADDSTRING( pak, "[GM]PlayerInfo" ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, text ); + ADDBYTE( pak, 0 ); + client->SendPacket(&pak); + return true; +} + +// clearn player lists +bool CPlayer::CleanPlayerVector( ) +{ + CMap* map = GServer->MapList.Index[Position->Map]; + VisiblePlayers.clear(); + VisibleMonsters.clear(); + VisibleDrops.clear(); + VisibleNPCs.clear(); + return true; +} + +// update visibility list +bool CPlayer::VisiblityList( ) +{ + if(!this->Session->inGame) return true; + std::vector newVisiblePlayers; + std::vector newVisibleDrops; + std::vector newVisibleMonsters; + std::vector newVisibleNPCs; + // Clients + CMap* map = GServer->MapList.Index[Position->Map]; + for(UINT i=0;iPlayerList.size();i++) + { + CPlayer* otherclient = map->PlayerList.at(i); + if(otherclient == NULL) + continue; + if ( this == otherclient || !otherclient->Session->inGame) + { + continue; + } + float distance = GServer->distance( this->Position->current, otherclient->Position->current ); + if ( GServer->IsVisible( this, otherclient ) ) + { + if ( distance < MAXVISUALRANGE && !otherclient->isInvisibleMode ) + { + newVisiblePlayers.push_back( otherclient ); + } + else + { + ClearObject( otherclient->clientid ); + } + } + else + { + if ( distance < MINVISUALRANGE && !otherclient->isInvisibleMode ) + { + newVisiblePlayers.push_back( otherclient ); + otherclient->SpawnToPlayer(this, otherclient); + } + } + } + // Monsters + for(UINT i=0;iMonsterList.size();i++) + { + CMonster* thismon = map->MonsterList.at( i ); + if(thismon == NULL) + continue; + float distance = GServer->distance ( this->Position->current, thismon->Position->current ); + if ( GServer->IsVisible( this, thismon ) ) + { + if (distance < MAXVISUALRANGE ) + { + newVisibleMonsters.push_back( thismon->clientid); + } + else + { + ClearObject( thismon->clientid ); + } + } + else + { + if ( distance< MINVISUALRANGE ) + { + newVisibleMonsters.push_back( thismon->clientid); + thismon->SpawnMonster(this, thismon ); + } + } + } + // Drops + for(unsigned i=0; iDropsList.size(); i++) + { + CDrop* thisdrop = map->DropsList.at(i); + if(thisdrop == NULL) + continue; + if( time(NULL) - thisdrop->droptime <= GServer->Config.DropDelay ) + continue; + float distance = GServer->distance( this->Position->current, thisdrop->pos ); + if ( GServer->IsVisible( this, thisdrop ) ) + { + if ( distance< MAXVISUALRANGE ) + { + newVisibleDrops.push_back( thisdrop ); + } + else + { + this->ClearObject( thisdrop->clientid ); + } + } + else + { + if ( distance < MINVISUALRANGE ) + { + thisdrop->spawned = true; + newVisibleDrops.push_back( thisdrop ); + GServer->pakSpawnDrop( this, thisdrop ); + } + } + } + // Npcs + for(unsigned i=0; iNPCList.size(); i++) + { + CNPC* thisnpc = map->NPCList.at(i); + float distance = GServer->distance( this->Position->current, thisnpc->pos ); + if ( GServer->IsVisible( this, thisnpc ) ) + { + if ( distance < MAXVISUALRANGE ) + { + newVisibleNPCs.push_back( thisnpc ); + } + else + { + this->ClearObject( thisnpc->clientid ); + } + } + else + { + if ( distance < MINVISUALRANGE ) + { + newVisibleNPCs.push_back( thisnpc ); + GServer->pakSpawnNPC( this, thisnpc ); + } + } + } + VisiblePlayers.clear(); + VisibleDrops.clear(); + VisibleMonsters.clear(); + VisibleNPCs.clear(); + VisiblePlayers = newVisiblePlayers; + VisibleDrops = newVisibleDrops; + VisibleMonsters = newVisibleMonsters; + VisibleNPCs = newVisibleNPCs; + return true; + +} + +bool CPlayer::ChangeDialog(unsigned int npc, unsigned int dialog, unsigned int eventid ) +{ + CMap* map = GServer->MapList.Index[Position->Map]; + if(map == NULL) + return true; + for(unsigned i=0; iNPCList.size(); i++) + { + CNPC* thisnpc = map->NPCList.at(i); + + if(thisnpc->npctype == npc) + { + //Log(MSG_INFO,"Found NPC %i with dialog %i",thisnpc->npctype, thisnpc->thisnpc->dialogid); + CNPC* newnpc = thisnpc; + this->ClearObject( thisnpc->clientid ); + newnpc->thisnpc->dialogid = dialog; + newnpc->thisnpc->eventid = eventid; + GServer->ObjVar[npc][0] = eventid; + VisibleNPCs.push_back( newnpc ); + GServer->pakSpawnNPC( this, newnpc ); + } + } + return true; +} + + +// Returns a free slot in the inventory (0xffff if is full) +UINT CPlayer::GetNewItemSlot( CItem thisitem ) +{ + UINT tabsize = 30; + UINT itemtab = 0; + switch(thisitem.itemtype) + { + case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9://equip + itemtab = 0; + break; + case 10://consumibles + itemtab = 1; + break; + case 11:case 12://etc + itemtab = 2; + break; + case 14://pat + itemtab = 3; + break; + default: + Log(MSG_WARNING,"unknown itemtype %i", thisitem.itemtype); + return 0xffff; + break; + } + //useitems and natural + //first we try to find a slot that already contains some of this item + //this way the items will always stack if possible. + if(itemtab == 1 || itemtab == 2) + { + for(int i=0;i<30;i++) + { + UINT slot=12; + slot += (tabsize*itemtab)+i; + if(items[slot].itemnum == thisitem.itemnum && items[slot].itemtype == thisitem.itemtype) + { + int totcount = thisitem.count + items[slot].count; + if(totcount < 1000) + return slot; + } + } + } + //now scan again to find a fully empty slot for mats, useitems and equipable stuff + //doesn't matter what tab type + for(int i=0;i<30;i++) + { + UINT slot=12; + slot += (tabsize*itemtab)+i; + if(items[slot].itemnum==0 &items[slot].count<1) + return slot; + } + return 0xffff; +} + +//count storage items +unsigned int CPlayer::Getnstorageitems( CPlayer* thisclient ) +{ + UINT count = 0; + for(UINT i=0;i<160;i++) + { + if(thisclient->storageitems[i].count > 0) + count++; + } + return count; +} + +// Returns a free slot in the storage (0xffff if is full) +UINT CPlayer::GetNewStorageItemSlot( CItem thisitem ) +{ + //first check for a slot that already contains this item if it is stackable + if(thisitem.itemtype == 10 || thisitem.itemtype == 11 || thisitem.itemtype == 12) + { + for(UINT i=0;i<144;i++) + { + if(storageitems[i].itemtype == thisitem.itemtype && storageitems[i].itemnum == thisitem.itemnum) + { + //now we make sure that the total of the new items plus the ones already there do not exceed 999 + int totalcount = storageitems[i].count + thisitem.count; + if(totalcount < 1000) + { + return i; + } + } + } + } + //so we couldn't find a matching entry to stack onto + //or the item is unstackable + //Let's look for an empty slot instead then + for(UINT i=0;i<144;i++) + { + if(storageitems[i].itemtype == 0) + { + + return i; + } + } + return 0xffff; +} + +// Erase a object from the user +bool CPlayer::ClearObject( unsigned int otherclientid ) +{ + BEGINPACKET( pak, 0x794 ); + ADDWORD( pak, otherclientid ); + client->SendPacket( &pak ); + return true; +} + +// Clean the player values +void CPlayer::RestartPlayerVal( ) +{ + ClearBattle( Battle ); + Shop->open = false; + Trade->trade_target = 0; + Trade->trade_status = 0; +} + +int CPlayer::GetQuestVar(short nVarType, short nVarNO){ + switch(nVarType){ + case 0: + { + SQuest* activeQuest = GetActiveQuest(); + if(activeQuest == NULL) return -1; + return activeQuest->Variables[nVarNO]; + } + case 0x100: + { + SQuest* activeQuest = GetActiveQuest(); + if(activeQuest == NULL) return -1; + return activeQuest->GetSwitchBit(nVarNO); + } + case 0x200://Remaining time + Log(MSG_DEBUG, "GetQuestVar got remaining quest time"); + return 1; + case 0x300: + if(nVarNO >= 5) return -1; + return quest.EpisodeVar[nVarNO]; + case 0x400: + if(nVarNO >= 3) return -1; + return quest.JobVar[nVarNO]; + case 0x500: + if(nVarNO >= 7) return -1; + return quest.PlanetVar[nVarNO]; + case 0x600: + if(nVarNO >= 10) return -1; + return quest.UnionVar[nVarNO]; + default: + break; + } + return -1; +} + +void CPlayer::SetQuestVar(short nVarType, short nVarNO, short nValue){ + switch(nVarType){ + case 0: + { + SQuest* activeQuest = GetActiveQuest(); + if(activeQuest == NULL) return; + activeQuest->Variables[nVarNO] = nValue; + } + return; + case 0x100: + { + SQuest* activeQuest = GetActiveQuest(); + if(activeQuest == NULL) return; + activeQuest->SetSwitchBit(nVarNO, nValue); + } + return; + case 0x300: + if(nVarNO >= 5) return; + quest.EpisodeVar[nVarNO] = nValue; + return; + case 0x400: + if(nVarNO >= 3) return; + quest.JobVar[nVarNO] = nValue; + return; + case 0x500: + if(nVarNO >= 7) return; + quest.PlanetVar[nVarNO] = nValue; + return; + case 0x600: + if(nVarNO >= 10) return; + quest.UnionVar[nVarNO] = nValue; + return; + default: + break; + } +} + + +int CPlayer::ExecuteQuestTrigger(dword hash) +{ + //Code that actually works. Taken from KTRose + CQuestTrigger* trigger = NULL; + CQuestTrigger* nexttrigger = NULL; + bool logme = false; + if (this->questdebug) logme = true; + CheckQuest = -1; + //if (GServer->questdebug) + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: searching for trigger %i in TriggerList with size %i", hash, GServer->TriggerList.size ()); + + for (unsigned j = 0; j < GServer->TriggerList.size (); j++) + { + if (GServer->TriggerList.at (j)->TriggerHash == hash) + { + trigger = GServer->TriggerList.at (j); + nexttrigger = GServer->TriggerList.at (j + 1); + break; + } + } + if (trigger == NULL) { + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: Could not find quest trigger. Returning Failure"); + return QUEST_FAILURE; + } + int success = QUEST_SUCCESS; + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: Trigger executed %s[%i]", trigger->TriggerName, trigger->CheckNext); + for (dword i = 0; i < trigger->ConditionCount; i++) + { + int command = trigger->Conditions[i]->opcode; + + if (command > 30 || command < 0) continue; + success = (*GServer->qstCondFunc[command])(GServer, this, trigger->Conditions[i]->data); + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: Condition %03u returned %d", command, success); + if (success == QUEST_FAILURE) + { + if (!trigger->CheckNext) + return success; + else return ExecuteQuestTrigger (nexttrigger->TriggerHash); + } + } + for (dword i = 0; i < trigger->ActionCount; i++) + { + int command = trigger->Actions[i]->opcode; + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: command %03u", command); + if (command > 35 || command < 0) continue; + /*if (this->questdebug) + Log(MSG_QUESTDEBUG, "ExecuteQuestTrigger:: Still good after command value check");*/ + success = (*GServer->qstRewdFunc[command])(GServer, this, trigger->Actions[i]->data); + if (this->questdebug) + Log (MSG_QUESTDEBUG, "ExecuteQuestTrigger:: reward %03u returned %d", command, success); + } + return success; + + + + + //CQuestTrigger* trigger = NULL; + // CQuestTrigger* nexttrigger = NULL; + // CheckQuest = -1; + // for(unsigned j=0; j < GServer->TriggerList.size(); j++) + // { + // if (GServer->TriggerList.at(j)->TriggerHash == hash) + // { + // trigger = GServer->TriggerList.at(j); + // nexttrigger = GServer->TriggerList.at(j + 1); + // break; + // } + // } + // if (trigger == NULL) return QUEST_FAILURE; + + // int success = QUEST_SUCCESS; + // Log(MSG_DEBUG, "Trigger Executed: %s [%i] hash %i", trigger->TriggerName, trigger->CheckNext, hash); + // if(Session->codedebug)GServer->SendPM(this, "Trigger executed %s[%i]", trigger->TriggerName, trigger->CheckNext); + // for (dword i = 0; i < trigger->ConditionCount; i++) + // { + // int command = trigger->Conditions[i]->opcode; + + // if (command > 30 || command < 0) continue; + // success = (*GServer->qstCondFunc[command])(GServer, this, trigger->Conditions[i]->data); + // //Log(MSG_DEBUG, "Condition %03u returned %d", command, success); + // if(Session->codedebug)GServer->SendPM(this, "Condition %03u returned %d", command, success); + // if (success == QUEST_FAILURE) + // { + // if (!trigger->CheckNext) + // return success; + // else return ExecuteQuestTrigger(nexttrigger->TriggerHash); + // } + // } + // for (dword i = 0; i < trigger->ActionCount; i++) + // { + // int command = trigger->Actions[i]->opcode; + // if(Session->codedebug)GServer->SendPM(this, "command %03u", command); + // if (command > 30 || command < 0) continue; + // if(Session->codedebug)GServer->SendPM(this, "Still good after command value check"); + // success = (*GServer->qstRewdFunc[command])(GServer, this, trigger->Actions[i]->data); + // if(Session->codedebug)GServer->SendPM(this, "reward %03u returned %d", command, success); + // //Log(MSG_DEBUG, "Reward %03u returned %d", command, success); + // } + // return success; +} + + +// HP/MP Regeneration Function +bool CPlayer::Regeneration() +{ + if (Stats->MaxHP == Stats->HP && Stats->MaxMP == Stats->MP) + { + lastRegenTime = 0; + return true; + } + + bool is_first_regen = false; + if (lastRegenTime == 0) + { + is_first_regen = true; + } + + clock_t etime = clock() - lastRegenTime; + if( etime >= 8 * CLOCKS_PER_SEC && Stats->HP > 0 ) + { + //Log(MSG_INFO,"Regeneration function. Character %i HP on entry %i", clientid, Stats->HP); + unsigned int hpamount = GetHPRegenAmount( ); + unsigned int mpamount = GetMPRegenAmount( ); + //if(stance = SITTING) + //{ + // hpamount = hpamount * 4; + // mpamount = mpamount * 4 + //} + Stats->HP += hpamount; + Stats->MP += mpamount; + if( Stats->HP > Stats->MaxHP) + Stats->HP = Stats->MaxHP; + if( Stats->MP > Stats->MaxMP ) + Stats->MP = Stats->MaxMP; + BEGINPACKET( pak, 0x7ec ); + ADDWORD( pak, Stats->HP); + ADDWORD( pak, Stats->MP ); + client->SendPacket( &pak ); + + if (Stats->MaxHP == Stats->HP && Stats->MaxMP == Stats->MP) + lastRegenTime = 0; + else + lastRegenTime = clock(); + //Log(MSG_INFO,"Regeneration function. Character %i HP on exit %i clock time %i", clientid, Stats->HP, lastRegenTime); + } + return true; +} + + +// Heal Player when use Food/Pots +bool CPlayer::PlayerHeal() +{ + clock_t transtime = clock() - UsedItem->lastRegTime; + if( UsedItem->usevalue != 0 && transtime >= 0.3 * CLOCKS_PER_SEC ) + { + if( UsedItem->used < UsedItem->usevalue && Stats->HP > 0 ) + { + int value = UsedItem->userate; + if((UsedItem->usevalue - UsedItem->used) < value) + { + value = UsedItem->usevalue - UsedItem->used; + } + switch( UsedItem->usetype ) + { + case 16: // HP + Stats->HP += value; + if(Stats->HP > Stats->MaxHP) + Stats->HP = Stats->MaxHP; + break; + case 17: // MP + Stats->MP += value; + if(Stats->MP > Stats->MaxMP) + Stats->MP = Stats->MaxMP; + break; + case 37: // Skill points + CharInfo->SkillPoints += value; + break; + case 76: //Stamina + CharInfo->stamina += value; + if(CharInfo->stamina > 5000) + CharInfo->stamina = 5000; + break; + default: + break; + } + UsedItem->used += value; + UsedItem->lastRegTime = clock(); + } + else + { + BEGINPACKET( pak,0x7b7 ); + ADDWORD ( pak, clientid ); + ADDDWORD ( pak, GServer->BuildBuffs( this ) ); + switch( UsedItem->usetype ) + { + case 16: // HP + ADDWORD( pak, Stats->HP ); + break; + case 17: // MP + ADDWORD( pak, Stats->MP ); + break; + case 37: //Skill Points + ADDWORD( pak, CharInfo->SkillPoints ); + break; + case 76: //Stamina + ADDWORD( pak, CharInfo->stamina ); + break; + default: + break; + } + GServer->SendToVisible( &pak, this ); + UsedItem->used = 0; + UsedItem->usevalue = 0; + UsedItem->userate = 0; + UsedItem->usetype = 0; + } + BEGINPACKET( pak, 0x7ec ); + ADDWORD( pak, Stats->HP); + ADDWORD( pak, Stats->MP ); + client->SendPacket( &pak ); + } + return true; +} + +void CPlayer::ReduceABC( ) +{ + unsigned int weapontype = 0; + weapontype = GServer->EquipList[WEAPON].Index[items[7].itemnum]->type; + switch(weapontype) + { + case 231: + items[132].count--; + if(items[132].count<=0) + { + ClearBattle( Battle ); + ClearItem( items[132] ); + } + break; + case 232: + items[133].count--; + if(items[133].count<=0) + { + ClearBattle( Battle ); + ClearItem( items[133] ); + } + break; + case 233: + items[134].count--; + if(items[134].count<=0) + { + ClearBattle( Battle ); + ClearItem( items[134] ); + } + break; + case 271: + items[132].count--; + if(items[132].count<=0) + { + ClearBattle( Battle ); + ClearItem( items[135] ); + } + break; + default: + break; + } +} + +SQuest* CPlayer::GetActiveQuest( ) +{ + for(dword i = 0; i < 10; i++){ + if(quest.quests[i].QuestID != ActiveQuest) continue; + return &quest.quests[i]; + } + return NULL; +} + +// return party pointer +CParty* CPlayer::GetParty( ) +{ + return Party->party; +} + +CLAN* CPlayer::GetClan( ) +{ + return Clan; +} +// return intelligence +unsigned int CPlayer::GetInt( ) +{ + return Attr->Int + Attr->Eint; + +} + +// add item [return item slot [0xffff if couldn't add it]] +unsigned int CPlayer::AddItem( CItem item ) +{ + unsigned int newslot = 0; + newslot = GetNewItemSlot( item ); + if(newslot != 0xffff) + { + if(items[newslot].count > 0) + { + unsigned int ntcount = item.count; + unsigned int utcount = items[newslot].count; + if(ntcount + utcount > 999) + { + item.count = ntcount + utcount - 999; + items[newslot].count = 999; + unsigned int otherslot = GetNewItemSlot( item ); + if(otherslot != 0xffff) + { + if(items[otherslot].count != 0) items[otherslot].count += item.count; + else items[otherslot] = item; + return newslot * 1000 + otherslot; + } + else + { + items[newslot].count = utcount; + return 0xffff; //not inventory space + } + } + else items[newslot].count = ntcount + utcount; + } + else items[newslot] = item; + } + return newslot; +} + +void CPlayer::UpdateInventory( unsigned int slot1, unsigned int slot2 ) +{ + + if(slot1 == 0xffff && slot2 == 0xffff) return; //neither slot is valid + BEGINPACKET( pak, 0x718 ); + if(slot1 != 0xffff && slot2 != 0xffff) {ADDBYTE( pak, 2 );} //both slots are valid + else {ADDBYTE( pak, 1 );} //one of the slots is valid + if(slot1 != 0xffff) + { + ADDBYTE ( pak, slot1); + ADDWORD ( pak, GServer->BuildItemHead( items[slot1] ) ); + ADDDWORD ( pak, GServer->BuildItemData( items[slot1] ) ); + } + if(slot2 != 0xffff) + { + ADDBYTE ( pak, slot2 ); + ADDWORD ( pak, GServer->BuildItemHead( items[slot2] ) ); + ADDDWORD ( pak, GServer->BuildItemData( items[slot2] ) ); + } + client->SendPacket( &pak ); + +} + +void CPlayer::takeSkillCost( CSkills* skill) { + for (int i = 0; i < 2; i++) + { + if (skill->costtype[i] == 0) continue; + Log(MSG_INFO, "Skill cost: %i", skill->costamount[i]); + switch (skill->costtype[i]) + { + case sHP: + Stats->HP -= skill->costamount[i]; + break; + case sMP: + Stats->MP -= (skill->costamount[i] - (skill->costamount[i] * Stats->MPReduction / 100)); + Log(MSG_INFO, "MP: %i", Stats->MP); + break; + case sMoney: + CharInfo->Zulies -= skill->costamount[i]; + break; + case sStamina: + CharInfo->stamina -= skill->costamount[i]; + break; + case sCartGauge: + Log(MSG_INFO, "Couldn't take CartGauge, no variable"); + break; + default: + break; + } + } +} diff --git a/Src/World Server/playerquest.cpp b/Src/World Server/playerquest.cpp new file mode 100644 index 0000000..c0d5e04 --- /dev/null +++ b/Src/World Server/playerquest.cpp @@ -0,0 +1,2 @@ +// This file is no longer needed, will remove from later versions when I get around to it - Drakia + diff --git a/Src/World Server/playerstats.cpp b/Src/World Server/playerstats.cpp new file mode 100644 index 0000000..b5e1811 --- /dev/null +++ b/Src/World Server/playerstats.cpp @@ -0,0 +1,1975 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "player.h" +#include "worldserver.h" + +// Return Dodge +unsigned int CPlayer::GetDodge( ) +{ + UINT Dodge = 0; + Dodge = (UINT)floor(( ((Stats->Level * 0.3) + ((Attr->Dex + Attr->Edex) * 1.9)) + 10 ) * 0.4); + UINT ClothDodge = 0; + + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if (i < 7) + ClothDodge += items[i].durability; + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_DODGE) + { + Dodge += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_DODGE) + { + Dodge += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_DODGE) + Dodge += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_DODGE) + Dodge += GServer->StatsList[items[i].stats].value[1]; + } + } + } + Dodge += (UINT)floor(ClothDodge*0.3); + + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sDodge ) + { + if( skill->value2[j] > 0 ) + Extra += Dodge * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + Dodge += Extra; + + if(Status->Dodge_up!=0xff) + Dodge += MagicStatus[Status->Dodge_up].Value; + if(Status->Dodge_down!=0xff) + Dodge -= MagicStatus[Status->Dodge_down].Value; + if(Dodge<30) + Dodge=30; + return Dodge; +} + +// Return Accury +unsigned int CPlayer::GetAccury( ) +{ + UINT Accury = 0; + if(items[7].count==0 ) + Accury = (UINT)floor( (((Attr->Con + Attr->Econ )+10)*0.5) + 15 ); + else + Accury = (UINT)floor( ((Attr->Con+Attr->Econ+10)*0.8) + ((GServer->EquipList[WEAPON].Index[items[7].itemnum]->quality*0.6) + (items[7].durability*0.8)) ); + UINT extra_refine[10] = { 0, 4, 7, 10, 14, 20, 26, 33, 40, 50}; + if( items[7].refine>0 ) + { + UINT refine = (UINT)floor((double)(items[7].refine/16)); + if(refine<10) + { + Accury += extra_refine[refine]; + } + } + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_ACCUR) + { + Accury += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_ACCUR) + { + Accury += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_ACCUR) + Accury += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_ACCUR) + Accury += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sAccuracy ) + { + if( skill->value2[j] > 0 ) + Extra += Accury * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + Accury += Extra; + + if(Status->Accury_up!=0xff) + Accury += MagicStatus[Status->Dodge_up].Value; + if(Status->Accury_down!=0xff) + Accury -= MagicStatus[Status->Accury_down].Value; + if(Accury<30) + Accury=30; + return Accury; +} + +// Return Critical +unsigned int CPlayer::GetCritical( ) +{ + UINT Critical = 0; + UINT weapontype = 1; + if(items[7].itemnum!=0 && items[7].count > 0) // weapon equipped + weapontype = GServer->EquipList[WEAPON].Index[items[7].itemnum]->type; + Critical = (UINT)floor(((Attr->Con + Attr->Econ + 20)*0.2) + Attr->Sen + Attr->Esen ); + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_CRITICAL) + { + Critical += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_CRITICAL) + { + Critical += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_CRITICAL) + Critical += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_CRITICAL) + Critical += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + //item specific crit boosts + for(int n=0;n<5;n++) + { + if( skill->weapon[n] == weapontype) // item defined in the skill + { + if( skill->value2[j] > 0 ) + Extra += Critical * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + } + Critical += Extra; + + if(Fairy)Critical = (unsigned int)floor(Critical*1.2); + if(Status->Critical_up!=0xff) + Critical += MagicStatus[Status->Critical_up].Value; + if(Status->Critical_down!=0xff) + Critical -= MagicStatus[Status->Critical_down].Value; + if(Critical<10) + Critical=10; + return Critical; +} + +// Return Magic Attack based on the weapon currently equipped +unsigned int CPlayer::GetMagicAttack( ) +{ + if(GServer->EquipList[WEAPON].Index[items[7].itemnum]->magicattack == 1) + return 1; + else + return 0; +} + + +// Return Magic Defense +unsigned int CPlayer::GetMagicDefense( ) +{ + UINT MagicDefense = 0; + UINT Extra = 0; + switch( CharInfo->Job ) + { + case 121:case 122: + case 221:case 222: + case 321:case 322: + case 421:case 422: + Extra += 20; + break; + default: + break; + } + MagicDefense += (UINT)floor(((Attr->Int + Attr->Eint + 5)*0.6) + ((Stats->Level + 15)*0.8)); + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if (i < 9 && items[i].itemtype != WEAPON) + MagicDefense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->magicresistence; + + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_MRESIST) + { + MagicDefense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_MRESIST) + { + MagicDefense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_MRESIST) + MagicDefense += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_MRESIST) + MagicDefense += GServer->StatsList[items[i].stats].value[1]; + } + } + } + + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMagicResistance ) + { + if( skill->value2[j] > 0 ) + Extra += MagicDefense * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + MagicDefense += Extra; + + if(Status->Magic_Defense_up!=0xff) + MagicDefense += MagicStatus[Status->Magic_Defense_up].Value; + if(Status->Magic_Defense_down!=0xff) + MagicDefense -= MagicStatus[Status->Magic_Defense_down].Value; + return MagicDefense; +} + + +// Return Attack +unsigned int CPlayer::GetAttackPower( ) +{ + UINT attack = 0; + UINT weapontype = 0; + UINT weaponatk = 0; + UINT Extra = 0; + UINT skilltype = 0; + switch(CharInfo->Job) + { + case 121:case 122: + case 221:case 222: + case 321:case 322: + case 421:case 422: + Extra += 30; + break; + default: + break; + } + if( items[7].itemnum!=0 && items[7].count > 0 && items[7].durability > 0 ) + { + UINT extra_refine[10] = {0, 7, 14, 21, 31, 41, 51, 65, 80 , 100}; + weaponatk = GServer->EquipList[WEAPON].Index[items[7].itemnum]->attackpower; + weapontype = GServer->EquipList[WEAPON].Index[items[7].itemnum]->type; + if( items[7].refine > 0 ) + { + UINT refine = (UINT)floor((double)(items[7].refine/16)); + if(refine>0 && refine<10) + { + weaponatk += (UINT)floor(extra_refine[refine] * 0.01 * weaponatk); + } + + } + if(Session->codedebug) + { + Log( MSG_INFO, "Base AP with weapon equipped (includes refines) %i",attack ); + } + } + else + { + attack += (UINT)floor( ((Attr->Dex + Attr->Edex) * 0.3) + ((Attr->Str + Attr->Estr) * 0.5) + (Stats->Level * 0.2)); + if(Session->codedebug) + { + Log( MSG_INFO, "AP unarmed %i",attack ); + } + } + switch( weapontype ) + { + case ONE_HAND_SWORD://one hand sword + { + attack += (UINT)floor( (Attr->Str + Attr->Estr) * 0.75 + Stats->Level * 0.2 + ((Attr->Str + Attr->Estr) * 0.05 + 29) * weaponatk * 0.03333334 ); + if(Session->codedebug) + { + Log( MSG_INFO, "AP from sword only is %i",attack ); + } + skilltype = 42; + } + break; + case ONE_HAND_BUNT_SWORD://one hand blunt sword + { + attack += (UINT)floor( (Attr->Str + Attr->Estr)*0.75 + Stats->Level*0.2 + ((Attr->Str + Attr->Estr)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 42; + } + break; + case TWO_HAND_SWORD://two hand sword + { + attack += (UINT)floor( (Attr->Str + Attr->Estr)*0.75 + Stats->Level*0.2 + ((Attr->Str + Attr->Estr)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 43; + } + break; + case SPEAR://spear + { + attack += (UINT)floor( (Attr->Str + Attr->Estr)*0.75 + Stats->Level*0.2 + ((Attr->Str + Attr->Estr)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 43; + } + break; + case TWO_HAND_AXE://two hand axe + { + attack += (UINT)floor( (Attr->Str + Attr->Estr)*0.75 + Stats->Level*0.2 + ((Attr->Str + Attr->Estr)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 43; + } + break; + case BOW://bow + { + float arrows = 0; + if(items[132].count>0) + arrows = GServer->NaturalList.Index[items[132].itemnum]->quality * 0.5; + attack += (UINT)floor((((Attr->Str + Attr->Estr) + Stats->Level) * 0.1) + ((Attr->Dex + Attr->Edex) * 0.04 + (Attr->Sen + Attr->Esen) * 0.03 + 29) * weaponatk * 0.03333334 + (Attr->Dex+Attr->Edex) * 0.52 + arrows); + if(Session->codedebug) + { + Log( MSG_INFO, "AP from Bow + Arrows only is %i",attack ); + } + skilltype = 44; //Bow attack power + } + break; + case GUN://gun + case DUAL_GUN://dual guns + { + float bullets = 0; + if(items[133].count>1) + bullets = GServer->NaturalList.Index[items[133].itemnum]->quality * 0.8; + attack += (UINT)floor( (Attr->Con + Attr->Econ)*0.47 + bullets + Stats->Level*0.1 + (Attr->Dex + Attr->Edex)*0.3 + ((Attr->Con + Attr->Econ)*0.04 + (Attr->Sen + Attr->Esen)*0.05 + 29) * weaponatk * 0.03333334); + skilltype = 45; + } + break; + case LAUNCHER://launcher + { + float cannons = 0; + if(items[134].count>0) + { + cannons = GServer->NaturalList.Index[items[134].itemnum]->quality; + } + attack += (UINT)floor( cannons*0.8 + Stats->Level*0.1 + (Attr->Str + Attr->Estr)*0.32 + (Attr->Con + Attr->Econ)*0.45 + ((Attr->Con + Attr->Econ)*0.04 + (Attr->Sen + Attr->Esen)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 45; + } + break; + case STAFF://staff + { + attack += (UINT)floor( Stats->Level*0.2 + ((Attr->Int + Attr->Eint)+(Attr->Str + Attr->Estr))*0.4 + ((Attr->Int + Attr-> Eint)*0.05 + 29) * weaponatk * 0.03333334 ); + skilltype = 46; + } + break; + case WAND://wand + { + attack += (UINT)floor( Stats->Level*0.2 + (Attr->Int + Attr->Eint)*0.6 + ( ((Attr->Sen + Attr->Esen)*0.1 +26) * 0.03703704 * weaponatk ) ); + skilltype = 46; + } + break; + case KATAR://katar + { + attack += (UINT)floor( Stats->Level*0.2 + (Attr->Str + Attr->Estr)*0.42 + (Attr->Dex + Attr->Edex)*0.55 + ((Attr->Dex + Attr->Edex)*0.05 + 20) * weaponatk * 0.0476190); + skilltype = 48; + } + break; + case DOUBLE_SWORD://double sword + { + attack += (UINT)floor( Stats->Level*0.2 + (Attr->Str + Attr->Estr)*0.63 + (Attr->Dex + Attr->Edex)*0.45 + ((Attr->Dex + Attr->Edex)*0.05 + 25) * weaponatk * 0.03846154); + skilltype = 48; + } + break; + case CROSSBOW://crossbow + { + float arrows = 0; + if(items[132].count>0) + arrows = GServer->NaturalList.Index[items[132].itemnum]->quality*0.5; + attack += (UINT)floor((((Attr->Str + Attr->Estr)+Stats->Level)*0.1) + ((Attr->Dex + Attr->Edex)*0.04 + (Attr->Sen +Attr->Esen)*0.03 + 29) * weaponatk * 0.03333334 + (Attr->Dex + Attr->Edex)*0.52 + arrows); + skilltype = 47; //Crossbow Attack Power + } + break; + default: + break; + } + + for(UINT i=1;i<12;i++)//cloth stats + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == 18) + { + attack += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == 18) + { + attack += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == 18) + attack += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == 18) + attack += GServer->StatsList[items[i].stats].value[1]; + } + } + } + if(Session->codedebug) + { + Log( MSG_INFO, "AP after cloth stats %i",attack ); + } + + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + //weapon specific AP boosts + for(int n=0;n<5;n++) + { + if(weapontype == 0) //no weapon equipped + { + if( skill->buff[j] == 41) //unarmed AP + { + if( skill->value2[j] > 0 ) + Extra += attack * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + else // some kind of weapon equipped + { + if( skill->weapon[n] == weapontype || skill->buff[j] == 18) + { + int xt1 = 0; + int xt2 = 0; + if(skill->buff[j] == skilltype) + { + xt1 = skill->value1[j]; + xt2 = attack * skill->value2[j] / 100; + if(xt1 > xt2) + Extra = xt1; + else + Extra = xt2; + //Extra += attack * skill->value2[j] / 100; + if(Session->codedebug) + { + Log( MSG_INFO, "Extra AP from passive %i",Extra ); + } + } + } + } + } + } + } + attack += Extra; + + if(Session->codedebug) + { + Log( MSG_INFO, "AP final value before fairy and buff check is %i",attack ); + } + if(Fairy)attack = (unsigned int)floor(attack*1.2); + if(Status->Attack_up!=0xff) + attack += MagicStatus[Status->Attack_up].Value; + if(Status->Attack_down!=0xff) + attack -= MagicStatus[Status->Attack_down].Value; + //if(attack<30) + // attack = 30; + return attack; +} + +// Return Defense +unsigned int CPlayer::GetDefense( ) +{ + UINT defense = 0; + UINT Extra = 0; + switch( CharInfo->Job ) + { + case 121:case 122: + case 221:case 222: + case 321:case 322: + case 421:case 422: + Extra += 25; + break; + default: + break; + } + defense += (UINT)floor( (( Attr->Estr + Attr->Str + 5) * 0.35) + ((Stats->Level + 15) * 0.7) ); + UINT extra_refine[10] = {0, 6, 12, 18, 27, 36, 45, 57, 70 , 85}; + for(UINT i=1;i<9;i++) + { + if(i==7) + continue; + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + defense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->defense; + if(items[i].refine>0) + { + UINT refine = (UINT)floor((double)(items[i].refine/16)); + if(refine<10) + { + defense += (UINT)floor(extra_refine[refine] * 0.01 * GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->defense); + } + } + } + } + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_DEFENSE) + { + defense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_DEFENSE) + { + defense += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_DEFENSE) + defense += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_DEFENSE) + defense += GServer->StatsList[items[i].stats].value[1]; + } + } + } + // Additional def due to pskills + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sDefense || skill->buff[j] == sDefense2 ) + { + if( skill->value2[j] > 0 ) + Extra += defense * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + //Shield specific def boosts. previously not included. + for(int n=0;n<5;n++) + { + if( skill->weapon[n] == items[8].type && items[8].count > 0) // item defined in the skill is currently equipped in the shield slot + { + if( skill->value2[j] > 0 ) + Extra += defense * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + // this is BS sShield = 35 and that is "Shield Damage" not defense + //if (items[8].count > 0 && skill->buff[j] == sShield) + //{ + // if( skill->value2[j] > 0 ) + // Extra += defense * skill->value2[j] / 100; + // if( skill->value1[j] > 0 ) + // Extra += skill->value1[j]; + //} + } + } + defense += Extra; + if(Fairy) defense = (unsigned int)floor(defense*1.2); + if(Status->Defense_up!=0xff) + defense += MagicStatus[Status->Defense_up].Value; + if(Status->Defense_down!=0xff) + defense -= MagicStatus[Status->Defense_down].Value; + return defense; +} + +// Return Attack Speed +unsigned int CPlayer::GetAttackSpeed( ) +{ + UINT aspeed = 0; + if( items[7].itemnum!=0 && items[7].count > 0 ) + { + UINT wpnspd = 0; + wpnspd = GServer->EquipList[WEAPON].Index[items[7].itemnum]->attackspeed; + switch(wpnspd) + { + case 12: //normal + aspeed = 88; + break; + case 11: //+1 + aspeed = 93; + break; + case 10: //+2 + aspeed = 100; + break; + case 9: //+3 + aspeed = 107; + break; + case 8: //+4 + aspeed = 115; + break; + case 7: //+5 + aspeed = 125; + break; + case 6: //+6 + aspeed = 136; + break; + default: + break; + } + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Weapon speed %i. Attack speed so far %i",wpnspd,aspeed); + } + } + else + { + aspeed = 115; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Attack speed with no weapon %i",aspeed); + } + } + + UINT weapontype = 0; + UINT skilltype = 0; + weapontype = GServer->EquipList[WEAPON].Index[items[7].itemnum]->type; + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + bool GotWeapon = false; //do I have one of the listed weapons? + bool GotRightWeapon = false; //Do i have the right one though? + if( skill->weapon[0] == 0) + { + GotWeapon = true; + } + else + { + for(int n=0;n<5;n++) + { + if( skill->weapon[n] != 0 && skill->weapon[n] == weapontype) // item defined in the skill + { + GotWeapon = true; + } + } + } + if(GotWeapon) //No point checking if we have the RIGHT weapon when we don't have any of them + { + for(UINT j = 0; j < 2; j++ ) + { + GotRightWeapon = false; + switch(skill->buff[j]) + { + case 24: //attackspeed + GotRightWeapon = true; //true for all weapons listed + break; + case 49: //bow speed + if(weapontype == 231 || weapontype == 271) + GotRightWeapon = true; //true if we have a bow or crossbow + break; + case 50: //gun speed + if(weapontype == 232 || weapontype == 253) + GotRightWeapon = true; //true if we have a gun or dual guns + break; + case 51: //combat weapon speed + if(weapontype == 251 || weapontype == 252) + GotRightWeapon = true; //true if we have a katar or dual swords + break; + case 118: //one hand sword attack speed + if(weapontype == 211) + GotRightWeapon = true; //true if we have a one hand sword + break; + case 119: //one hand melee weapon attack speed + if(weapontype == 212) + GotRightWeapon = true; //true if we have a one hand melee weapon + break; + case 120: //two hand sword attack speed + if(weapontype == 221) + GotRightWeapon = true; //true if we have a two hand sword + break; + case 121: //spear attack speed + if(weapontype == 222) + GotRightWeapon = true; //true if we have a spear + break; + case 122: //two hand axe attack speed + if(weapontype == 223) + GotRightWeapon = true; //true if we have an axe + break; + default: + break; + } + if(GotRightWeapon) + { + if( skill->value2[j] > 0 ) + Extra += aspeed * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Attack speed %i added for weapon type %i. Skill ID %i",Extra,weapontype,skill->id); + } + } + } + } + } + aspeed += Extra; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Attack speed after passives %i",aspeed); + } + + //active buffs + if(Status->Haste_up!=0xff) + aspeed += MagicStatus[Status->Haste_up].Value; + if(Status->Haste_down!=0xff) + aspeed -= MagicStatus[Status->Haste_down].Value; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Attack speed after buffs %i",aspeed); + } + + //cloth stats + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_HASTE) + { + aspeed += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_HASTE) + { + aspeed += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + //don't need gems. Covered by the stats section + if(items[i].stats>0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_HASTE) + aspeed += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_HASTE) + aspeed += GServer->StatsList[items[i].stats].value[1]; + } + } + } + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Attack speed after cloth stats and gems %i",aspeed); + } + return aspeed; +} + +// Return Movement Speed +unsigned int CPlayer::GetMoveSpeed( ) +{ + UINT mspeed = 425; + if(!Status->CanRun) + Status->Stance = WALKING; + //mspeed += UINT((Attr->Dex)*0.85); + switch(Status->Stance) + { + case WALKING: //walking + mspeed = 200; + break; + case 1: + case RUNNING: // running + { + if( items[6].count != 0 )//Foot + { + UINT tspeed = GServer->EquipList[SHOE].Index[items[6].itemnum]->movespeed; + mspeed += ( tspeed - 65 ) * 5; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed with shoes on %i",mspeed); + } + } + for(UINT i=0;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype > 9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == MOVE_SPEED) + { + mspeed += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == MOVE_SPEED) + { + mspeed += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %i after equipment stats for item in slot %i",mspeed,i); + } + } + if(items[i].stats > 0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_DASH) + { + mspeed += GServer->StatsList[items[i].stats].value[0]; + } + if(GServer->StatsList[items[i].stats].stat[1] == A_DASH) + { + mspeed += GServer->StatsList[items[i].stats].value[1]; + } + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %i after adding extra/gem stats for item in slot %i",mspeed,i); + } + } + } + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %i after adding all equipped items",mspeed); + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMoveSpeed || skill->buff[j] == sMoveSpeed2 ) + { + if( skill->value2[j] > 0 ) + Extra += mspeed * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + mspeed += Extra; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %i after adding passive skills",mspeed); + } + Stats->Mspd_base = mspeed; + if(Fairy) + { + mspeed += 110; + if(Session->codedebug) //Debug code + { + Log( MSG_INFO, "Fairy detected. move speed + 110 %i", mspeed); + } + } + if(Status->Dash_up!=0xff) + { + mspeed += MagicStatus[Status->Dash_up].Value; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Speed buff detected. amount: %i new move speed: %i",MagicStatus[Status->Dash_up].Value, mspeed); + } + } + if(Status->Dash_down!=0xff) + { + mspeed -= MagicStatus[Status->Dash_down].Value; + + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Speed DE-buff detected. amount: %i new move speed: %i",MagicStatus[Status->Dash_down].Value, mspeed); + } + } + + } + break; + case DRIVING: //cart or gear + { + //Log(MSG_INFO, "player is driving"); + float m_speed = 0; + int patType = GServer->PatList.Index[items[135].itemnum]->type; + //if(this->items[135].itemnum > 30 && this->items[135].itemnum < 40 ) //Castle gear body + if(patType == 512) //Castle gear body + { + m_speed = 1000; + float mod1 = (float)GServer->PatList.Index[items[136].itemnum]->speed; + mod1 /= 100; //engine + float mod2 = (float)GServer->PatList.Index[items[137].itemnum]->speed; + mod2 /= 100; // legs + m_speed *= mod1; + m_speed *= mod2; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %i riding a Castle Gear",m_speed); + } + } + else if(patType == 511) //Cart body + { + m_speed = 1000; + float mod1 = (float)GServer->PatList.Index[items[136].itemnum]->speed; + mod1 /= 100; //engine + float mod2 = (float)GServer->PatList.Index[items[137].itemnum]->speed; + mod2 /= 100; //wheels + m_speed *= mod1; + m_speed *= mod2; + // Many accessory items give a speed boost too. Added in after the modifiers + if(GServer->PatList.Index[items[138].itemnum]->condition[0] == 23) // accessory + m_speed += (float)GServer->PatList.Index[items[138].itemnum]->modifier[0]; + if(GServer->PatList.Index[items[138].itemnum]->condition[1] == 23) // accessory + m_speed += (float)GServer->PatList.Index[items[138].itemnum]->modifier[1]; + // some frames give a speed boost too. Added in after the modifiers + if(GServer->PatList.Index[items[135].itemnum]->condition[0] == 23) // frame + m_speed += (float)GServer->PatList.Index[items[135].itemnum]->modifier[0]; + if(GServer->PatList.Index[items[135].itemnum]->condition[1] == 23) // frame + m_speed += (float)GServer->PatList.Index[items[135].itemnum]->modifier[1]; + // some engines give a speed boost too. Added in after the modifiers + if(GServer->PatList.Index[items[136].itemnum]->condition[0] == 23) // engine + m_speed += (float)GServer->PatList.Index[items[135].itemnum]->modifier[0]; + if(GServer->PatList.Index[items[136].itemnum]->condition[1] == 23) // engine + m_speed += (float)GServer->PatList.Index[items[135].itemnum]->modifier[1]; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Move speed %f riding a Cart",m_speed); + } + } + else + { + Log(MSG_ERROR,"What the heck is this character driving? it is type %i", patType); + } + mspeed = (int)floor(m_speed); + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Final Speed before fairy buffs added: %i", mspeed); + } + Stats->Mspd_base = mspeed; + if(Fairy) + { + //mspeed = (unsigned int)floor(mspeed * 1.2); + mspeed += 110; + if(Session->codedebug) //Debug code + { + GServer->SendPM(this, "Fairy detected. move speed * 1.2 %i", mspeed); + } + } + } + break; + default: + break; + } + return mspeed; +} + +// Return Max HP +unsigned int CPlayer::GetMaxHP( ) +{ + if (Stats->Level<1) Stats->Level=1; + //unsigned int hpmax = (unsigned int)floor(((sqrt(Stats->Level + 20) * (Stats->Level + 5 )) * 3.5) + ((Attr->Str + Attr->Estr) << 1)); + //unsigned int hpmax1 = (unsigned int)floor((0.1555 * Stats->Level * Stats->Level) + (13.812 * Stats->Level) + 45 + ((Attr->Str + Attr->Estr) * 2)); + + // Base stats for Visitor + float multi = 2.36; + int add1 = 26; + int add2 = 4; + // Now change them based on class + switch (CharInfo->Job) { + case jSoldier: + multi = 3.5; + add1 = 20; + add2 = 5; + break; + case jKnight: + multi = 3.5; + add1 = 28; + add2 = 5; + break; + case jChampion: + multi = 3.5; + add1 = 22; + add2 = 5; + break; + + case jMuse: + multi = 2.36; + add1 = 26; + add2 = 4; + break; + case jMage: + multi = 2.37; + add1 = 26; + add2 = 5; + break; + case jCleric: + multi = 2.4; + add1 = 26; + add2 = 7; + break; + + + case jHawker: + multi = 2.7; + add1 = 20; + add2 = 5; + break; + case jRaider: + multi = 3.0; + add1 = 23; + add2 = 5; + break; + case jScout: + multi = 2.7; + add1 = 21; + add2 = 5; + break; + + case jDealer: + case jBourgeois: + case jArtisan: + multi = 2.7; + add1 = 20; + add2 = 5; + break; + default: + break; + } + // Max HP calc from client + UINT Extra = 0; + float ModLevel = add1 + Stats->Level; + UINT hpmax = (UINT)floor((( sqrt(ModLevel) * (add2 + Stats->Level) ) * multi) + ((Attr->Str + Attr->Estr) << 1) ); + // Stat boost for second jobs + switch( CharInfo->Job ) + { + case 121:case 122: + case 221:case 222: + case 321:case 322: + case 421:case 422: + Extra += 300; + break; + default: + break; + } + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_MAX_HP || GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_HP ) + { + hpmax += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_MAX_HP || GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_HP ) + { + hpmax += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_MAX_HP || GServer->StatsList[items[i].stats].stat[0] == A_HP) + hpmax += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_MAX_HP || GServer->StatsList[items[i].stats].stat[0] == A_HP) + hpmax += GServer->StatsList[items[i].stats].value[1]; + } + } + } + + // Additional hp due to pskills + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMaxHP || skill->buff[j] == sMaxHP2 ) + { + if( skill->value2[j] > 0 ) + Extra += hpmax * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + hpmax += Extra; + if(Fairy) hpmax = (unsigned int)floor(hpmax*1.2); + if(Status->HP_up!=0xff) + hpmax += MagicStatus[Status->HP_up].Value; + if(Status->HP_down!=0xff) + hpmax -= MagicStatus[Status->HP_down].Value; + return hpmax; +} +// Return Max MP [fixed by Tomiz] TODO: add cloth stat +unsigned int CPlayer::GetMaxMP( ) +{ + + if( Stats->Level < 0 ) + Stats->Level = 1; + UINT maxmp = 0; + float Mult = 0; + int JobValue = (int)CharInfo->Job; + switch(JobValue) + { + case 111: + case 411: + case 421: + Mult = 4; + JobValue = 3; + break; + case 122: + Mult = 5; + JobValue = -6; + break; + case 211: + Mult = 6; + JobValue = 0; + break; + case 221: + Mult = 7; + JobValue = -7; + break; + case 222: + Mult = 6.5; + JobValue = -4; + break; + case 311: + case 321: + Mult = 4; + JobValue = 4; //LMA + break; + case 121: + case 322: + case 422: + Mult = 4.5; + JobValue = 0; + break; + default: + Mult = 3; + JobValue = 4; + break; + } + maxmp = (UINT)floor((Stats->Level + JobValue) * Mult + ((Attr->Int + Attr->Eint) << 2)); + + + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_MAX_MP || GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_MP ) + { + maxmp += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_MAX_MP || GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_MP ) + { + maxmp += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_MAX_MP || GServer->StatsList[items[i].stats].stat[0] == A_MP) + maxmp += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_MAX_MP || GServer->StatsList[items[i].stats].stat[0] == A_MP) + maxmp += GServer->StatsList[items[i].stats].value[1]; + } + } + } + + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMaxMP || skill->buff[j] == sMaxMP2 ) + { + if( skill->value2[j] > 0 ) + Extra += maxmp * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + maxmp += Extra; + + if(Fairy) maxmp = (unsigned int)floor(maxmp*1.2); + if(Status->MP_up!=0xff) + maxmp += MagicStatus[Status->MP_up].Value; + if(Status->MP_down!=0xff) + maxmp -= MagicStatus[Status->MP_down].Value; + + return maxmp; +} + +// Get HP Regeneration Amount +unsigned int CPlayer::GetHPRegenAmount( ) +{ + UINT amount = (UINT)ceil ( Stats->MaxHP * 0.02 ); + if( Status->Stance == 1 ) + amount *= 4; + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_HP_REC_RATE) + { + amount += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_HP_REC_RATE) + { + amount += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_HP_REC_RATE ) + amount += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_HP_REC_RATE ) + amount += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sHPRate || skill->buff[j] == sHPRecovery ) // Not sure on sHPRate + { + if( skill->value2[j] > 0 ) + Extra += amount * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + amount += Extra; + + return amount; +} + +// Get MP Regeneration Amount +unsigned int CPlayer::GetMPRegenAmount( ) +{ + UINT amount = (UINT)ceil ( Stats->MaxMP * 0.02 ); + if( Status->Stance == 1 ) + amount *= 4; + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_MP_REC_RATE) + { + amount += amount * GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1] / 100; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_MP_REC_RATE) + { + amount += amount * GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1] / 100; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_MP_REC_RATE ) + amount += amount * GServer->StatsList[items[i].stats].value[0] / 100; + if(GServer->StatsList[items[i].stats].stat[1] == A_MP_REC_RATE ) + amount += amount * GServer->StatsList[items[i].stats].value[1] / 100; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMPRate || skill->buff[j] == sMPRecovery ) + { + if( skill->value2[j] > 0 ) + Extra += amount * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + amount += Extra; + return amount; +} + +// max weight supported +unsigned int CPlayer::GetMaxWeight( ) +{ + UINT weight = 0; + //weight = ((Attr->Str + Attr->Estr) + (Attr->Str + Attr->Estr)*2) * 2 + Stats->Level+220 + (Stats->Level+220)*4; + weight = ((Attr->Str + Attr->Estr)*6)+((Stats->Level+220)*5); + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == A_INVENTORY_CAPACITY) + { + weight += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == A_INVENTORY_CAPACITY) + { + weight += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == A_INVENTORY_CAPACITY ) + weight += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == A_INVENTORY_CAPACITY ) + weight += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMaxWeight || skill->buff[j] == sMaxWeight2 ) + { + if( skill->value2[j] > 0 ) + Extra += weight * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + + weight += Extra; + return weight; +} + +// Current weight +unsigned int CPlayer::GetCurrentWeight( ) +{ + UINT weight = 0; + for(UINT i=0;i 0) + { + weight += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->weight; + } + else + { + switch(items[i].itemtype) + { + case CONSUMIBLE: + weight += GServer->UseList.Index[items[i].itemnum]->weight*items[i].count; + break; + case JEM: + weight += GServer->JemList.Index[items[i].itemnum]->weight*items[i].count; + break; + case NATURAL: + weight += GServer->NaturalList.Index[items[i].itemnum]->weight*items[i].count; + break; + case PAT: + weight += GServer->PatList.Index[items[i].itemnum]->weight; + break; + default: + break; + } + } + } + return weight; +} + +// MP Reduction +unsigned int CPlayer::GetMPReduction( ) +{ + UINT mpreduction = 0; + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if(items[i].count>0) + { + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == MP_COST_RED) + { + mpreduction += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == MP_COST_RED) + { + mpreduction += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == MP_COST_RED ) + mpreduction += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == MP_COST_RED ) + mpreduction += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sMPSpendDecrease ) + { + if( skill->value2[j] > 0 ) + Extra += mpreduction * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + mpreduction += Extra; + return mpreduction; +} + +// Max summon capacity +unsigned int CPlayer::GetMaxSummonGauge( ) +{ + UINT gauge = 50; + for(UINT i=1;i<12;i++)//cloth stats [from tomiz] + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if(items[i].count>0) + { + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == SUMMON_GAUGE) + { + gauge += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == SUMMON_GAUGE) + { + gauge += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + } + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == MP_COST_RED ) + gauge += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == MP_COST_RED ) + gauge += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sExpandSummonGauge ) + { + if( skill->value2[j] > 0 ) + Extra += gauge * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + gauge += Extra; + + return gauge; +} + +// Extra attributes [str/dex/con/cha/sen/int] +void CPlayer::GetExtraStats( ) +{ + Attr->Estr = 0; + Attr->Edex = 0; + Attr->Eint = 0; + Attr->Econ = 0; + Attr->Esen = 0; + Attr->Echa = 0; + for(UINT i=1;i<12;i++)//cloth stats + { + if( items[i].count > 0 ) + { + UINT Stat1 = GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0]; + UINT Stat2 = GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0]; + switch(Stat1) + { + case A_STR: Attr->Estr += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + case A_DEX: Attr->Edex += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + case A_INT: Attr->Eint += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + case A_CON: Attr->Econ += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + case A_CHA: Attr->Echa += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + case A_SEN: Attr->Esen += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; break; + default: + break; + } + switch(Stat2) + { + case A_STR: Attr->Estr += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + case A_DEX: Attr->Edex += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + case A_INT: Attr->Eint += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + case A_CON: Attr->Econ += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + case A_CHA: Attr->Echa += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + case A_SEN: Attr->Esen += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; break; + default: + break; + } + // Why are we doing jems and stats seperately? they are the same thing O.o + /*if(items[i].gem != 0) // jem stats + { + //somehow a player had a negative value in his gem on an item. Put this code in to patch it. + if(items[i].gem < 0 || items[i].stats < 0) + { + items[i].gem = 0; + items[i].stats = 0; + } + + UINT Stat1 = GServer->JemList.Index[items[i].gem]!=NULL?GServer->JemList.Index[items[i].gem]->stat1[0]:0; + UINT Stat2 = GServer->JemList.Index[items[i].gem]!=NULL?GServer->JemList.Index[items[i].gem]->stat2[0]:0; + switch(Stat1) + { + case STRENGTH: Attr->Estr += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + case DEXTERY: Attr->Edex += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + case INTELIGENCE: Attr->Eint += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + case CONCENTRATION: Attr->Econ += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + case CHARM: Attr->Echa += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + case SENSE: Attr->Esen += GServer->JemList.Index[items[i].gem]->stat1[1]; break; + } + switch(Stat2) + { + case STRENGTH: Attr->Estr += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + case DEXTERY: Attr->Edex += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + case INTELIGENCE: Attr->Eint += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + case CONCENTRATION: Attr->Econ += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + case CHARM: Attr->Echa += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + case SENSE: Attr->Esen += GServer->JemList.Index[items[i].gem]->stat2[1]; break; + } + } */ + if(items[i].stats != 0 && items[i].stats < 500) // this should cover all the gems too + { + Stat1 = GServer->StatsList[items[i].stats].stat[0]; + Stat2 = GServer->StatsList[items[i].stats].stat[1]; + switch(Stat1) + { + case STRENGTH: Attr->Estr += GServer->StatsList[items[i].stats].value[0]; break; + case DEXTERY: Attr->Edex += GServer->StatsList[items[i].stats].value[0]; break; + case INTELIGENCE: Attr->Eint += GServer->StatsList[items[i].stats].value[0]; break; + case CONCENTRATION: Attr->Econ += GServer->StatsList[items[i].stats].value[0]; break; + case CHARM: Attr->Echa += GServer->StatsList[items[i].stats].value[0]; break; + case SENSE: Attr->Esen += GServer->StatsList[items[i].stats].value[0]; break; + default: + break; + } + switch(Stat2) + { + case STRENGTH: Attr->Estr += GServer->StatsList[items[i].stats].value[1]; break; + case DEXTERY: Attr->Edex += GServer->StatsList[items[i].stats].value[1]; break; + case INTELIGENCE: Attr->Eint += GServer->StatsList[items[i].stats].value[1]; break; + case CONCENTRATION: Attr->Econ += GServer->StatsList[items[i].stats].value[1]; break; + case CHARM: Attr->Echa += GServer->StatsList[items[i].stats].value[1]; break; + case SENSE: Attr->Esen += GServer->StatsList[items[i].stats].value[1]; break; + default: + break; + } + } + } + } +} + +float CPlayer::GetAttackDistance( ) +{ + if(items[7].count==0) return (float)1; + float AttackDistance = 0; + AttackDistance = GServer->EquipList[WEAPON].Index[items[7].itemnum]->attackdistance/100; + return AttackDistance; +} + +// Return XPRate +unsigned int CPlayer::GetXPRate( ) +{ + UINT XPRate = 0; + if(Stats->ItemXPRate == -1) + return 0; // sends back a rate of 0. Used for exp nulification + + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype>9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == XP_RATE) + XPRate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == XP_RATE) + XPRate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + if(items[i].stats>0 && items[i].stats<500) + { + if(GServer->StatsList[items[i].stats].stat[0] == XP_RATE) + XPRate += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == XP_RATE) + XPRate += GServer->StatsList[items[i].stats].value[1]; + } + } + } + if(Stats->ItemXPRate != 0 && Stats->ItemXPRate != 1 && Stats->ItemXPRate != 2) + Stats->ItemXPRate = 0; + XPRate += Stats->ItemXPRate; // add timed XPRate buffs + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == sEXPRate ) + { + if( skill->value2[j] > 0 ) + Extra += XPRate * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + CMap* map = GServer->MapList.Index[Position->Map]; + + XPRate += Extra; + + if(XPRate < 1) + XPRate = 1; + XPRate = XPRate * map->mapXPRate; + //Log( MSG_INFO, "XP rate calculated as = %i", XPRate); + Log( MSG_INFO, "XPRate: %i Map XP rate: %i for map %i",XPRate,map->mapXPRate, map->id); + return XPRate; +} + +// Return Item Drop Rate +unsigned int CPlayer::GetItemDropRate( ) +{ + int itemdroprate = 0; + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype > 9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == 79 ) //ITEM_DROP_RATE + itemdroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == 79 ) //ITEM_DROP_RATE + itemdroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + if(items[i].stats > 0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == 79 ) //ITEM_DROP_RATE + itemdroprate += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == 79 ) //ITEM_DROP_RATE + itemdroprate += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == 79 ) //sItemDropIncrease + { + if( skill->value2[j] > 0 ) + Extra += itemdroprate * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + itemdroprate += Extra; + //Log( MSG_INFO, "item drop rate calculated as = %i", itemdroprate); + if(itemdroprate < 0) + itemdroprate = 0; + //itemdroprate += GServer->Config.DROP_RATE; + //Log( MSG_INFO, "item drop rate calculated as = %i", itemdroprate); + //Log( MSG_INFO, "Server item drop rate = %i", GServer->Config.DROP_RATE); + return itemdroprate; +} + +// Return Item Drop count increase +unsigned int CPlayer::GetItemDropCountRate( ) +{ + int itemdroprate = 0; + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype > 9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == 63 ) //ITEM_DROP_Count RATE + itemdroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == 63 ) //ITEM_DROP_Coiunt RATE + itemdroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + if(items[i].stats > 0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == 63 ) //ITEM_DROP_Count RATE + itemdroprate += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == 63 ) //ITEM_DROP_Count RATE + itemdroprate += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == 63 ) //Item drop Count rate + { + if( skill->value2[j] > 0 ) + Extra += itemdroprate * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + itemdroprate += Extra; + //Log( MSG_INFO, "item drop rate calculated as = %i", itemdroprate); + if(itemdroprate < 0) + itemdroprate = 0; + //itemdroprate += GServer->Config.DROP_RATE; + //Log( MSG_INFO, "item drop rate calculated as = %i", itemdroprate); + //Log( MSG_INFO, "Server item drop rate = %i", GServer->Config.DROP_RATE); + return itemdroprate; +} + +// Return Zuly Drop Rate +unsigned int CPlayer::GetZulyDropRate( ) +{ + int zulydroprate = 1; + for(UINT i=1;i<12;i++) + { + if( items[i].count != 0 ) + { + if(items[i].itemtype > 9) + { + Log(MSG_WARNING, "Char %s have equip invalid item: %i,%i", CharInfo->charname, items[i].itemtype, items[i].itemnum ); + continue; + } + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[0] == 64 ) //ZULY_DROP_RATE + zulydroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat1[1]; + if( GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[0] == 64 ) //ZULY_DROP_RATE + zulydroprate += GServer->EquipList[items[i].itemtype].Index[items[i].itemnum]->stat2[1]; + if(items[i].stats > 0 && items[i].stats < 500) + { + if(GServer->StatsList[items[i].stats].stat[0] == 64 ) //ZULY_DROP_RATE + zulydroprate += GServer->StatsList[items[i].stats].value[0]; + if(GServer->StatsList[items[i].stats].stat[1] == 64 ) //ZULY_DROP_RATE + zulydroprate += GServer->StatsList[items[i].stats].value[1]; + } + } + } + UINT Extra = 0; + for(UINT i=0;i<30;i++) + { + if (pskill[i] == 0) + continue; + CSkills* skill = GServer->GetSkillByID( (pskill[i])+(pskilllvl[i])); + if( skill == NULL ) + continue; + for(UINT j = 0; j < 2; j++ ) + { + if( skill->buff[j] == 64 ) //sItemZulyIncrease + { + if( skill->value2[j] > 0 ) + Extra += zulydroprate * skill->value2[j] / 100; + if( skill->value1[j] > 0 ) + Extra += skill->value1[j]; + } + } + } + zulydroprate += Extra; + if(zulydroprate < 1) + zulydroprate = 1; + //Log( MSG_INFO, "zuly drop rate calculated as = %i", zulydroprate); + return zulydroprate; +} + + + + +// calculate player stats +void CPlayer::SetStats( ) +{ + GetExtraStats( ); + Stats->MaxHP = GetMaxHP( ); + Stats->MaxMP = GetMaxMP( ); + Stats->Attack_Power = GetAttackPower( ); + Stats->Defense = GetDefense( ); + Stats->Critical = GetCritical( ); + Stats->Move_Speed = GetMoveSpeed( ); + //Stats->Mspd_base = GetMoveSpeed(1); + Stats->Magic_Defense = GetMagicDefense( ); + Stats->magicattack = GetMagicAttack( ); + Stats->Accury = GetAccury( ); + Stats->Dodge = GetDodge( ); + Stats->Attack_Speed = GetAttackSpeed( ); + Stats->Attack_Distance = GetAttackDistance( ); + Stats->MaxWeight = GetMaxWeight( ); + Stats->MaxSummonGauge = GetMaxSummonGauge( ); + Stats->MPReduction = GetMPReduction( ); + Stats->xprate = GetXPRate( ); + Stats->itemdroprate = GetItemDropRate( ); + Stats->itemdropcountrate = GetItemDropCountRate( ); + Stats->zulydroprate = GetZulyDropRate( ); +} + diff --git a/Src/World Server/quest.cpp b/Src/World Server/quest.cpp new file mode 100644 index 0000000..a68f313 --- /dev/null +++ b/Src/World Server/quest.cpp @@ -0,0 +1,125 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +/*bool CWorldServer::DoQuestScript( CPlayer* thisclient, CQuest* thisquest ) +{ + BEGINPACKET( pak, 0 ); + switch( thisquest->script ) + { + case 1: //Teleport + { + if(thisquest->value3>0) + { + thisclient->CharInfo->Zulies -= thisquest->value3; + RESETPACKET( pak, 0x71e ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + } + fPoint thispoint; + thispoint.x = floor( thisquest->value2/10000 ); + thispoint.y = floor( thisquest->value2%10000 ); + TeleportTo ( thisclient, thisquest->value1, thispoint ); + thisclient->EndQuest( thisquest->questid ); + } + break; + case 2: // Change Job + { + if( thisclient->CharInfo->Job == 0 ) + { + thisclient->CharInfo->Job = thisquest->value1; + RESETPACKET(pak, 0x721 ); + ADDWORD (pak, 0x0004 ); + ADDWORD (pak, thisclient->CharInfo->Job ); + ADDWORD (pak, 0x0000 ); + thisclient->client->SendPacket(&pak); + } + } + break; + case 20: // 2nd Change Job + { //Credits to tomiz + if( thisclient->CharInfo->Job > 0 ) + { + thisclient->CharInfo->Job = thisquest->value1; + RESETPACKET(pak, 0x721 ); + ADDWORD (pak, 0x0004 ); + ADDWORD (pak, thisclient->CharInfo->Job ); + ADDWORD (pak, 0x0000 ); + thisclient->client->SendPacket(&pak); + } + if(thisquest->value3>0) // zuly need for second job + { + thisclient->CharInfo->Zulies-= thisquest->value3; + RESETPACKET( pak, 0x71e ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + } + } + break; + case 3: //Give Stats/Skill points + { + switch( thisquest->value1 ) + { + case 1: // Give Stats points + thisclient->CharInfo->StatPoints += thisquest->value2; + RESETPACKET( pak, 0x720 ); + ADDWORD ( pak, 0x0020 ); + ADDDWORD ( pak, thisquest->value2 ); + thisclient->client->SendPacket( &pak ); + break; + case 2: // Give Skill points + thisclient->CharInfo->SkillPoints += thisquest->value2; + break; + } + } + break; + case 4: //Restart Stats/Skills points + switch( thisquest->value1 ) + { + case 1: // Stats points [fixed by Geobot] + thisclient->CharInfo->StatPoints = 0; + for(UINT i=2;i<=thisclient->Stats->Level;i++) + thisclient->CharInfo->StatPoints += 10 + (i/2); + thisclient->Attr->Str = 15; + thisclient->Attr->Dex = 15; + thisclient->Attr->Int = 15; + thisclient->Attr->Con = 15; + thisclient->Attr->Cha = 10; + thisclient->Attr->Sen = 10; + break; + case 2: // Skill points + + break; + } + break; + case 10: //Delete a quest + if( thisquest->value1==0 ) + return true; + CQuest* athisquest = GetQuestByID( thisquest->value1 ); + QUESTS* myquest = thisclient->GetQuestByQuestID( athisquest->questid ); + if( myquest!=0 ) + myquest->active = false; + break; + } + return true; +}*/ diff --git a/Src/World Server/quest/CIntStb.hpp b/Src/World Server/quest/CIntStb.hpp new file mode 100644 index 0000000..a83d877 --- /dev/null +++ b/Src/World Server/quest/CIntStb.hpp @@ -0,0 +1,55 @@ +// Props to ExJam for this code :D + +class CIntStb +{ +public: + CIntStb() : rows(0) {} + CIntStb(char* filename) : rows(0){ LoadSTB(filename); } + ~CIntStb(){ CleanUp(); } + + int LoadSTB( char* filename ){ + FILE* fh; + char tmpfield[512]; + unsigned dataoffset = 0; + unsigned short fieldlen = 0; + + if ( (fh = fopen( filename, "rb" )) != 0 ) { + Log( MSG_ERROR, "Could not load STB '%s'", filename ); + return 1; + }else{ + fseek( fh, 4, SEEK_SET ); + fread( &dataoffset, 4, 1, fh ); + fread( &rowcount, 4, 1, fh ); + fread( &fieldcount, 4, 1, fh ); + + rowcount--; + fieldcount--; + + fseek( fh, dataoffset, SEEK_SET ); + int* tmp = new int[rowcount*fieldcount]; + rows = new int*[rowcount]; + for(dword i = 0; i < rowcount; i++ ) rows[i] = &tmp[i*fieldcount]; + for(dword j = 0; j < rowcount*fieldcount; j++) { + fread( &fieldlen, 2, 1, fh ); + fread( tmpfield, 1, fieldlen, fh ); + tmpfield[fieldlen]=0; + tmp[j] = atoi( tmpfield ); + } + fclose( fh ); + } + + return 0; + } + + dword rowcount; + dword fieldcount; + int** rows; + +private: + void CleanUp(){ + if(rows != NULL){ + delete [] rows[0]; + delete [] rows; + } + } +}; diff --git a/Src/World Server/quest/CRoseArray.hpp b/Src/World Server/quest/CRoseArray.hpp new file mode 100644 index 0000000..c6a5064 --- /dev/null +++ b/Src/World Server/quest/CRoseArray.hpp @@ -0,0 +1,120 @@ +//Made by Caali + +#define __CARRAY_HPP__ + +/* Includes */ +#include + +/* Variable type definitions */ +typedef size_t CARRAY_SIZE; + +/* Defines */ +#ifndef NULL +# ifdef __cplusplus +# define NULL 0 +# else +# define NULL (void*)0 +# endif +#endif + +/* A basic CArray class. It is being used because C++ has no function to get the actual size in memory of a pointer. */ +template +class CRoseArray +{ + private: + CARRAY_SIZE arraysize; + public: + T* arr; + CRoseArray( CARRAY_SIZE arrsize ) + { + this->arr = NULL; + this->arraysize = 0; + this->arr = new T[arrsize]; + if( this->arr != NULL ) + { + this->arraysize = arrsize; + Clean( ); + } + } + + ~CRoseArray( ) + { + if( this->arr != NULL ) + { + delete[] this->arr; + this->arraysize = 0; + } + } + + void resize( CARRAY_SIZE newsize ) + { + if( this->arr == NULL ) return; + T* tmp = new T[(size_t)(this->arraysize)]; + CARRAY_SIZE tmpsize = this->arraysize; + memcpy( tmp, this->arr, (size_t)(this->arraysize*sizeof(T)) ); + delete[] this->arr; + this->arr = new T[(size_t)(newsize)]; + if( this->arr != NULL ) + { + memcpy( this->arr, tmp, (size_t)(tmpsize * sizeof(T)) ); + delete[] tmp; + this->arraysize = newsize; + } + else + { + delete[] tmp; + this->arraysize = 0; + } + } + + void Clean( ) + { + if( this->arr != NULL ) + { + for( CARRAY_SIZE i = 0; i < this->arraysize; i++ ) + { + this->arr[i] = 0; + } + } + } + CARRAY_SIZE size() + { + return this->arraysize; + } + CARRAY_SIZE absSize() + { + return (this->arraysize*sizeof(T)); + } + inline CRoseArray& operator = ( const CRoseArray& otherarr ) + { + if( arr != NULL ) delete[] arr; + arraysize = otherarr.arraysize; + for( CARRAY_SIZE i = 0; i +#include + +#ifndef __CARRAY_HPP__ +#include "CRoseArray.hpp" +#endif + +/* MS Visual C++ Deprecation control defines */ +#ifdef _MSC_VER +# ifdef _CRT_SECURE_NO_DEPRECATE +# define CFILE_NODEPRECATE +# endif +#else +# define CFILE_NODEPRECATE +#endif + +//Debug: +//#define CFILE_NOUNICODE + +/* Various defines */ +#define CFILE_MAXLINELENGHT 255 +#define CFILE_UNICODE_HEADER 0xFEFF + +/* Variable type declarations */ +/* ETC */ +typedef bool CFILE_BOOLEAN; +typedef size_t CFILE_SIZE; + +/* Unsigned */ +typedef unsigned long long int CFILE_ULONGLONG; +typedef unsigned long int CFILE_ULONG; +typedef unsigned int CFILE_UINT; +typedef unsigned short int CFILE_USHORT; +typedef unsigned char CFILE_UCHAR; + +/* Signed */ +typedef signed long long int CFILE_SLONGLONG; +typedef signed long int CFILE_SLONG; +typedef signed int CFILE_SINT; +typedef signed short int CFILE_SSHORT; +typedef signed char CFILE_SCHAR; +typedef char CFILE_CHAR; + +/* File types */ +typedef enum +{ + FT_NONE, + FT_BINARY, + FT_ASCII, + FT_UNICODE +} FILETYPE; +/* File open modes */ +typedef enum +{ + FM_NONE = 0, + FM_READ = (1<<0), + FM_WRITE = (1<<1), + FM_CREATEIFDOESNTEXIST = (1<<2), + FM_BINARY = (1<<3), + FM_UNICODE = (1<<4), + FM_READWRITE = (FM_READ | FM_WRITE), + FM_RW = (FM_READWRITE | FM_CREATEIFDOESNTEXIST) +} FILEMODE; + +/* File errors */ +typedef enum +{ + FE_SUCCESS = 0, //No error + FE_EOF, //CFile::EndOfFile( ) == true + FE_NOFILEOPENED, //fh == NULL + FE_NORIGHTSFORACTION, //Like when trying to read when only opening in write mode + FE_INTERNALERROR, //CFile-internal errors, supposed to destruct the CFile now! + FE_UNICODENOTALLOWED, //#ifdef CFILE_NOUNICODE and file is unicode + FE_UNABLETOOPENFILE //fopen( ) == NULL +} FILEERROR; + +/* The CFile class */ +class CRoseFile +{ + private: + /** The current filetype + */ + FILETYPE ftype; + /** The last error occured + */ + FILEERROR flasterr; + /** The current file open mode bitfield + */ + CFILE_CHAR fmode; + /** Function to throw an error + @param + err: A FILEERROR + */ + void DoError( FILEERROR err ) + { + flasterr = err; + } + /** Converts the bitfield file open mode to c standart ASCII open modes + @param + mode: A byte with the bitfield + @result + string with the ASCII string + */ + CFILE_CHAR* ConvertBitFieldFileModeToASCIIFileMode( CFILE_SCHAR mode ) + { + CFILE_CHAR* result = new CFILE_CHAR[30]; + CFILE_UCHAR len = 0; + for( CFILE_CHAR i = 0; i<30; i++ ) result[i] = 0x00; + if( (mode & FM_READWRITE) == FM_READWRITE ) + { + if( (mode & FM_CREATEIFDOESNTEXIST) == FM_CREATEIFDOESNTEXIST ) + { + result[0] = 'w'; + result[1] = '+'; + len = 2; + } + else + { + result[0] = 'r'; + result[1] = '+'; + len = 2; + } + } + else if( (mode & FM_READ) == FM_READ ) + { + result[0] = 'r'; + len = 1; + } + else if( (mode & FM_WRITE) == FM_WRITE ) + { + result[0] = 'w'; + len = 1; + } + else + { + DoError( FE_INTERNALERROR ); + delete[] result; + return NULL; + } + if( (mode & FM_BINARY) == FM_BINARY ) + { + result[len] = 'b'; + len++; + } + if( (mode & FM_UNICODE) == FM_UNICODE ) + { + printf( (CFILE_CHAR*)result, 30, "%s, ccs=UNICODE", (CFILE_CHAR*)result ); + len += 13; + } + result[len] = 0x00; + return result; + } + + public: + + /** The current filehandle + */ + FILE* fh; + /** Contructor + */ + CRoseFile( ) + { + fh = NULL; + ftype = FT_NONE; + fmode = FM_NONE; + flasterr = FE_SUCCESS; + } + /** Contructor opening a file after contructing + @param + fname: A string with the filename + @param + mode: A bitfield using the FILETYPE enumerator to specify the fopen mode + */ + CRoseFile( CFILE_CHAR* fname, CFILE_CHAR mode ) + { + fh = NULL; + ftype = FT_NONE; + fmode = FM_NONE; + flasterr = FE_SUCCESS; + Open( fname, mode ); + } + + //Destructors + ~CRoseFile( ) + { + if( fh != NULL ) Close( ); + } + + /** Opens a file + @param + fname: A string with the filename + @param + mode: A bitfield using the FILETYPE enumerator to specify the fopen mode + @result + Boolean indicating if Open successed or failed + @note + If there is already an opened file, CFile will close it and open the new file + */ + CFILE_BOOLEAN Open( CFILE_CHAR* fname, CFILE_CHAR mode ) + { + if( fh != NULL ) Close( ); + fmode = mode; + CFILE_CHAR* fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode ); + if( fasciimode == NULL ) + { + DoError( FE_INTERNALERROR ); + return false; + } + if( (fmode & FM_BINARY) == FM_BINARY ) + { + ftype = FT_BINARY; + } + #ifndef CFILE_NODEPRECATE + if( fopen_s( &fh, fname, fasciimode ) == 0 ) + { + #else + if( ( fh = fopen( fname, fasciimode ) ) != NULL ) + { + #endif + delete[] fasciimode; + //Check if unicode or ascii + if( ftype != FT_BINARY ) + { + CFILE_USHORT buf; + Read( &buf, sizeof( buf ), 1 ); + if( buf == CFILE_UNICODE_HEADER ) + { + #ifndef CFILE_NOUNICODE + fmode |= FM_UNICODE; + Close( ); + CFILE_CHAR* fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode ); + //fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode ); + if( fasciimode == NULL ) + { + DoError( FE_INTERNALERROR ); + return false; + } + #ifndef CFILE_NODEPRECATE + fopen_s( &fh, fname, fasciimode ); + #else + fh = fopen( fname, fasciimode ); + #endif + SetAbsPos( 2 ); + ftype = FT_UNICODE; + #else + DoError( FE_UNICODENOTALLOWED ); + Close( ); + #endif + } + else + { + SetAbsPos( 0 ); + ftype = FT_ASCII; + } + } + else + { + SetAbsPos( 0 ); + } + return true; + } + else + { + DoError( FE_UNABLETOOPENFILE ); + fh = NULL; + delete[] fasciimode; + return false; + } + } + + /** Closes the current open file + */ + void Close( ) + { + if( fh != 0 ) + { + DoError( FE_SUCCESS ); + fclose( fh ); + fh = NULL; + } + } + + CFILE_BOOLEAN IsOpen( ) + { + return (fh != NULL)?(true):(false); + } + + /** Returns if the file has been opened in read mode + @result + Boolean indicating if the current file has been opened in read mode + */ + CFILE_BOOLEAN IsRead( ) + { + return ((fmode & FM_READ) == FM_READ)?(true):(false); + } + /** Returns if the file has been opened in write mode + @result + Boolean indicating if the current file has been opened in write mode + */ + CFILE_BOOLEAN IsWrite( ) + { + return ((fmode & FM_WRITE) == FM_WRITE)?(true):(false); + } + /** Returns filehandle + @result + Returns the current filehandle + */ + FILE* FileHandle( ) + { + return fh; + } + /** Returns filetype + @result + returns the current filetype + */ + FILETYPE FileType( ) + { + return ftype; + } + /** Returns last error + @result + Returns the last error occured + */ + FILEERROR GetLastError( ) + { + return flasterr; + } + + /** Moves the file position to a custom point + @param + offset: Indicates how many bytes to move from origin + @param + origin: SEEK_SET -> File start | SEEK_CUR -> Current position | SEEK_END -> Last byte in file + @result + Boolean indicating if the seek process has successed + @note + Offset can be negative, too. + */ + CFILE_BOOLEAN Seek( CFILE_SLONG offset, signed int origin ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return false; + } + if( fseek( fh, offset, origin ) == 0 ) + { + return true; + } + else + { + return false; + } + } + /** Moves the file position to an absolute position from the file start + @param + offset: Indicates how many bytes to move from SEEK_SET + @result + Boolean indicating if the seek process has successed + @note + Offset MUST NOT be negative, else it will not do anything + */ + CFILE_BOOLEAN SetAbsPos( CFILE_SLONG offset ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return false; + } + if( offset < 0 ) + { + DoError( FE_INTERNALERROR ); + return false; + } + return Seek(offset,SEEK_SET); + } + /** Moves the file position to the start of the file + @result + Boolean indicating if the rewinding process has successed + @note + This function checks if the file has been opened in unicode mode, and if so, it sets the pointer to 2, + the start of the file AFTER the unicode file header + */ + CFILE_BOOLEAN GoToStart( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return false; + } + if( ftype == FT_ASCII || ftype == FT_BINARY ) + { + return SetAbsPos( 0 ); + } + else if( ftype == FT_UNICODE ) + { + return SetAbsPos( 2 ); + } + DoError( FE_INTERNALERROR ); + return false; + } + /** Tells the current position in the file + @result + Signed long indicating the absolute offset from SEEK_SET + */ + CFILE_SLONG Tell( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return EOF; + } + return ftell( fh ); + } + /** Checks if the file position is set after the end of the file + @result + Boolean indicating if the file pointer is at the end of the file + */ + CFILE_BOOLEAN EndOfFile( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return true; + } + return (feof( fh )==0)?(false):(true); + } + /** Moves the file position to a custom point + @result + Unsigned long indicating the size of the file in bytes + */ + CFILE_ULONG FileSize( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + CFILE_SLONG tmp_offset = Tell( ); + if( tmp_offset < 0 ) return 0; + Seek( 0, SEEK_END ); + CFILE_SLONG result = Tell( ); + SetAbsPos( tmp_offset ); + return result; + } + /** Reads from the file starting at the current position + @param + buf: A pointer to a buffer where the data will be stored + @param + element_size: The size of each block being read + @param + element_count: The number of blocks being read + @result + Unsigned int indicating the number of bytes read + */ + CFILE_UINT Read( void* buf, CFILE_SIZE element_size, CFILE_SIZE element_count = 1 ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + return (CFILE_UINT)( fread( buf, element_size, element_count, fh ) ); + } + /** Reads from the file starting at [offset] relative to [origin] + @param + buf: A pointer to a buffer where the data will be stored + @param + element_size: The size of each block being read + @param + element_count: The number of blocks being read + @param + offset: The offset to start reading from, relative to [origin] + @param + origin: Where offset is relative to, SEEK_SET -> Beginning of file | SEEK_CUR -> Current position | SEEK_END -> End of file + @result + Unsigned int indicating the number of bytes read + */ + CFILE_UINT ReadFrom( void* buf, CFILE_UINT element_size, CFILE_UINT element_count, CFILE_SLONG offset, signed int origin = SEEK_SET ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + if( !Seek( offset, origin ) ) return 0; + return (CFILE_UINT)( Read( buf, element_size, element_count ) ); + } + /** Reads from the file starting at from and ending at to + @param + buf: A pointer to a buffer where the data will be stored + @param + from: The offset to start reading from, absolute, aka relative to SEEK_SET + @param + to: The offset to end reading at, absolute, aka relative to SEEK_SET + @result + Unsigned int indicating the number of bytes read + @note + If (to-from) > the size of buf, it throws an exception (-> access violation), so check it before calling this function! + */ + CFILE_UINT ReadFromTo( void* buf, CFILE_SLONG from, CFILE_SLONG to ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + if( to < from ) return 0; + if( !SetAbsPos( from ) ) return 0; + return (CFILE_UINT)( Read( buf, (to-from), 1 ) ); + } + /** Reads one byte and returns it as signed char + @result + char with the byte read + */ + CFILE_CHAR GetC( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return EOF; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return EOF; + } + if( ftype == FT_ASCII || ftype == FT_BINARY ) + { + return (CFILE_CHAR)( fgetc( fh ) ); + } + else if( ftype == FT_UNICODE ) + { + #ifndef CFILE_NOUNICODE + CFILE_CHAR buf; + wchar_t buffer; + buffer = fgetwc( fh ); + #ifndef CFILE_NODEPRECATE + size_t tmp; + wcstombs_s( &tmp, &buf, (size_t)(sizeof(buf)), &buffer, 1 ); + #else + wcstombs( &buf, &buffer, 1 ); + #endif + return (CFILE_CHAR)buf; + #else + DoError( FE_UNICODENOTALLOWED ); + return EOF; + #endif + } + return EOF; + } + + /** Reads from the file starting at from and ending at to + @param + buf: A pointer to a buffer where the data will be stored + @param + max_count: The maximum number of bytes to read + @result + string with the string read + @note + GetS either reads up to [max_count] bytes or until the end of one line has been reached. (0x0D 0x0A) + @note + If the file is opened in binary mode, GetS throws an error and does nothing + */ + CFILE_CHAR* GetS( CRoseArray* buf, CFILE_UINT max_count ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return NULL; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return NULL; + } + if( ftype == FT_ASCII ) + { + fgets( buf->arr, max_count, fh ); + return NULL; + } + else if( ftype == FT_UNICODE ) + { + #ifndef CFILE_NOUNICODE + wchar_t* buffer = new wchar_t[max_count]; + fgetws( buffer, max_count, fh ); + #ifndef CFILE_NODEPRECATE + size_t tmp; + wcstombs_s( &tmp, buf->arr, (size_t)(buf->absSize()), buffer, max_count ); + #else + wcstombs( buf->arr, buffer, max_count ); + #endif + return NULL; + #else + DoError( FE_UNICODENOTALLOWED ); + return NULL; + #endif + } + DoError( FE_INTERNALERROR ); + return NULL; + } + /** Reads one line + @result + string with the line read + @note + If the file is opened in binary mode, the call to GetS throws an error and does nothing + */ + CFILE_CHAR* ReadLn( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return NULL; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return NULL; + } + if( EndOfFile( ) ) + { + DoError( FE_INTERNALERROR ); + return NULL; + } + CRoseArray* result = new CRoseArray( CFILE_MAXLINELENGHT ); + result->Clean( ); + if( GetS( result, CFILE_MAXLINELENGHT ) == NULL ) + { + CFILE_CHAR* res = new CFILE_CHAR[ (size_t)( result->size() ) ]; + for( CFILE_UINT i = 0;isize(); i++ ) res[i] = 0x00; + memcpy( res, result->arr, (size_t)(result->size()*sizeof(CFILE_CHAR)) ); + for( CFILE_UINT i = 0;i= FileSize( ) - 2 ) + { + delete result; + delete[] res; + return NULL; + } + res[i] = 0x00; + break; + } + } + delete result; + return res; + } + else + { + DoError( FE_INTERNALERROR ); + delete result; + return NULL; + } + } + /** Template to read different types + @ + typename + @result + typename: the value being read + */ + template T Get( ) + { + T result; + Read( &result, sizeof( T ), 1 ); + return result; + } + /*template CVector2F Get( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return CVector2F(0,0); + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return CVector2F(0,0); + } + CVector2F result; + Read( &result, sizeof( CVector2F ), 1 ); + return result; + } */ + /** Reads the whole file into the memory + @result + string: pointer to the memory section, NULL on error + @note + The current file position pointer DOES NOT get affected by this function! + */ + /*string ReadFileToMemory( ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsRead( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + CFILE_SLONG curpos = Tell( ); + SetAbsPos( 0 ); + string mem = new char[(CFILE_SIZE)(FileSize( ))]; + if( mem == NULL ) //not enough RAM + { + SetAbsPos( curpos ); + return NULL; + } + else + { + for( CFILE_UINT i = 0; i < FileSize( ); i++ ) mem[i] = 0x00; + Read( mem, (CFILE_SIZE)(FileSize( )) ); + SetAbsPos( curpos ); + return mem; + /* + if( Read( mem, (CFILE_SIZE)(FileSize( )) ) != FileSize( ) ) + { + delete[] mem; + return NULL; + } + else + { + SetAbsPos( curpos ); + return mem; + } + * + } + }*/ + + /** Writes to the file + @param + ptr: A pointer to a buffer where the data will be read to write from + @param + element_size: The size of one block being written + @param + element_count: The number of blocks being written + @result + unsigned int indicating the number of bytes being written + */ + CFILE_UINT Write( void* ptr, CFILE_SIZE element_size, CFILE_SIZE element_count=1 ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + return (CFILE_UINT)( fwrite( ptr, (size_t)( element_size ), (size_t)( element_count ), fh ) ); + } + /** Writes to the file starting at [offset] bytes relative to [origin] + @param + ptr: A pointer to a buffer where the data will be read to write from + @param + element_size: The size of one block being written + @param + element_count: The number of blocks being written + @param + offset: The offset to start writing from, relative to [origin] + @param + origin: The position offset is relative to, SEEK_SET -> Start of file | SEEK_CUR -> Current position | SEEK_END -> End of file + @result + unsigned int indicating the number of bytes being written + */ + CFILE_UINT WriteFrom( void* ptr, CFILE_SIZE element_size, CFILE_SIZE element_count, CFILE_SLONG offset, signed int origin ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + if( !Seek( offset, origin ) ) return 0; + return (CFILE_UINT)( Write( ptr, element_size, element_count ) ); + } + /** Writes to the file, from [from] to [to] + @param + ptr: A pointer to a buffer where the data will be read to write from + @param + from: The absolute (aka relative to SEEK_SET) offset to start writing from + @param + to: The absolute (aka relative to SEEK_SET) offset to stop writing at + @result + unsigned int indicating the number of bytes being written + @note + If (to-from) > the size of ptr, it throws an exception, so check it before calling this function! + */ + CFILE_UINT WriteFromTo( void* ptr, CFILE_SLONG from, CFILE_SLONG to ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + if( to < from ) return 0; + if( !SetAbsPos( from ) ) return 0; + return (CFILE_UINT)( Write( ptr, (to-from), 1 ) ); + } + /** Writes one byte to the file, as char + @param + chr: The byte to be written + @result + signed char: If success, it returns the char being written, else it returns EOF + */ + CFILE_SCHAR PutC( CFILE_SCHAR chr ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return EOF; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return EOF; + } + if( ftype == FT_ASCII || ftype == FT_BINARY ) + { + return fputc( chr, fh ); + } + else if( ftype == FT_UNICODE ) + { + #ifndef CFILE_NOUNICODE + wchar_t buffer; + #ifndef CFILE_NODEPRECATE + size_t tmp; + mbstowcs_s( &tmp, &buffer, 1, (CFILE_CHAR*)(&chr), 1 ); + #else + mbstowcs( &buffer, (CFILE_CHAR*)(&chr), 1 ); + #endif + return (CFILE_SCHAR)(fputwc( buffer, fh )); + #else + DoError( FE_UNICODENOTALLOWED ); + return EOF; + #endif + } + return EOF; + } + + /** Writes a string + @param + str: A string to the string to be written + @result + boolean indicating if writing the string has worked + @note + If the file is opened in binary mode, it throws an error and does nothing + */ + CFILE_BOOLEAN PutS( CRoseArray* str ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return false; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return false; + } + if( ftype == FT_ASCII ) + { + return (fputs( str->arr, fh )!=EOF)?(true):(false); + } + else + { + #ifndef CFILE_NOUNICODE + wchar_t* buffer = new wchar_t[ (size_t)(str->size()) ]; + #ifndef CFILE_NODEPRECATE + size_t tmp; + mbstowcs_s( &tmp, buffer, (size_t)(str->size()), str->arr, (size_t)(str->size()) ); + #else + mbstowcs( buffer, str->arr, str->size() ); + #endif + return (fputws( buffer, fh )!=EOF)?(true):(false); + #else + DoError( FE_UNICODENOTALLOWED ); + return false; + #endif + } + DoError( FE_INTERNALERROR ); + return false; + } + + /** Template to write different types + @ + typename + @param + typename val: the value to be written + @param + cnt: times val will be written + @result + typename: If success, it returns the value being written, else it returns 0 + */ + template T Put( T val, CFILE_UINT cnt=1 ) + { + if( fh == NULL ) + { + DoError( FE_NOFILEOPENED ); + return 0; + } + if( !IsWrite( ) ) + { + DoError( FE_NORIGHTSFORACTION ); + return 0; + } + return (Write( &val, sizeof( val ), cnt ) != 0)?(val):(0); + } +}; diff --git a/Src/World Server/quest/CStrStb.hpp b/Src/World Server/quest/CStrStb.hpp new file mode 100644 index 0000000..f9c372b --- /dev/null +++ b/Src/World Server/quest/CStrStb.hpp @@ -0,0 +1,82 @@ +// Props to ExJam for this code :D + +class CStrStb +{ +public: + CStrStb():_Data(0){} + CStrStb(const char* filename) : _Data(0){ LoadSTB(filename); } + ~CStrStb(){CleanUp();} + + bool LoadSTB(const char* path) + { + CleanUp(); + + CRoseFile* fh = new CRoseFile((CFILE_CHAR*) path, FM_READ | FM_BINARY); + if(!fh->IsOpen()){ + delete fh; + return false; + } // if is open + + fh->Seek(4, SEEK_CUR); + dword offset = fh->Get(); + + _Rows = fh->Get(); + _Columns = fh->Get(); + + _Rows--; + _Columns--; + + fh->SetAbsPos(offset); + _Data = new char**[_Rows]; + for(dword i = 0; i < _Rows; i++) + _Data[i] = new char*[_Columns]; + + for(dword i = 0; i < _Rows; i++){ + for(dword j = 0; j < _Columns; j++){ + dword len = fh->Get(); + if(len == 0){ + _Data[i][j] = NULL; + continue; + } // if len + _Data[i][j] = new char[len+1]; + fh->Read(_Data[i][j], len, 1); + _Data[i][j][len] = 0; + } // for j + } // for i + + fh->Close(); + delete fh; + return true; + } // bool + + char* Data(dword Row, dword Column){ + if(Row > _Rows) return NULL; + if(Column > _Columns) return NULL; + + return _Data[Row][Column]; + } + + dword Rows(){ return _Rows; } + dword Columns(){ return _Columns; } + +private: + void CleanUp(){ + if(_Data != NULL){ + for(dword i = 0; i < _Rows; i++){ + for(dword j = 0; j < _Columns; j++){ + delete [] _Data[i][j]; + } + delete [] _Data[i]; + } + delete [] _Data; + } + _Rows = 0; + _Columns = 0; + } + +private: + dword _Rows; + dword _Columns; + + char*** _Data; +}; diff --git a/Src/World Server/quest/QuestActions.cpp b/Src/World Server/quest/QuestActions.cpp new file mode 100644 index 0000000..5c297df --- /dev/null +++ b/Src/World Server/quest/QuestActions.cpp @@ -0,0 +1,1052 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#include "../WorldServer.h" + +dword GetRewardValue(dword function, dword amount, CPlayer* client, word nDupCNT) +{ + switch(function) + { + case 0: + { + dword tempVal = amount + 0x1E; + tempVal *= client->Attr->Cha + 0x0a; // We need to add a way to get all stats totals (Including buff, E, etc) - Drakia + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->Stats->Level + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 1: + { + dword tempVal = (client->Stats->Level + 3) * amount; + tempVal *= (client->Attr->Cha >> 1) + client->Stats->Level + 0x28; + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x2710; + } + break; + case 2: + return (amount * nDupCNT); + break; + case 3: + case 5: + { + dword tempVal = amount + 0x14; + tempVal *= client->Attr->Cha + 0x0a; + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->Stats->Level + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 4: + { + dword tempVal = amount + 0x2; + tempVal *= (client->Attr->Cha + client->Stats->Level + 0x28); + tempVal *= 0x28;//Fame + 0x28 + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x222E0; + } + break; + case 6: + { + dword tempVal = amount + 0x14; + tempVal *= client->Attr->Cha + client->Stats->Level; + tempVal *= 0x14;//Fame + 0x14 + tempVal *= (100 & 0xFFFF);//World Rate + return (tempVal / 0x2DC6C0) + amount; + } + break; + default: + break; + } + return amount; +} + +//Update Quest +QUESTREWD(000) +{ + //dword iQuestSN pos 0x00 + //byte btOp pos 0x04 + GETREWDDATA(000); + switch(data->btOp)//0 remove, 1 start, 2 replace quest keep items, 3 replace quest delete items, 4 select + { + case 0: + { + if( client->questdebug ) + server->SendPM(client, "Remove Quest: %u", data->iQuestSN); + if (client->ActiveQuest == data->iQuestSN) client->ActiveQuest = 0; + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != data->iQuestSN) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + break; + } + } + break; + case 1: + { + if( client->questdebug ) + server->SendPM(client, "Start Quest: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID == data->iQuestSN) return QUEST_SUCCESS; + if(client->quest.quests[i].QuestID != 0) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 2: + { + if( client->questdebug ) + server->SendPM(client, "Replace Quest, keep items: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != client->ActiveQuest) continue; + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 3: + { + if( client->questdebug ) + server->SendPM(client, "Replace Quest, delete items: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != client->ActiveQuest) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 4: + { + if( client->questdebug ) + server->SendPM(client, "Select Quest: %u", data->iQuestSN); + client->ActiveQuest = data->iQuestSN; + } + break; + default: + break; + } + return QUEST_SUCCESS; +} + +//Update Quest Items +QUESTREWD(001) +{ + //dword uiItemSN; pos 0x00 + //byte btOp; pos 0x04 + //word nDupCNT; pos 0x06 + //byte btPartyOpt; pos 0x0a + GETREWDDATA(001); + if( client->questdebug )server->SendPM(client, "Update Quest Item (uiItemSN: %u btOp: %u nDupCNT: %u)", data->uiItemSN, data->btOp, data->nDupCNT); + CItem tmpItem; + tmpItem.itemtype = data->uiItemSN / 1000; + tmpItem.itemnum = data->uiItemSN % 1000; + tmpItem.count = data->nDupCNT; + + SQuest* curQuest = client->GetActiveQuest(); + if(curQuest == NULL) + { + //Log(MSG_DEBUG, "Couldn't find active quest, wtf? Id %u", client->ActiveQuest); + return QUEST_FAILURE; + } + curQuest->AddItem(&tmpItem, data->btOp); + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(002) +{ + GETREWDDATA(002); + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 union. This or + //short m_mVarNo pos 0x04 this + + //word m_wVarTYPE; pos 0x06 Type of quest variable + // 0 GetVarType = "ActiveQuest" + // 16 GetVarType = "SwitchBit" + // 32 GetVarType = "Remaining Time" + // 48 GetVarType = "EpisodeVar" + // 64 GetVarType = "JobVar" + // 80 GetVarType = "PlanetVar" + // 96 GetVarType = "UnionVar" + //word nValue; pos 0x08 + //byte btOp; pos 0x0a + + for(dword i = 0; i < data->iDataCnt; i++) + { + dword address = i * sizeof(STR_QUEST_DATA); + address += (dword)data; + address += 4; + STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Set quest var[%#04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp ); + word nValue = curQst->nValue; + word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO); + OperateValues(curQst->btOp, &tempValue, curQst->nValue); + client->SetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO, tempValue); + } + return QUEST_SUCCESS; +} + +//Update Stats +QUESTREWD(003) +{ + GETREWDDATA(003); + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 + //dword iValue; pos 0x08 + //byte btOp; pos 0x0c + for(dword i = 0; i < data->iDataCnt; i++) + { + //dword address = i * 0x0C; + //address += (dword)data; + //address += 4; + //STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Update stat[%i] %i (Op: %i)", data->iType, data->iValue, data->btOp); + switch( data->iType ) + { + case 2: //Gender + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Sex, data->iValue)) + return QUEST_FAILURE; + //client->CharInfo->Sex = data->iValue; + //BEGINPACKET( pak, 0x720 ); + //ADDWORD( pak, 2 ); + //ADDWORD( pak, client->CharInfo->Sex ); + //ADDWORD( pak, 0 ); + //GServer->SendToVisible( &pak, client ); + //client->client->SendPacket( &pak ); + //RESETPACKET( pak, 0x0730 ); + //ADDWORD( pak, 5 ); + //ADDWORD( pak, 0xa24d ); + //ADDWORD( pak, 0x40b3 ); + //GServer->SendToVisible( &pak, client ); + //client->client->SendPacket( &pak ); + client->quicksave(); + } + break; + case 4: //job + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Job, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + client->quicksave(); + } + break; + case 5: //union + { + if (!OperateValues(data->btOp, (int*)&client->Union_s->unionvar[0], data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + client->quicksave(); + //Log(MSG_DEBUG,"Client Union changed"); + } + break; + case 8: // face + { + if (!OperateValues(data->btOp, (int*)&client->CharInfo->Face, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 8); + ADDWORD(pak, client->CharInfo->Face); + ADDWORD(pak, 0); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + client->quicksave(); + } + break; + case 9: // Hair + { + if (!OperateValues(data->btOp, (int*)&client->CharInfo->Hair, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 9); + ADDWORD(pak, client->CharInfo->Hair); + ADDWORD(pak, 0); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + client->quicksave(); + } + case 81: // Union points for junon order + case 82: + case 83: // Union points for righteous crusaders + case 84: + case 85: + case 86: + case 87: // Union 7 points used for tracking previous memberships + case 88: + case 89: //kuro-Tejina Union points + case 90: + { + if (!OperateValues(data->btOp, (int*)&client->Union_s->unionvar[data->iType - 80], data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x720 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + + if(data->iType == 89) //update the database if the quest just gave KTPoints + { + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + client->Session->KTPoints,client->Session->userid, client->Session->password); + } + } + break; + case 10: //strength + if(!OperateValues(data->btOp, (int*)&client->Attr->Str, data->iValue)) + return QUEST_FAILURE; + break; + case 11: //dexterity + if(!OperateValues(data->btOp, (int*)&client->Attr->Dex, data->iValue)) + return QUEST_FAILURE; + break; + case 12: //inteligence + if(!OperateValues(data->btOp, (int*)&client->Attr->Int, data->iValue)) + return QUEST_FAILURE; + break; + case 13: //concentration + if(!OperateValues(data->btOp, (int*)&client->Attr->Con, data->iValue)) + return QUEST_FAILURE; + break; + case 14: //charm + if(!OperateValues(data->btOp, (int*)&client->Attr->Cha, data->iValue)) + return QUEST_FAILURE; + break; + case 15: //sense + if(!OperateValues(data->btOp, (int*)&client->Attr->Sen, data->iValue)) + return QUEST_FAILURE; + break; + case 30: // set global XP rate + { + GServer->Config.EXP_RATE = data->iValue; + return QUEST_SUCCESS; + } + break; + case 31: //level + if(!OperateValues(data->btOp, (int*)&client->Stats->Level, data->iValue)) + return QUEST_FAILURE; + break; + case 32: // Stat points + if(!OperateValues(data->btOp, (int*)&client->CharInfo->StatPoints, data->iValue)) + return QUEST_FAILURE; + break; + case 37: // Skill points + if(!OperateValues(data->btOp, (int*)&client->CharInfo->SkillPoints, data->iValue)) + return QUEST_FAILURE; + break; + //case sMoney: + //if(!OperateValues(data->btOp, (int*)&client->CharInfo->Zulies, data->iValue)) + //return QUEST_FAILURE; + //break; + case 40: //money + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Zulies, data->iValue)) + return QUEST_FAILURE; + // Send zuly update packet + BEGINPACKET(pak, 0x71D); + ADDQWORD( pak, client->CharInfo->Zulies ); + client->client->SendPacket( &pak ); + } + break; + default: + Log(MSG_WARNING, "Type Unknown: '%i'", data->iType); + break; + } + } + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(004) +{ + return QUEST_REWD_002(server, client, raw); +} + + //Give Reward +QUESTREWD(005) +{ + GETREWDDATA(005); + if( client->questdebug ) + server->SendPM(client, "Quest reward 005 using equation %i", data->btEquation); + switch(data->btTarget) + { + case 0://EXP + { + if( client->questdebug ) + server->SendPM(client, "Give EXP: %i", data->iValue); + client->CharInfo->Exp += GetRewardValue(data->btEquation, data->iValue, client, 0); + BEGINPACKET(pak, 0x79b); + ADDDWORD(pak, client->CharInfo->Exp); + ADDDWORD(pak, client->CharInfo->stamina); + ADDWORD (pak, 0); + client->client->SendPacket(&pak); + } + break; + case 1://Zuly + { + if( client->questdebug ) + server->SendPM(client, "Give Zuly: %i", data->iValue); + unsigned int j = client->CheckQuest; + int itemcount = client->quest.quests[j].Items[0].count; + if( client->questdebug ) + server->SendPM(client, "Items returned: %i", itemcount); + //int Zulyreward = GetRewardValue(data->btEquation, data->iValue, client, itemcount); + // temp fix + int Zulyreward = data->iValue; + if(itemcount > 0 ) //this is for cases where multiple items are handed in for fetch quest. + Zulyreward = data->iValue * itemcount; + client->CharInfo->Zulies += Zulyreward; + //client->CharInfo->Zulies += GetRewardValue(data->btEquation, data->iValue, client, 1);//dunno nDupCount for this one! + BEGINPACKET(pak, 0x71D); + ADDQWORD(pak, client->CharInfo->Zulies); + client->client->SendPacket(&pak); + } + break; + case 2://Item + { + CItem nItem; + nItem.itemtype = data->iItemSN / 1000; + nItem.itemnum = data->iItemSN % 1000; + if(nItem.IsStackable()) + { + nItem.count = GetRewardValue(data->btEquation, data->iValue, client, 0); + } + else + { + nItem.count = 1; + } + if( client->questdebug ) + server->SendPM(client, "Give item [%i][%i]x%i", nItem.itemtype, nItem.itemnum, nItem.count); + nItem.durability = GServer->STB_ITEM[nItem.itemtype-1].rows[nItem.itemnum][29]; + nItem.gem = 0; + nItem.stats = 0; + nItem.refine = 0; + nItem.socketed = 0; + nItem.lifespan = 100; + nItem.appraised = 1; + dword slot = client->AddItem(nItem); + if (slot == 0xffff) // Fail + { + BEGINPACKET( pak, 0x7a7); + ADDWORD(pak, 0x00); + ADDBYTE(pak, 0x03); + ADDBYTE(pak, 0x00); + client->client->SendPacket(&pak); + } + else // Success + { + BEGINPACKET( pak, 0x71f); + ADDBYTE(pak, 0x01); + ADDBYTE(pak, slot); + ADDWORD(pak, client->items[slot].GetPakHeader( ) ); + ADDDWORD(pak, client->items[slot].GetPakData( ) ); + client->client->SendPacket(&pak); + } + } + break; + default: + break; + } + return QUEST_SUCCESS; +} + +//Restore HP/MP +QUESTREWD(006) +{ + //dword iPercentOfHP pos 0x00 + //dword iPercentOfMP pos 0x04 + //byte btPartyOpt pos 0x08 + GETREWDDATA(006); + if( client->questdebug ) + server->SendPM(client, "Restore %i%% HP, %i%% MP", data->iPercentOfHP, data->iPercentOfMP); + client->Stats->HP = (long int)((float)client->Stats->MaxHP / 100.0f) * data->iPercentOfHP; + client->Stats->MP = (long int)((float)client->Stats->MaxHP / 100.0f) * data->iPercentOfMP; + return QUEST_SUCCESS; +} + +//Teleport +QUESTREWD(007) +{ + //dword iZoneSN; pos 0x00 + //dword iX; pos 0x04 + //dword iY; pos 0x08 + //byte btPartyOpt; pos 0x0d + GETREWDDATA(007); + fPoint thispoint; + thispoint.x = floor(((float)data->iX)/100); + thispoint.y = floor(((float)data->iY)/100); + if( client->questdebug ) + server->SendPM(client, "Teleport [%i][%f][%f]", data->iZoneSN, thispoint.x, thispoint.y); + GServer->TeleportTo(client, data->iZoneSN, thispoint); + return QUEST_SUCCESS; +} + +//Spawn Monster +QUESTREWD(008) +{ + //dword iMonsterSN pos 0x00 + //dword iHowMany pos 0x04 + // byte btWho pos 0x08 or + // int iWho pos 0x08 + //dword iZoneSN pos 0x0a + //dword iX pos 0x0e + //dword iY pos ox11 + //dword iRange pos 0x15 + //dword iTeamNo pos 0x19 + GETREWDDATA(008); + fPoint position; + dword mapId; + if(data->iX == 0 || data->iY == 0 || data->iZoneSN == 0) + { + position.x = client->Position->current.x; + position.y = client->Position->current.y; + mapId = client->Position->Map; + } + else + { + position.x = data->iX / 100; + position.y = data->iY / 100; + mapId = data->iZoneSN; + } + for(dword i = 0; i < data->iHowMany; i++) + { + fPoint pos = GServer->RandInCircle( position, data->iRange ); + if( client->questdebug ) + server->SendPM(client, "Spawn mob[%i] @ %f, %f", data->iMonsterSN, pos.x, pos.y); + CMap* map = GServer->MapList.Index[mapId]; + CMonster* mon = map->AddMonster( data->iMonsterSN, pos, 0, 0 , 1 ); + if(data->iMonsterSN > 750 && data->iMonsterSN < 755) // ghost rackies = non aggresive + { + mon->thisnpc->aggresive = 0; + } + else + { + mon->thisnpc->aggresive = 999; // Force the mob to be agressive. + } + mon->lastSighCheck = 0; // Force sight check instantly. + } + return QUEST_SUCCESS; +} + +//Execute Quest Trigger +QUESTREWD(009) +{ + //word shNameLen pos 0x00 + //string szNextTriggerSN pos 0x02 + GETREWDDATA(009); + char* tempName = reinterpret_cast(&data->szNextTriggerSN) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, "Execute Quest Trigger %s[%d] [%08x]", tempName, data->shNameLen, hash); + return client->ExecuteQuestTrigger(hash); + //return QUEST_SUCCESS; +} + +//Reset Stats +QUESTREWD(010) +{ + if( client->questdebug ) + server->SendPM(client, "Reset Stats"); + client->CharInfo->StatPoints = 0; + client->Attr->Str = 15; + client->Attr->Dex = 15; + client->Attr->Int = 15; + client->Attr->Con = 15; + client->Attr->Cha = 10; + client->Attr->Sen = 10; + for(int i = 2; i <= client->Stats->Level; i++) + { + client->CharInfo->StatPoints += 10; + client->CharInfo->StatPoints += i / 2; + } + return QUEST_SUCCESS; +} + +//Update Object Var +QUESTREWD(011) +{ + GETREWDDATA(011); + /*if(data->btWho == 0) //npc + { + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return QUEST_FAILURE; + if(data->nVarNo > 19) return AI_FAILURE; + short tempval = GServer->ObjVar[monster->thisnpc->refNPC][data->nVarNo]; + switch(data->btOp) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + case 9: + tempval++; + break; + default: + return QUEST_FAILURE; + break; + } + if(tempval < 0)tempval = 0; + GServer->ObjVar[monster->thisnpc->refNPC][data->nVarNo] = tempval; + return QUEST_SUCCESS; + } + else if(data->btWho == 1) // event + { + //need to write the event code still it seems. Seems easy enough + return QUEST_FAILURE; + }*/ + + + /*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + + if(data->btWho == 0){//Npc + CNpc* thisNpc = reinterpret_cast(entity); + thisNpc = thisNpc->SelectedNpc; + if(thisNpc == NULL) return QUEST_FAILURE; + + short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo); + OperateValues(data->btOp, &VarValue, (short)data->iValue); + thisNpc->ObjVar.SetVar(data->nVarNo, VarValue); + }else if(data->btWho == 1){//Event + short VarValue = server->EventVar.GetVar(data->nVarNo); + OperateValues(data->btOp, &VarValue, (short)data->iValue); + server->EventVar.SetVar(data->nVarNo, VarValue); + } + + + return QUEST_SUCCESS;*/ + //byte btWho; pos 0x00 + //word nVarNo; pos 0x02 + //dword iValue; pos 0x04 this was an int but ints only hold 2 bytes so btop would be out of position + //byte btOp; pos 0x08 btop definitely at position 0x08 so had to adjust iValue + int tempval; + if(data->btWho == 0) + { + if(data->nVarNo > 19) return AI_FAILURE; + tempval = GServer->ObjVar[client->NPCvar][data->nVarNo]; + //Log(MSG_INFO,"Just checking. NPCvar = %i", client->NPCvar); + switch( data->btOp ) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + break; + case 9: + tempval++; + break; + default: + return QUEST_FAILURE; + break; + } + } + else if(data->btWho == 1) //event + { + // not sure what to do here yet + return QUEST_FAILURE; + } + GServer->ObjVar[client->NPCvar][data->nVarNo] = tempval; + return QUEST_SUCCESS; +} + +//NPC Speak +QUESTREWD(012) +{ + //if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + GETREWDDATA(012); +/* + CNpc* thisNpc = reinterpret_cast(entity); + if(thisNpc->SelectedNpc == NULL) return QUEST_FAILURE; + std::map::iterator triggerITR = server->NpcNames.find(thisNpc->SelectedNpc->NpcID); + if(triggerITR == server->NpcNames.end()) return QUEST_FAILURE; + + char* npcName = triggerITR->second; + char* sayStr = server->lngQst->GetLTBStr(data->iStrID); + if(sayStr == NULL) return QUEST_FAILURE; + if(data->btMsgType == 1){ + //Shout + CPacket pakout(0x785); + pakout.Add(npcName); + pakout.Add(sayStr); + server->SendPacketToZone(thisNpc, &pakout); + }else if(data->btMsgType == 2){ + //Announce + CPacket pakout(0x702); + pakout.AddBytes((byte*)npcName, strlen(npcName)); + pakout.Add('>'); + pakout.Add(sayStr); + server->SendPacketToZone(thisNpc, &pakout); + } + delete [] sayStr;*/ + + dword npcId = 0; + //if(entity->_EntityType == ENTITY_NPC) + //CMonster* thisMonster = reinterpret_cast(entity); + switch(data->btMsgType) + { + case 0: //whisper to client + // don't think this is possible. No way to get a character to send it to?? + break; + case 1: //shout to map + Log(MSG_INFO,"Quest -- NPC Shouts LTB string %i to map %i",data->iStrID,client->Position->Map); + GServer->NPCShout(client->Position->Map,GServer->Ltbstring[data->iStrID].QuestLTB,GServer->Ltbstring[data->iStrID].QuestNpcName); + break; + case 2: //announce to server. + Log(MSG_INFO,"Quest -- NPC announces LTB string %i to map %i",data->iStrID,client->Position->Map); + GServer->NPCAnnounce(GServer->Ltbstring[data->iStrID].QuestLTB,GServer->Ltbstring[data->iStrID].QuestNpcName); + break; + default: + break; + } + + return QUEST_SUCCESS; + // This is cool and all, but we lack the stuff to do it - Drakia + // Not any more - PY +} + +//Unknown +QUESTREWD(013) +{ + // byte btWho pos 0x00 + // int iWho pos 0x00 + //dword iSec pos 0x02 + //word nNameLength pos 0x06 + //string szTriggerName pos 0x08 + //dword m_HashNextTrigger pos 0x06 + nNameLength? + //looks like a quest trigger to me + GETREWDDATA(013); + char* tempName = reinterpret_cast(&data->szTriggerName) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, " RWD 013 Execute Quest Trigger %s[%d] [%08x]", tempName, data->nNameLength, hash); + //suppress the actual execution for now just to be sure + return client->ExecuteQuestTrigger(hash); + return QUEST_SUCCESS; +} + +//Learn Skill +QUESTREWD(014) +{ + // byte btOp pos 0x00 + // int iOp pos 0x00 + //dword iSkillNo pos 0x02 + GETREWDDATA(014); + if( client->questdebug ) + server->SendPM(client, "Learn skill: %i", data->iSkillNo); + GServer->LearnSkill(client, data->iSkillNo, false); + return QUEST_SUCCESS; +} + +//Set Quest Flag +QUESTREWD(015) +{ + GETREWDDATA(015); + //word nSN; 0x00 + //byte btOp; 0x02 + if( client->questdebug ) + server->SendPM(client, "Set QFlag[%i]=%i", data->nSN, data->btOp); + client->quest.SetFlag(data->nSN, (data->btOp == 1)?true:false); + // PY method + //client->quest.Qflags[data->nSN] == data->btOp; + // PY end + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(016) +{ + //word nGroupSN pos 0x00 + return QUEST_SUCCESS; +} + +//Reset All Quest Flags +QUESTREWD(017) +{ + if( client->questdebug ) + server->SendPM(client, "Reset all quest flags"); + //memset(&client->quest.flags, 0, 64); + // PY method + for(UINT i=0;i<1000;i++) + { + for(int j=0;j<8;j++) + { + client->quest.Qflags[i][j] = 0; + } + } + // PY end + return QUEST_SUCCESS; +} + +//Send Announcement +QUESTREWD(018) +{ + //dword iStrID pos 0x00 + //word nCnt pos 0x04 + //string Data pos 0x06 + //why isn't this coded? looks simple enough + GETREWDDATA(018); + char* tempName = reinterpret_cast(&data->Data) - 2; + if( client->questdebug ) + server->SendPM(client, " RWD 018 Send announcement %s", tempName); + Log(MSG_INFO,"Announcement to all %s",tempName); + GServer->NPCAnnounce(tempName,"Quest NPC"); + return QUEST_SUCCESS; +} + +//Execute Quest Trigger in Other Map +QUESTREWD(019) +{ + //this is going to be a pain + //it runs a quest trigger that would appear to be sent to every player in the designated zone + + //word nZoneNo; //pos 0x00 + //word nTeamNo; //pos 0x02 + //word nTriggerLength; //pos 0x04 This is definitely NOT the length of the following name. PY + //string TriggerName; //pos 0x06 + nTriggerlength more bytes. Name always appears to be 17 bytes long + //dword m_HashTrigger; + //word m_HashTrigger; //pos 0x17 only has 2 bytes not 4. PY + + // let's check the values first + GETREWDDATA(019); + //Log(MSG_DEBUG,"Execute Quest trigger: %s for zone %i. Hash: %i",data->TriggerName, data->nZoneNo, data->m_HashTrigger); + + /*CMap* map = GServer->MapList.Index[data->nZoneNo]; + if(map == NULL) return QUEST_SUCCESS; + if( map->PlayerList.size()<1 ) return QUEST_SUCCESS; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + player->ExecuteQuestTrigger(data->m_HashTrigger); + } + return QUEST_SUCCESS;*/ + + char* tempName = reinterpret_cast(&data->TriggerName) - 2; + dword hash = MakeStrHash(tempName); + CMap* map = GServer->MapList.Index[data->nZoneNo]; + if(map == NULL) return QUEST_SUCCESS; + if( map->PlayerList.size()<1 ) return QUEST_SUCCESS; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + player->ExecuteQuestTrigger(hash); + } + + return QUEST_SUCCESS; +} + +//PvP Status +QUESTREWD(020) +{ + GETREWDDATA(020); + //byte btNoType; pos 0x00 + + return QUEST_SUCCESS; +} + +//Set Respawn Position +QUESTREWD(021) +{ + //dword iX pos 0x00 + //dword iY pos 0x04 + GETREWDDATA(020); + client->Position->saved = client->Position->Map; + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(022) +{ + //word nZonNo pos 0x00 + //byte btOp pos 0x02 + return QUEST_SUCCESS; +} + +//Raise Clan Grade - This is not the least bit efficient - Drakia +QUESTREWD(023) +{ + GETREWDDATA(023); + if( client->questdebug ) + server->SendPM(client, "Raise clan grade"); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + continue; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if( player->Clan->clanid != client->Clan->clanid ) continue; + player->Clan->grade++; + + //load clan info in char server + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0xfb ); //action to update clan informacion (charserver) + ADDWORD ( pak, player->Clan->clanid ); + ADDWORD ( pak, player->Clan->grade ); + GServer->SendISCPacket( &pak ); + + //Send to other players + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, player->clientid ); + ADDWORD ( pak, player->Clan->clanid); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, player->Clan->back ); + ADDWORD ( pak, player->Clan->logo ); + ADDBYTE ( pak, player->Clan->grade );//clan grade + ADDBYTE ( pak, 0x06 );//clan rank + ADDSTRING ( pak, player->Clan->clanname ); + ADDBYTE ( pak, 0x00 ); + GServer->SendToVisible( &pak, player ); + } + } + GServer->DB->QExecute("UPDATE list_clan SET grade=%i WHERE id=%i", client->Clan->grade, client->Clan->clanid); + return QUEST_SUCCESS; +} + +//Clan Money +QUESTREWD(024) +{ + //int iMONEY pos 0x00 + //byte btOP pos 0x02 + GETREWDDATA(024); + return QUEST_SUCCESS; +} + +//Clan Points +QUESTREWD(025) +{ + //short nPOINT pos 0x00 + //byte btOP pos 0x02 + GETREWDDATA(025); + return QUEST_SUCCESS; +} + +//Clan Skill +QUESTREWD(026) +{ + GETREWDDATA(026); + //word nSkillNo; pos 0x00 + //byte btOP; pos 0x02 + //do something (based on btOp) with a clan skill id = data->nSkillNo + if( client->questdebug )server->SendPM(client, "Clan skill rwd[026] skill id: %i btOP: %i",data->nSkillNo,data->btOP); + return QUEST_SUCCESS; +} + +//Clan Contribution +QUESTREWD(027) +{ + GETREWDDATA(027); + //short nCONT; pos 0x00 + //byte btOP; pos 0x02 + if( client->questdebug )server->SendPM(client, "Clan Contribution rwd[026] nCONT: %i btOP: %i",data->nCONT,data->btOP); + + return QUEST_SUCCESS; +} + +//Clan Teleportation - Again, not efficient. Maybe keep a list of pointers to clan members? - Drakia +QUESTREWD(028) +{ + //dword iRange; pos 0x00 + //word nZoneNo; pos 0x04 + //dword iX; pos 0x06 + //dword iY; pos 0x0a + GETREWDDATA(028); + fPoint telepos; + fPoint newPos; + telepos.x = data->iX/100; + telepos.y = data->iY/100; + if( client->questdebug ) + server->SendPM(client, "Clan teleport to [%i][%f][%f]", data->nZoneNo, telepos.x, telepos.y); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + continue; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if( player->Clan->clanid != client->Clan->clanid ) continue; + newPos = GServer->RandInCircle(telepos, data->iRange); + GServer->TeleportTo(player, data->nZoneNo, newPos ); + } + } + return QUEST_SUCCESS; +} + +QUESTREWD(029) //execute client quest trigger (LUA) +{ + GETREWDDATA(029); + char* tempName = reinterpret_cast(&data->LuaName) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, "Execute Client LUA %s[%d] [%08x]", tempName, data->strLen, hash); + //return client->ExecuteQuestTrigger(hash); + //need to code a packet here. on evo it's 0x7f8. Don't even know if we use one here + Log(MSG_DEBUG,"ran quest action 29"); + return QUEST_SUCCESS; +} + +QUESTREWD(030) //Reset skills +{ + //Log(MSG_DEBUG,"ran quest action 30"); + for(int i=0;i<30;i++) + { + //give one sp for every active skill present + if(client->askill[i] != 0) + { + client->CharInfo->SkillPoints += 1; + } + client->askill[i] = 0; + //give SP equal to the level of the active skill. Actual level is always 1 more than this + client->CharInfo->SkillPoints += client->askilllvl[i]; + client->askilllvl[i] = 0; + //give one sp for every passive skill present + if(client->pskill[i] != 0) + { + client->pskill[i] = 0; + client->CharInfo->SkillPoints += 1; + } + //give SP equal to the level of the passive skill. Actual level is always 1 more than this + client->CharInfo->SkillPoints += client->pskilllvl[i]; + client->pskilllvl[i] = 0; + } + client->savedata(); + return QUEST_SUCCESS; +} diff --git a/Src/World Server/quest/QuestActions.cpp.wrong b/Src/World Server/quest/QuestActions.cpp.wrong new file mode 100644 index 0000000..65612b9 --- /dev/null +++ b/Src/World Server/quest/QuestActions.cpp.wrong @@ -0,0 +1,1138 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#include "../WorldServer.h" + +dword GetRewardValue(dword function, dword amount, CPlayer* client, word nDupCNT) +{ + switch(function) + { + case 0: + { + dword tempVal = amount + 0x1E; + tempVal *= client->Attr->Cha + 0x0a; // We need to add a way to get all stats totals (Including buff, E, etc) - Drakia + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->Stats->Level + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 1: + { + dword tempVal = (client->Stats->Level + 3) * amount; + tempVal *= (client->Attr->Cha >> 1) + client->Stats->Level + 0x28; + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x2710; + } + break; + case 2: + return (amount * nDupCNT); + break; + case 3: + case 5: + { + dword tempVal = amount + 0x14; + tempVal *= client->Attr->Cha + 0x0a; + tempVal *= (100 & 0xFFFF);//World Rate + tempVal *= 0x14;//Fame + 0x14 + tempVal = ((tempVal / (client->Stats->Level + 0x46)) / 0x7530) + amount; + return tempVal; + } + break; + case 4: + { + dword tempVal = amount + 0x2; + tempVal *= (client->Attr->Cha + client->Stats->Level + 0x28); + tempVal *= 0x28;//Fame + 0x28 + tempVal *= (100 & 0xFFFF);//World Rate + return tempVal / 0x222E0; + } + break; + case 6: + { + dword tempVal = amount + 0x14; + tempVal *= client->Attr->Cha + client->Stats->Level; + tempVal *= 0x14;//Fame + 0x14 + tempVal *= (100 & 0xFFFF);//World Rate + return (tempVal / 0x2DC6C0) + amount; + } + break; + default: + break; + } + return amount; +} + +//Update Quest +QUESTREWD(000) +{ + //dword iQuestSN pos 0x00 + //byte btOp pos 0x04 + GETREWDDATA(000); + switch(data->btOp) //0 remove, 1 start, 2 replace quest keep items, 3 replace quest delete items, 4 select + { + case 0: + { + if( client->questdebug ) + server->SendPM(client, "Remove Quest: %u", data->iQuestSN); + if (client->ActiveQuest == data->iQuestSN) client->ActiveQuest = 0; + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != data->iQuestSN) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + break; + } + } + break; + case 1: + { + if( client->questdebug ) + server->SendPM(client, "Start Quest: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID == data->iQuestSN) return QUEST_SUCCESS; + if(client->quest.quests[i].QuestID != 0) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 2: + { + if( client->questdebug ) + server->SendPM(client, "Replace Quest, keep items: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != client->ActiveQuest) continue; + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 3: + { + if( client->questdebug ) + server->SendPM(client, "Replace Quest, delete items: %u", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID != client->ActiveQuest) continue; + memset(&client->quest.quests[i], 0, sizeof(SQuest)); + client->quest.quests[i].QuestID = data->iQuestSN; + client->quest.quests[i].StartTime = time(NULL); + break; + } + client->ActiveQuest = data->iQuestSN; + } + break; + case 4: + { + if( client->questdebug ) + server->SendPM(client, "Select Quest: %u", data->iQuestSN); + client->ActiveQuest = data->iQuestSN; + } + break; + default: + break; + } + return QUEST_SUCCESS; +} + +//Update Quest Items +QUESTREWD(001) +{ + //dword uiItemSN; pos 0x00 + //byte btOp; pos 0x04 + //word nDupCNT; pos 0x06 + //byte btPartyOpt; pos 0x0a + GETREWDDATA(001); + if( client->questdebug )server->SendPM(client, "Update Quest Item (uiItemSN: %u btOp: %u nDupCNT: %u)", data->uiItemSN, data->btOp, data->nDupCNT); + CItem tmpItem; + tmpItem.itemtype = data->uiItemSN / 1000; + tmpItem.itemnum = data->uiItemSN % 1000; + tmpItem.count = data->nDupCNT; + + SQuest* curQuest = client->GetActiveQuest(); + if(curQuest == NULL) + { + //Log(MSG_DEBUG, "Couldn't find active quest, wtf? Id %u", client->ActiveQuest); + return QUEST_FAILURE; + } + curQuest->AddItem(&tmpItem, data->btOp); + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(002) +{ + GETREWDDATA(002); + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 union. This or + //short m_mVarNo pos 0x04 this + + //word m_wVarTYPE; pos 0x06 Type of quest variable + // 0 GetVarType = "ActiveQuest" + // 16 GetVarType = "SwitchBit" + // 32 GetVarType = "Remaining Time" + // 48 GetVarType = "EpisodeVar" + // 64 GetVarType = "JobVar" + // 80 GetVarType = "PlanetVar" + // 96 GetVarType = "UnionVar" + //word nValue; pos 0x08 + //byte btOp; pos 0x0a + + for(dword i = 0; i < data->iDataCnt; i++) + { + dword address = i * sizeof(STR_QUEST_DATA); + address += (dword)data; + address += 4; + STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Set quest var[%#04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp ); + word nValue = curQst->nValue; + word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO); + OperateValues(curQst->btOp, &tempValue, curQst->nValue); + client->SetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO, tempValue); + } + return QUEST_SUCCESS; +} + +//Update Stats +QUESTREWD(003) +{ + GETREWDDATA(003); + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 + //dword iValue; pos 0x08 + //byte btOp; pos 0x0c + for(dword i = 0; i < data->iDataCnt; i++) + { + //dword address = i * 0x0C; + //address += (dword)data; + //address += 4; + //STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Update stat[%i] %i (Op: %i)", data->iType, data->iValue, data->btOp); + switch( data->iType ) + { + case 2: //Gender + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Sex, data->iValue)) + return QUEST_FAILURE; + //client->CharInfo->Sex = data->iValue; + //BEGINPACKET( pak, 0x720 ); + //ADDWORD( pak, 2 ); + //ADDWORD( pak, client->CharInfo->Sex ); + //ADDWORD( pak, 0 ); + //GServer->SendToVisible( &pak, client ); + //client->client->SendPacket( &pak ); + //RESETPACKET( pak, 0x0730 ); + //ADDWORD( pak, 5 ); + //ADDWORD( pak, 0xa24d ); + //ADDWORD( pak, 0x40b3 ); + //GServer->SendToVisible( &pak, client ); + //client->client->SendPacket( &pak ); + client->quicksave(); + } + break; + case 4: //job + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Job, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + client->quicksave(); + } + break; + case 5: //union + { + if (!OperateValues(data->btOp, (int*)&client->Union_s->unionvar[0], data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + client->quicksave(); + //Log(MSG_DEBUG,"Client Union changed"); + } + break; + case 8: // face + { + if (!OperateValues(data->btOp, (int*)&client->CharInfo->Face, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 8); + ADDWORD(pak, client->CharInfo->Face); + ADDWORD(pak, 0); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + client->quicksave(); + } + break; + case 9: // Hair + { + if (!OperateValues(data->btOp, (int*)&client->CharInfo->Hair, data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x721); + ADDWORD(pak, 9); + ADDWORD(pak, client->CharInfo->Hair); + ADDWORD(pak, 0); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + RESETPACKET(pak, 0x0730); + ADDWORD(pak, 5); + ADDWORD(pak, 0xa24d); + ADDWORD(pak, 0x40b3); + GServer->SendToVisible( &pak, client ); + //client->client->SendPacket(&pak); + client->quicksave(); + } + case 81: // Union points for junon order + case 82: + case 83: // Union points for righteous crusaders + case 84: + case 85: + case 86: + case 87: // Union 7 points used for tracking previous memberships + case 88: + case 89: //kuro-Tejina Union points + case 90: + { + if (!OperateValues(data->btOp, (int*)&client->Union_s->unionvar[data->iType - 80], data->iValue)) + return QUEST_FAILURE; + BEGINPACKET(pak, 0x720 ); + ADDWORD(pak, data->iType ); + ADDDWORD(pak, data->iValue ); + client->client->SendPacket( &pak ); + + if(data->iType == 89) //update the database if the quest just gave KTPoints + { + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + client->Session->KTPoints,client->Session->userid, client->Session->password); + } + } + break; + case 10: //strength + if(!OperateValues(data->btOp, (int*)&client->Attr->Str, data->iValue)) + return QUEST_FAILURE; + break; + case 11: //dexterity + if(!OperateValues(data->btOp, (int*)&client->Attr->Dex, data->iValue)) + return QUEST_FAILURE; + break; + case 12: //inteligence + if(!OperateValues(data->btOp, (int*)&client->Attr->Int, data->iValue)) + return QUEST_FAILURE; + break; + case 13: //concentration + if(!OperateValues(data->btOp, (int*)&client->Attr->Con, data->iValue)) + return QUEST_FAILURE; + break; + case 14: //charm + if(!OperateValues(data->btOp, (int*)&client->Attr->Cha, data->iValue)) + return QUEST_FAILURE; + break; + case 15: //sense + if(!OperateValues(data->btOp, (int*)&client->Attr->Sen, data->iValue)) + return QUEST_FAILURE; + break; + case 30: // set global XP rate + { + GServer->Config.EXP_RATE = data->iValue; + return QUEST_SUCCESS; + } + break; + case 31: //level + if(!OperateValues(data->btOp, (int*)&client->Stats->Level, data->iValue)) + return QUEST_FAILURE; + break; + case 32: // Stat points + if(!OperateValues(data->btOp, (int*)&client->CharInfo->StatPoints, data->iValue)) + return QUEST_FAILURE; + break; + case 37: // Skill points + if(!OperateValues(data->btOp, (int*)&client->CharInfo->SkillPoints, data->iValue)) + return QUEST_FAILURE; + break; + //case sMoney: + //if(!OperateValues(data->btOp, (int*)&client->CharInfo->Zulies, data->iValue)) + //return QUEST_FAILURE; + //break; + case 40: //money + { + if(!OperateValues(data->btOp, (int*)&client->CharInfo->Zulies, data->iValue)) + return QUEST_FAILURE; + // Send zuly update packet + BEGINPACKET(pak, 0x71D); + ADDQWORD( pak, client->CharInfo->Zulies ); + client->client->SendPacket( &pak ); + } + break; + default: + Log(MSG_WARNING, "Type Unknown: '%i'", data->iType); + break; + } + } + return QUEST_SUCCESS; +} + +//Set Quest Variable +QUESTREWD(004) +{ + return QUEST_REWD_002(server, client, raw); +} + + //Give Reward +QUESTREWD(005) +{ + GETREWDDATA(005); + //byte btTarget; // pos 0x00 + //union + //{ + // byte btEquation; // pos 0x02 apparently should be 0x01 + // word nEquation; + //}; + //byte btEquation; // pos 0x01 + //word nEquation; // pos 0x02 + //int iValue; // pos 0x04 + //dword iItemSN; // pos 0x06 + //union + //{ + // byte btPartyOpt; // pos 0x0a + // word nPartyOpt; + //}; + //word nItemOpt; // pos 0x0c + //word nSkill. Co-opting nItemOpt as a skill id + word nSkill = data->nItemOpt; + if( client->questdebug ) + server->SendPM(client, "Quest reward 005 using equation %i", data->btEquation); + switch(data->btTarget) + { + case 0://EXP + { + if( client->questdebug ) + server->SendPM(client, "Give EXP: %i", data->iValue); + client->CharInfo->Exp += GetRewardValue(data->btEquation, data->iValue, client, 0); + BEGINPACKET(pak, 0x79b); + ADDDWORD(pak, client->CharInfo->Exp); + ADDDWORD(pak, client->CharInfo->stamina); + ADDWORD (pak, 0); + client->client->SendPacket(&pak); + } + break; + case 1://Zuly + { + if( client->questdebug ) + server->SendPM(client, "Give Zuly: %i", data->iValue); + unsigned int j = client->CheckQuest; + int itemcount = client->quest.quests[j].Items[0].count; + if( client->questdebug ) + server->SendPM(client, "Items returned: %i", itemcount); + //int Zulyreward = GetRewardValue(data->btEquation, data->iValue, client, itemcount); + // temp fix + int Zulyreward = data->iValue; + if(itemcount > 0 ) //this is for cases where multiple items are handed in for fetch quest. + Zulyreward = data->iValue * itemcount; + client->CharInfo->Zulies += Zulyreward; + //client->CharInfo->Zulies += GetRewardValue(data->btEquation, data->iValue, client, 1);//dunno nDupCount for this one! + BEGINPACKET(pak, 0x71D); + ADDQWORD(pak, client->CharInfo->Zulies); + client->client->SendPacket(&pak); + } + break; + case 2://Item + { + CItem nItem; + nItem.itemtype = data->iItemSN / 1000; + nItem.itemnum = data->iItemSN % 1000; + if(nItem.IsStackable()) + { + nItem.count = GetRewardValue(data->btEquation, data->iValue, client, 0); + } + else + { + nItem.count = 1; + } + if( client->questdebug ) + server->SendPM(client, "Give item [%i][%i]x%i", nItem.itemtype, nItem.itemnum, nItem.count); + nItem.durability = GServer->STB_ITEM[nItem.itemtype-1].rows[nItem.itemnum][29]; + nItem.gem = 0; + nItem.stats = 0; + nItem.refine = 0; + nItem.socketed = 0; + nItem.lifespan = 100; + nItem.appraised = 1; + dword slot = client->AddItem(nItem); + if (slot == 0xffff) // Fail + { + BEGINPACKET( pak, 0x7a7); + ADDWORD(pak, 0x00); + ADDBYTE(pak, 0x03); + ADDBYTE(pak, 0x00); + client->client->SendPacket(&pak); + } + else // Success + { + BEGINPACKET( pak, 0x71f); + ADDBYTE(pak, 0x01); + ADDBYTE(pak, slot); + ADDWORD(pak, client->items[slot].GetPakHeader( ) ); + ADDDWORD(pak, client->items[slot].GetPakData( ) ); + client->client->SendPacket(&pak); + } + } + break; + //PY: This seems a little overcomplicated. we can just send the request directly to AddBuffs() or UseBuffSkill() + /*case 3://Recieve Buff. start battle so that AIP can actually do the buffing based on RefVar values + { + CCharacter* monster = GServer->GetMonsterByID(client->NPCvar,client->Position->Map); + monster->StartAction( client, SKILL_ATTACK, nSkill ); + + } + break; + */ + case 3: + { + //need a CCharacter object first + CSkills* thisskill = GServer->GetSkillByID( nSkill ); + client->UseBuffSkillQSD(client, thisskill); + } + break; + default: + break; + } + return QUEST_SUCCESS; +} + +//Restore HP/MP +QUESTREWD(006) +{ + //dword iPercentOfHP pos 0x00 + //dword iPercentOfMP pos 0x04 + //byte btPartyOpt pos 0x08 + GETREWDDATA(006); + if( client->questdebug ) + server->SendPM(client, "Restore %i%% HP, %i%% MP", data->iPercentOfHP, data->iPercentOfMP); + client->Stats->HP = (long int)((float)client->Stats->MaxHP / 100.0f) * data->iPercentOfHP; + client->Stats->MP = (long int)((float)client->Stats->MaxHP / 100.0f) * data->iPercentOfMP; + return QUEST_SUCCESS; +} + +//Teleport +QUESTREWD(007) +{ + //dword iZoneSN; pos 0x00 + //dword iX; pos 0x04 + //dword iY; pos 0x08 + //byte btPartyOpt; pos 0x0d + GETREWDDATA(007); + fPoint thispoint; + thispoint.x = floor(((float)data->iX)/100); + thispoint.y = floor(((float)data->iY)/100); + if( client->questdebug ) + server->SendPM(client, "Teleport [%i][%f][%f]", data->iZoneSN, thispoint.x, thispoint.y); + GServer->TeleportTo(client, data->iZoneSN, thispoint); + return QUEST_SUCCESS; +} + +//Spawn Monster +QUESTREWD(008) +{ + //dword iMonsterSN pos 0x00 + //dword iHowMany pos 0x04 + // byte btWho pos 0x08 or + // int iWho pos 0x08 + //dword iZoneSN pos 0x0a + //dword iX pos 0x0e + //dword iY pos ox11 + //dword iRange pos 0x15 + //dword iTeamNo pos 0x19 + GETREWDDATA(008); + fPoint position; + dword mapId; + if(data->iX == 0 || data->iY == 0 || data->iZoneSN == 0) + { + position.x = client->Position->current.x; + position.y = client->Position->current.y; + mapId = client->Position->Map; + } + else + { + position.x = data->iX / 100; + position.y = data->iY / 100; + mapId = data->iZoneSN; + } + for(dword i = 0; i < data->iHowMany; i++) + { + fPoint pos = GServer->RandInCircle( position, data->iRange ); + if( client->questdebug ) + server->SendPM(client, "Spawn mob[%i] @ %f, %f", data->iMonsterSN, pos.x, pos.y); + CMap* map = GServer->MapList.Index[mapId]; + CMonster* mon = map->AddMonster( data->iMonsterSN, pos, 0, 0 , 1 ); + if(data->iMonsterSN > 750 && data->iMonsterSN < 755) // ghost rackies = non aggresive + { + mon->thisnpc->aggresive = 0; + } + else + { + mon->thisnpc->aggresive = 999; // Force the mob to be agressive. + } + mon->lastSighCheck = 0; // Force sight check instantly. + } + return QUEST_SUCCESS; +} + +//Execute Quest Trigger +QUESTREWD(009) +{ + //word shNameLen pos 0x00 + //string szNextTriggerSN pos 0x02 + GETREWDDATA(009); + char* tempName = reinterpret_cast(&data->szNextTriggerSN) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, "Execute Quest Trigger %s[%d] [%08x]", tempName, data->shNameLen, hash); + return client->ExecuteQuestTrigger(hash); + //return QUEST_SUCCESS; +} + +//Reset Stats +QUESTREWD(010) +{ + if( client->questdebug ) + server->SendPM(client, "Reset Stats"); + client->CharInfo->StatPoints = 0; + client->Attr->Str = 15; + client->Attr->Dex = 15; + client->Attr->Int = 15; + client->Attr->Con = 15; + client->Attr->Cha = 10; + client->Attr->Sen = 10; + for(int i = 2; i <= client->Stats->Level; i++) + { + client->CharInfo->StatPoints += 10; + client->CharInfo->StatPoints += i / 2; + } + return QUEST_SUCCESS; +} + +//Update Object Var +QUESTREWD(011) +{ + GETREWDDATA(011); + /*if(data->btWho == 0) //npc + { + CMonster* monster = reinterpret_cast(entity); + if(monster == NULL) return QUEST_FAILURE; + if(data->nVarNo > 19) return AI_FAILURE; + short tempval = GServer->ObjVar[monster->thisnpc->refNPC][data->nVarNo]; + switch(data->btOp) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + case 9: + tempval++; + break; + default: + return QUEST_FAILURE; + break; + } + if(tempval < 0)tempval = 0; + GServer->ObjVar[monster->thisnpc->refNPC][data->nVarNo] = tempval; + return QUEST_SUCCESS; + } + else if(data->btWho == 1) // event + { + //need to write the event code still it seems. Seems easy enough + return QUEST_FAILURE; + }*/ + + + /*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + + if(data->btWho == 0){//Npc + CNpc* thisNpc = reinterpret_cast(entity); + thisNpc = thisNpc->SelectedNpc; + if(thisNpc == NULL) return QUEST_FAILURE; + + short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo); + OperateValues(data->btOp, &VarValue, (short)data->iValue); + thisNpc->ObjVar.SetVar(data->nVarNo, VarValue); + }else if(data->btWho == 1){//Event + short VarValue = server->EventVar.GetVar(data->nVarNo); + OperateValues(data->btOp, &VarValue, (short)data->iValue); + server->EventVar.SetVar(data->nVarNo, VarValue); + } + + + return QUEST_SUCCESS;*/ + //byte btWho; pos 0x00 + //word nVarNo; pos 0x02 + //dword iValue; pos 0x04 this was an int but ints only hold 2 bytes so btop would be out of position + //byte btOp; pos 0x08 btop definitely at position 0x08 so had to adjust iValue + int tempval; + if(data->btWho == 0) + { + if(data->nVarNo > 19) return AI_FAILURE; + tempval = GServer->ObjVar[client->NPCvar][data->nVarNo]; + //Log(MSG_INFO,"Just checking. NPCvar = %i", client->NPCvar); + switch( data->btOp ) + { + case 5: + tempval = data->iValue; + break; + case 6: + tempval += data->iValue; + break; + case 7: + tempval -= data->iValue; + break; + case 9: + tempval++; + break; + default: + return QUEST_FAILURE; + break; + } + } + else if(data->btWho == 1) //event + { + // not sure what to do here yet + return QUEST_FAILURE; + } + GServer->ObjVar[client->NPCvar][data->nVarNo] = tempval; + return QUEST_SUCCESS; +} + +//NPC Speak +QUESTREWD(012) +{ + //if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + GETREWDDATA(012); +/* + CNpc* thisNpc = reinterpret_cast(entity); + if(thisNpc->SelectedNpc == NULL) return QUEST_FAILURE; + std::map::iterator triggerITR = server->NpcNames.find(thisNpc->SelectedNpc->NpcID); + if(triggerITR == server->NpcNames.end()) return QUEST_FAILURE; + + char* npcName = triggerITR->second; + char* sayStr = server->lngQst->GetLTBStr(data->iStrID); + if(sayStr == NULL) return QUEST_FAILURE; + if(data->btMsgType == 1){ + //Shout + CPacket pakout(0x785); + pakout.Add(npcName); + pakout.Add(sayStr); + server->SendPacketToZone(thisNpc, &pakout); + }else if(data->btMsgType == 2){ + //Announce + CPacket pakout(0x702); + pakout.AddBytes((byte*)npcName, strlen(npcName)); + pakout.Add('>'); + pakout.Add(sayStr); + server->SendPacketToZone(thisNpc, &pakout); + } + delete [] sayStr;*/ + + dword npcId = 0; + //if(entity->_EntityType == ENTITY_NPC) + //CMonster* thisMonster = reinterpret_cast(entity); + switch(data->btMsgType) + { + case 0: //whisper to client + // don't think this is possible. No way to get a character to send it to?? + break; + case 1: //shout to map + Log(MSG_INFO,"Quest -- NPC Shouts LTB string %i to map %i",data->iStrID,client->Position->Map); + GServer->NPCShout(client->Position->Map,GServer->Ltbstring[data->iStrID].QuestLTB,GServer->Ltbstring[data->iStrID].QuestNpcName); + break; + case 2: //announce to server. + Log(MSG_INFO,"Quest -- NPC announces LTB string %i to map %i",data->iStrID,client->Position->Map); + GServer->NPCAnnounce(GServer->Ltbstring[data->iStrID].QuestLTB,GServer->Ltbstring[data->iStrID].QuestNpcName); + break; + default: + break; + } + + return QUEST_SUCCESS; + // This is cool and all, but we lack the stuff to do it - Drakia + // Not any more - PY +} + +//Execute Quest Trigger with time delay. STILL NEEDS TO BE CODED +QUESTREWD(013) +{ + // byte btWho pos 0x00 + // int iWho pos 0x00 + //dword iSec pos 0x02 + //word nNameLength pos 0x06 + //string szTriggerName pos 0x08 + //dword m_HashNextTrigger pos 0x06 + nNameLength? + //looks like a quest trigger with a timer delay to me + GETREWDDATA(013); + char* tempName = reinterpret_cast(&data->szTriggerName) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, " RWD 013 Execute Quest Trigger %s[%d] [%08x]", tempName, data->nNameLength, hash); + //suppress the actual execution for now just to be sure + return client->ExecuteQuestTrigger(hash); + return QUEST_SUCCESS; +} + +//Learn Skill +QUESTREWD(014) +{ + // byte btOp pos 0x00 + // int iOp pos 0x00 + //dword iSkillNo pos 0x02 + GETREWDDATA(014); + if( client->questdebug ) + server->SendPM(client, "Learn skill: %i", data->iSkillNo); + GServer->LearnSkill(client, data->iSkillNo, false); + return QUEST_SUCCESS; +} + +//Set Quest Flag +QUESTREWD(015) +{ + GETREWDDATA(015); + //word nSN; 0x00 + //byte btOp; 0x02 + if( client->questdebug ) + server->SendPM(client, "Set QFlag[%i]=%i", data->nSN, data->btOp); + client->quest.SetFlag(data->nSN, (data->btOp == 1)?true:false); + // PY method + //client->quest.Qflags[data->nSN] == data->btOp; + // PY end + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(016) +{ + //word nGroupSN pos 0x00 + return QUEST_SUCCESS; +} + +//Reset All Quest Flags +QUESTREWD(017) +{ + if( client->questdebug ) + server->SendPM(client, "Reset all quest flags"); + //memset(&client->quest.flags, 0, 64); + // PY method + for(UINT i=0;i<1000;i++) + { + for(int j=0;j<8;j++) + { + client->quest.Qflags[i][j] = 0; + } + } + // PY end + return QUEST_SUCCESS; +} + +//Send Announcement +QUESTREWD(018) +{ + //dword iStrID pos 0x00 + //word nCnt pos 0x04 + //string Data pos 0x06 + //why isn't this coded? looks simple enough + GETREWDDATA(018); + char* tempName = reinterpret_cast(&data->Data) - 2; + if( client->questdebug ) + server->SendPM(client, " RWD 018 Send announcement %s", tempName); + Log(MSG_INFO,"Announcement to all %s",tempName); + GServer->NPCAnnounce(tempName,"Quest NPC"); + return QUEST_SUCCESS; +} + +//Execute Quest Trigger in Other Map +QUESTREWD(019) +{ + //this is going to be a pain + //it runs a quest trigger that would appear to be sent to every player in the designated zone + + //word nZoneNo; //pos 0x00 + //word nTeamNo; //pos 0x02 + //word nTriggerLength; //pos 0x04 This is definitely NOT the length of the following name. PY + //string TriggerName; //pos 0x06 + nTriggerlength more bytes. name is zero terminated so m_hashtrigger position varies + //dword m_HashTrigger; + //word m_HashTrigger; //pos 0x17 only has 2 bytes not 4. PY + + // let's check the values first + GETREWDDATA(019); + //Log(MSG_DEBUG,"Execute Quest trigger: %s for zone %i. Hash: %i",data->TriggerName, data->nZoneNo, data->m_HashTrigger); + + /*CMap* map = GServer->MapList.Index[data->nZoneNo]; + if(map == NULL) return QUEST_SUCCESS; + if( map->PlayerList.size()<1 ) return QUEST_SUCCESS; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + player->ExecuteQuestTrigger(data->m_HashTrigger); + } + return QUEST_SUCCESS;*/ + + char* tempName = reinterpret_cast(&data->TriggerName) - 2; + dword hash = MakeStrHash(tempName); + CMap* map = GServer->MapList.Index[data->nZoneNo]; + if(map == NULL) return QUEST_SUCCESS; + if( map->PlayerList.size()<1 ) return QUEST_SUCCESS; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + player->ExecuteQuestTrigger(hash); + } + + return QUEST_SUCCESS; +} + +//PvP Status +QUESTREWD(020) +{ + GETREWDDATA(020); + //byte btNoType; pos 0x00 + + return QUEST_SUCCESS; +} + +//Set Respawn Position +QUESTREWD(021) +{ + //dword iX pos 0x00 + //dword iY pos 0x04 + GETREWDDATA(021); + client->Position->saved = client->Position->Map; + return QUEST_SUCCESS; +} + +//Unknown +QUESTREWD(022) +{ + //word nZonNo pos 0x00 + //byte btOp pos 0x02 + return QUEST_SUCCESS; +} + +//Raise Clan Grade - This is not the least bit efficient - Drakia +QUESTREWD(023) +{ + GETREWDDATA(023); + if( client->questdebug ) + server->SendPM(client, "Raise clan grade"); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + continue; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if( player->Clan->clanid != client->Clan->clanid ) continue; + player->Clan->grade++; + + //load clan info in char server + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0xfb ); //action to update clan informacion (charserver) + ADDWORD ( pak, player->Clan->clanid ); + ADDWORD ( pak, player->Clan->grade ); + GServer->SendISCPacket( &pak ); + + //Send to other players + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, player->clientid ); + ADDWORD ( pak, player->Clan->clanid); + ADDWORD ( pak, 0x0000 );//??? + ADDWORD ( pak, player->Clan->back ); + ADDWORD ( pak, player->Clan->logo ); + ADDBYTE ( pak, player->Clan->grade );//clan grade + ADDBYTE ( pak, 0x06 );//clan rank + ADDSTRING ( pak, player->Clan->clanname ); + ADDBYTE ( pak, 0x00 ); + GServer->SendToVisible( &pak, player ); + } + } + GServer->DB->QExecute("UPDATE list_clan SET grade=%i WHERE id=%i", client->Clan->grade, client->Clan->clanid); + return QUEST_SUCCESS; +} + +//Clan Money +QUESTREWD(024) +{ + //int iMONEY pos 0x00 + //byte btOP pos 0x02 + GETREWDDATA(024); + return QUEST_SUCCESS; +} + +//Clan Points +QUESTREWD(025) +{ + //short nPOINT pos 0x00 + //byte btOP pos 0x02 + GETREWDDATA(025); + int points = data->nPOINT; + //this whole function appears to be screwed up. Fix later + /* + + + //Loading "real" CP points. + //getClanPoints function is mysteriously missing so skip it for now + //client->Clan->CP = GServer->getClanPoints(client->Clan->clanid); + + //we "delete" points. + if(data->btOP == 7) + { + points =- 1 * points; + } + + if (client->Clan->CP + points < 0) + { + client->Clan->CP = 0; + } + else + { + client->Clan->CP += points; + } + + //sending packet. + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0xfe ); + ADDDWORD ( pak, client->CharInfo->charid); //charid + ADDDWORD ( pak, points); //Clan points (to be added) + cryptPacket( (char*)&pak, GServer->cct ); + send( GServer->csock, (char*)&pak, pak.Size, 0 ); + + Log(MSG_WARNING,"We added %i CP",points); + + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x35 ); + ADDWORD ( pak, client->clientid ); + ADDWORD ( pak, client->Clan->clanid ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, client->Clan->back ); + ADDWORD ( pak, client->Clan->logo ); + ADDBYTE ( pak, client->Clan->grade ); + ADDBYTE ( pak, client->Clan->clanrank); + ADDSTRING ( pak, client->Clan->clanname ); + ADDBYTE ( pak, 0x00 ); + client->client->SendPacket(&pak); + + */ + return QUEST_SUCCESS; +} + +//Clan Skill +QUESTREWD(026) +{ + GETREWDDATA(026); + //word nSkillNo; pos 0x00 + //byte btOP; pos 0x02 + //do something (based on btOp) with a clan skill id = data->nSkillNo + if( client->questdebug )server->SendPM(client, "Clan skill rwd[026] skill id: %i btOP: %i",data->nSkillNo,data->btOP); + return QUEST_SUCCESS; +} + +//Clan Contribution +QUESTREWD(027) +{ + GETREWDDATA(027); + //short nCONT; pos 0x00 + //byte btOP; pos 0x02 + if( client->questdebug )server->SendPM(client, "Clan Contribution rwd[026] nCONT: %i btOP: %i",data->nCONT,data->btOP); + + return QUEST_SUCCESS; +} + +//Clan Teleportation - Again, not efficient. Maybe keep a list of pointers to clan members? - Drakia +QUESTREWD(028) +{ + //dword iRange; pos 0x00 + //word nZoneNo; pos 0x04 + //dword iX; pos 0x06 + //dword iY; pos 0x0a + GETREWDDATA(028); + fPoint telepos; + fPoint newPos; + telepos.x = data->iX/100; + telepos.y = data->iY/100; + if( client->questdebug ) + server->SendPM(client, "Clan teleport to [%i][%f][%f]", data->nZoneNo, telepos.x, telepos.y); + for(UINT i=0;iMapList.Map.size();i++) + { + CMap* map = GServer->MapList.Map.at(i); + if( map->PlayerList.size()<1 ) + continue; + for(UINT j=0;jPlayerList.size();j++) + { + CPlayer* player = map->PlayerList.at(j); + if( player->Clan->clanid != client->Clan->clanid ) continue; + newPos = GServer->RandInCircle(telepos, data->iRange); + GServer->TeleportTo(player, data->nZoneNo, newPos ); + } + } + return QUEST_SUCCESS; +} + +QUESTREWD(029) //execute client quest trigger (LUA) +{ + GETREWDDATA(029); + char* tempName = reinterpret_cast(&data->LuaName) - 2; + dword hash = MakeStrHash(tempName); + if( client->questdebug ) + server->SendPM(client, "Execute Client LUA %s[%d] [%08x]", tempName, data->strLen, hash); + //return client->ExecuteQuestTrigger(hash); + //need to code a packet here. on evo it's 0x7f8. Don't even know if we use one here + Log(MSG_DEBUG,"ran quest action 29"); + return QUEST_SUCCESS; +} + +QUESTREWD(030) //Reset skills +{ + //Log(MSG_DEBUG,"ran quest action 30"); + for(int i=0;i<30;i++) + { + //give one sp for every active skill present + if(client->askill[i] != 0) + { + client->CharInfo->SkillPoints += 1; + } + client->askill[i] = 0; + //give SP equal to the level of the active skill. Actual level is always 1 more than this + client->CharInfo->SkillPoints += client->askilllvl[i]; + client->askilllvl[i] = 0; + //give one sp for every passive skill present + if(client->pskill[i] != 0) + { + client->pskill[i] = 0; + client->CharInfo->SkillPoints += 1; + } + //give SP equal to the level of the passive skill. Actual level is always 1 more than this + client->CharInfo->SkillPoints += client->pskilllvl[i]; + client->pskilllvl[i] = 0; + } + client->savedata(); + return QUEST_SUCCESS; +} +QUESTREWD(050) //Respawn NPC after time delay. Used only on the missing children in Zant +{ + return QUEST_SUCCESS; +} diff --git a/Src/World Server/quest/QuestActions.h b/Src/World Server/quest/QuestActions.h new file mode 100644 index 0000000..cd0fe28 --- /dev/null +++ b/Src/World Server/quest/QuestActions.h @@ -0,0 +1,242 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#pragma once +#define QUESTREWD(reward) int QUEST_REWD_ ## reward (class CWorldServer* server, class CPlayer* client, byte* raw) +typedef int (*fpQuestRewd)(class CWorldServer*, class CPlayer*, byte*); +#define GETREWDDATA(rewd) STR_REWD_ ## rewd * data = (STR_REWD_ ## rewd *)raw + +QUESTREWD(000);QUESTREWD(001);QUESTREWD(002); +QUESTREWD(003);QUESTREWD(004);QUESTREWD(005); +QUESTREWD(006);QUESTREWD(007);QUESTREWD(008); +QUESTREWD(009);QUESTREWD(010);QUESTREWD(011); +QUESTREWD(012);QUESTREWD(013);QUESTREWD(014); +QUESTREWD(015);QUESTREWD(016);QUESTREWD(017); +QUESTREWD(018);QUESTREWD(019);QUESTREWD(020); +QUESTREWD(021);QUESTREWD(022);QUESTREWD(023); +QUESTREWD(024);QUESTREWD(025);QUESTREWD(026); +QUESTREWD(027);QUESTREWD(028);QUESTREWD(029); +QUESTREWD(030);QUESTREWD(050); + +struct STR_REWD_000 { + dword iQuestSN; + byte btOp; +}; + +struct STR_REWD_001 { + dword uiItemSN; + union { + byte btOp; + word nOp; + }; + word nDupCNT; + byte btPartyOpt; +}; + +struct STR_REWD_002 +{ + dword iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_REWD_003 +{ + dword iDataCnt; //pos 0x00 + dword iType; //pos 0x04 + dword iValue; //pos 0x08 + byte btOp; //pos 0x0c + //this (below original code) is just plain wrong + //dword iDataCnt; + //STR_ABIL_DATA* CheckData; + //byte btPartyOpt; +}; + +struct STR_REWD_004 { + dword iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_REWD_005 +{ + byte btTarget; // pos 0x00 + //union + //{ + // byte btEquation; // pos 0x02 apparently should be 0x01 + // word nEquation; + //}; + byte btEquation; // pos 0x01 + word nEquation; // pos 0x02 + int iValue; // pos 0x04 + dword iItemSN; // pos 0x06 + union + { + byte btPartyOpt; // pos 0x0a + word nPartyOpt; + }; + word nItemOpt; // pos 0x0c +}; + +struct STR_REWD_006 { + dword iPercentOfHP; + dword iPercentOfMP; + byte btPartyOpt; +}; + +struct STR_REWD_007 { + dword iZoneSN; + dword iX; + dword iY; + byte btPartyOpt; +}; + +struct STR_REWD_008 { + dword iMonsterSN; + dword iHowMany; + union { + byte btWho; + int iWho; + }; + dword iZoneSN; + dword iX; + dword iY; + dword iRange; + dword iTeamNo; +}; + +struct STR_REWD_009 { + word shNameLen; + string szNextTriggerSN; +}; + +struct STR_REWD_010 { +}; + +struct STR_REWD_011 +{ + union + { + byte btWho; + word usWho; + }; + word nVarNo; + dword iValue; + byte btOp; +}; + +struct STR_REWD_012 { + union + { + byte btMsgType; + int iMsgType; + }; + dword iStrID; +}; + +struct STR_REWD_013 { + union { + byte btWho; + int iWho; + }; + dword iSec; + word nNameLength; + string szTriggerName; + dword m_HashNextTrigger; +}; + +struct STR_REWD_014 +{ + union + { + byte btOp; + int iOp; + }; + dword iSkillNo; +}; + +struct STR_REWD_015 { + word nSN; + byte btOp; +}; + +struct STR_REWD_016 { + word nGroupSN; +}; + +struct STR_REWD_017 { +}; + +struct STR_REWD_018 { + dword iStrID; + word nCnt; + string Data; +}; + +struct STR_REWD_019 +{ + word nZoneNo; //pos 0x00 + word nTeamNo; //pos 0x02 + word nTriggerLength; //pos 0x04 This is definitely NOT the length of the following name. PY + string TriggerName; //pos 0x06 + nTriggerlength more bytes. Name always appears to be 17 bytes long + //dword m_HashTrigger; + word m_HashTrigger; //pos 0x17 only has 2 bytes not 4. PY +}; + +struct STR_REWD_020 +{ + byte btNoType; +}; + +struct STR_REWD_021 { + dword iX; + dword iY; +}; + +struct STR_REWD_022 { + word nZonNo; + byte btOp; +}; + +struct STR_REWD_023 +{ +}; + +struct STR_REWD_024 { + int iMONEY; + byte btOP; +}; + +struct STR_REWD_025 { + short nPOINT; + byte btOP; +}; + +struct STR_REWD_026 { + word nSkillNo; + byte btOP; +}; + +struct STR_REWD_027 { + short nCONT; + byte btOP; +}; + +struct STR_REWD_028 { + dword iRange; + word nZoneNo; + dword iX; + dword iY; +}; + +struct STR_REWD_029 { + word strLen; + string LuaName; +}; + +struct STR_REWD_030 //reset skills +{ + +}; + +struct STR_REWD_050 //respawn NPC after time delay +{ + +}; + diff --git a/Src/World Server/quest/QuestConditions.cpp b/Src/World Server/quest/QuestConditions.cpp new file mode 100644 index 0000000..e417033 --- /dev/null +++ b/Src/World Server/quest/QuestConditions.cpp @@ -0,0 +1,771 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#include "../WorldServer.h" + +//Check Quest +QUESTCOND(000) +{ + GETCONDDATA(000); + if( client->questdebug ) + server->SendPM(client, "Check quest: %i", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID == data->iQuestSN) + { + client->CheckQuest = i; + client->ActiveQuest = data->iQuestSN; + if( client->questdebug ) + server->SendPM(client, "Check Quest - Found!"); + return QUEST_SUCCESS; + } + } + if( client->questdebug ) + server->SendPM(client, "Check Quest - Not Found!"); + return QUEST_FAILURE; +} + +//Check Quest Variable +QUESTCOND(001) +{ + // int iDataCnt pos 0x00 + // dword iType pos 0x02 + // short m_wVarNO pos 0x02 + // word m_wVarTYPE pos 0x04 + // word nValue pos 0x06 + // byte btOp pos 0x08 + GETCONDDATA(001); + int retVal = 1; + if( client->questdebug )server->SendPM(client, "Data Count for this quest %i",data->iDataCnt); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * sizeof(STR_QUEST_DATA); + address += (dword)data; + address += 4; + STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address; + word nValue = curQst->nValue; + word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO); + //word tempValue = client->quest.quests[curQst->m_wVarTYPE].Variables[curQst->m_wVarNO]; + //retVal = OperateValues(curQst->btOp, &tempValue, curQst->nValue); + switch(curQst->btOp) + { + case 0: + retVal = (tempValue == curQst->nValue); + break; + case 1: + retVal = (tempValue > curQst->nValue); + break; + case 2: + retVal = (tempValue >= curQst->nValue); + break; + case 3: + retVal = (tempValue < curQst->nValue); + break; + case 4: + retVal = (tempValue <= curQst->nValue); + break; + case 10: + retVal = (tempValue != curQst->nValue); + break; + default: + retVal = 0; + Log( MSG_DEBUG, "QuestCond 001 bad value for btOp %i",curQst->btOp ); + break; + } + if( client->questdebug ) + { + server->SendPM(client, "is %i < %i ? retval %i",tempValue, curQst->nValue, retVal); + server->SendPM(client, "Check Quest Var[%04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp); + } + if(retVal == 0) return QUEST_FAILURE; + } + return (retVal == 0)?QUEST_FAILURE:QUEST_SUCCESS; +} + +//Check Quest Variable +QUESTCOND(002) +{ + return QUEST_COND_001(server, client, raw); +} + +//Check Stats +QUESTCOND(003) +{ + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 + //dword iValue; pos 0x08 + //byte btOp; pos 0x0c + GETCONDDATA(003); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * 0x0C; + address += (dword)data; + address += 4; + STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Check Stat[%i] %i (Op: %i)", curAbil->iType, curAbil->iValue, curAbil->btOp); + int tempValue; + switch( curAbil->iType ) + { + case sGender: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->Sex, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sJob: + tempValue = client->CharInfo->Job / 100; + if( client->questdebug ) + server->SendPM(client, "Job Tempvalue = %i", tempValue); + //if(!OperateValues(curAbil->btOp, (int*)&tempValue, curAbil->iValue)) + if(!OperateValues(curAbil->btOp, &tempValue, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sUnion: // Unions aren't implemented? - Drakia + if(!OperateValues(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue)) + return QUEST_FAILURE; + break; + case sStrength: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sDexterity: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sIntelligence: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sConcentration: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sCharm: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sSensibility: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sLevel: + if(!OperateValues(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sStatPoints: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sMoney: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sEXPRate: + if(!OperateValues(curAbil->btOp, (int*)&client->Stats->xprate, curAbil->iValue)) + return QUEST_FAILURE; + break; + default: + Log(MSG_WARNING, "QuestCond 003: Type Unknown: '%i'", curAbil->iType); + break; + } + } + return QUEST_SUCCESS; +} + +//Check Items +QUESTCOND(004) +{ + GETCONDDATA(004); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * 0x10; + address += (dword)data; + address += 4; + STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Check Item - [Slot:%i][Type:%i]", curItem->iWhere, curItem->uiItemSN); + // Check quest items for a specific amount + if (curItem->iWhere == 13) // it's a quest item + { + // Ok, check the current Check'd quest. + if (client->CheckQuest < 0) return QUEST_SUCCESS; + unsigned int j = client->CheckQuest; + bool Hasitem = false; + bool FoundItem = false; + for (unsigned int k = 0; k < 5; k++) //5 quest item slots + { + if (client->quest.quests[j].Items[k].itemnum == (curItem->uiItemSN % 1000)) + { + FoundItem = true; + if( client->questdebug ) + server->SendPM(client, "Operate - btOp: %i Val1: %i Val2: %i", curItem->btOp, client->quest.quests[j].Items[k].count, curItem->iRequestCnt); + if(OperateValues(curItem->btOp, (int*)(&client->quest.quests[j].Items[k].count), curItem->iRequestCnt)) + Hasitem = true; + } + } + if(FoundItem == false) // didn't find any of the item in any of the quest slots + { + for (unsigned int k = 0; k < 5; k++) //Find the first empty slot + { + if (client->quest.quests[j].Items[k].count == 0 && Hasitem == false) + { + // Since the item doesn't exist, treat it as if there's 0 of them. + int tempInt = 0; + if(OperateValues(curItem->btOp, &tempInt, curItem->iRequestCnt)) + Hasitem = true; + if( client->questdebug ) + server->SendPM(client, "Requested item is not found. Comparing first empty slot instead"); + } + } + } + if(Hasitem == false) + { + if( client->questdebug ) + server->SendPM(client, "Requested item is not present. Returning quest failure"); + return QUEST_FAILURE; + } + } + else + { + //item slot is not 13 + // Check equipped items for a specific item? - Drakia + if(curItem->uiItemSN == 0 && curItem->iWhere != 0) + { + int itemHead = client->items[curItem->iWhere].itemtype; + if(!OperateValues(curItem->btOp, &itemHead, curItem->uiItemSN)) + return QUEST_FAILURE; + } + } + } + if( client->questdebug ) + server->SendPM(client, "QuestCond 004 Returning Quest Success"); + return QUEST_SUCCESS; +} + +//Check Party +QUESTCOND(005) +{ + //byte btIsLeader pos 0x00 + //int iLevel pos 0x02 + //byte btReversed pos 0x04 + GETCONDDATA(005); + return QUEST_SUCCESS; +} + +//Near Point +QUESTCOND(006) +{ + //int iZoneSN; pos 0x00 + //int iX; pos 0x04 + //int iY; pos 0x08 + //int iZ; pos 0x0c + //int iRadius; pos 0x10 + GETCONDDATA(006); + client->UpdatePosition(); + if(data->iZoneSN != 0) + { + if(client->Position->Map != data->iZoneSN) + return QUEST_FAILURE; + } + float dx = client->Position->current.x - (float)(data->iX / 100); + float dy = client->Position->current.y - (float)(data->iY / 100); + float distance = sqrt((dx*dx) + (dy*dy)); + return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE; +} + +//World Time +QUESTCOND(007) +{ + //dword ulTime pos 0x00 + //dword ulEndTime pos 0x04 + return QUEST_SUCCESS; +} + +//Quest Time +QUESTCOND(008) +{ + //dword ulTime pos 0x00 + //byte btOp pos 0x04 + GETCONDDATA(008); + if( client->questdebug ) + server->SendPM(client, "QuestTime - ulTime: %i btOp: %i", data->ulTime, data->btOp); + if (client->CheckQuest < 0) return QUEST_FAILURE; // Not checking a quest, don't return success + long int Time = 0; + if (server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] > 0) + { + Time += client->quest.quests[client->CheckQuest].StartTime; // Start time + Time += server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] * 10; // Time to finish + Time -= time(NULL); // Current time + if (Time < 0) Time = 0; // Time is up + Time /= 10; // Divide to get 10's of seconds + } + if (!OperateValues(data->btOp, (dword*)&Time, data->ulTime)) + return QUEST_FAILURE; + else + return QUEST_SUCCESS; +} + +//Check Skill +QUESTCOND(009) +{ + //int iSkillSN1 pos 0x00 + //int iSkillSN2 pos 0x02 + //byte btOp pos 0x04 + GETCONDDATA(009); + // Line not needed, never used - Drakia + //int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE; + if( client->questdebug ) + server->SendPM(client, "Check Skill: %i", data->iSkillSN1); + bool s1 = false; + bool s2 = false; + for(dword i = 0; i < 30; i++) + { + if(client->bskills[i] == data->iSkillSN1) + s1 = true; + if(client->bskills[i] == data->iSkillSN2) + s2 = true; + if(client->askill[i] == data->iSkillSN1) + s1 = true; + if(client->askill[i] == data->iSkillSN2) + s2 = true; + if(client->pskill[i] == data->iSkillSN1) + s1 = true; + if(client->pskill[i] == data->iSkillSN2) + s2 = true; + } + if(data->btOp == 0) + { + if(s1 == false && s2 == false) + return QUEST_SUCCESS; //true if player does not have either skill + else + return QUEST_FAILURE; + } + else + { + if(s1 == true || s2 == true) + return QUEST_SUCCESS; //true if player has either skill + else + return QUEST_FAILURE; + } + //Log(MSG_DEBUG,"Questcond 009 btOp is neither 0 nor 1."); + //return QUEST_FAILURE; +} + +//Unknown +QUESTCOND(010) +{ + GETCONDDATA(010); + //byte btLowPcnt; pos 0x00 + //byte btHighPcnt; pos 0x01 + // I think this is a percentage chance range. + // might be something to do with the item count though??? + + //int randv = GServer->RandNumber(0,100); + //if(randv >= data->btLowPcnt && randv <= data->btHighPcnt)return QUEST_SUCCESS; + + // problem is that this is only called in quest triggers from the client where it has already + // passed the random number test. + // we need to always return true or some quests will never finish + return QUEST_SUCCESS; +} + +//Object Variable +QUESTCOND(011) +{ + + //byte btWho pos 0x00 + //word nVarNo pos 0x02 + //int iValue pos 0x04 + //byte btOp pos 0x09 + //if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + GETCONDDATA(011); + /* + if(data->btWho == 0) + { + CNpc* thisNpc = reinterpret_cast(entity); + thisNpc = thisNpc->SelectedNpc; + if(thisNpc == NULL) return QUEST_FAILURE; + + short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo); + if(OperateValues(data->btOp, &VarValue, (short)data->iValue)) return QUEST_SUCCESS; + }*/ + if(data->btWho == 0) + { + + if(data->nVarNo > 19) return AI_FAILURE; + int refVal = GServer->ObjVar[client->NPCvar][data->nVarNo]; + int tempval = 0; + switch( data->btOp ) + { + case 0: + if(refVal == data->iValue)return QUEST_SUCCESS; + break; + case 1: + if(refVal > data->iValue)return QUEST_SUCCESS; + break; + case 2: + if(refVal >= data->iValue)return QUEST_SUCCESS; + break; + case 3: + if(refVal < data->iValue)return QUEST_SUCCESS; + break; + case 4: + if(refVal <= data->iValue)return QUEST_SUCCESS; + break; + case 10: + if(refVal != data->iValue)return QUEST_SUCCESS; + break; + default: + Log(MSG_WARNING,"Unknown btOp in QuestCond 011"); + break; + } + } + return QUEST_FAILURE; +} + +//check Proximity and Trigger in Zone +QUESTCOND(012) +{ + GETCONDDATA(012); + //word iZone; pos 0x00 + //dword ulZone; pos 0x00 + //int iX; pos 0x04 + //int iY; pos 0x06 + //int iEventID; pos 0x08 + //dword m_HashEventOBJ; pos 0x08 + //this should be easy enough - PY + + return QUEST_SUCCESS; +} + +//Select NPC +QUESTCOND(013) +{ + //int iNpcNo pos 0x00 + GETCONDDATA(013); + // dammit!!!! This darn function is set up to use the CCharacter class so I can't use entity (CCharacter) + // This is going to be a serious pain in the ass + // OK so we now have a temporary player structure so we shuold be able to do this. + + //Log(MSG_DEBUG,"Setting NPC ref number AICOND(017). NPC Ref = %i",data->iNpcNo); + //CMonster* monster = reinterpret_cast(entity); + //if(monster == NULL) return AI_FAILURE; + //monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar + //Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC); + //not sure this is going to work properly for an NPC. + //NPC only exists as a temporary entity so refNPC may be lost after it returns from here. + + + /*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + + CNpc* thisNpc = reinterpret_cast(entity); + std::map::iterator triggerITR = server->NpcList.find(data->iNpcNo); + if(triggerITR == server->NpcList.end()){ + thisNpc->SelectedNpc = NULL; + return QUEST_FAILURE; + } + thisNpc->SelectedNpc = triggerITR->second; + */ + client->NPCvar = data->iNpcNo; + //Actually that was kind of easy.. lol - PY + + //return QUEST_SUCCESS; + // More NPC specific stuff + return QUEST_SUCCESS; +} + +//Check Quest Flag +QUESTCOND(014) +{ + GETCONDDATA(014); + //word nSN; pos 0x00 + //byte btOp; pos 0x02 + bool dataOp = (data->btOp)?true:false; + if( client->questdebug ) + server->SendPM(client, "Check QFlag[%i] == %i", data->nSN, data->btOp); + if(client->quest.GetFlag(data->nSN) == dataOp) + { + if( client->questdebug ) + server->SendPM(client, "Quest cond 014 (check quest flag) returned Success"); + return QUEST_SUCCESS; + } + else + { + if( client->questdebug ) + server->SendPM(client, "Quest cond 014 (check quest flag) returned Failure"); + return QUEST_FAILURE; + } +} + +//Party member count between X and Y - PY +QUESTCOND(015) +{ + + //word nNumber1 pos 0x00 + //word nNumber2 pos 0x02 + GETCONDDATA(015); + + + return QUEST_SUCCESS; +} + +//Zone Time +QUESTCOND(016) +{ + + //byte btWho pos 0x00 + //dword ulWho pos 0x00 + //dword ulTime pos 0x04 + //dword ulEndTime pos 0x08 + GETCONDDATA(016); + + return QUEST_SUCCESS; +} + +//NPC & Obj Variables? +QUESTCOND(017) +{ + //dword iNpcNo pos 0x00 + //word nVarNo pos 0x04 + //word unused pos 0x06 + //dword iNpcNo pos 0x08 + //word nVarNo pos 0x0c + //word unused pos 0x0e + //byte btOp pos 0x0f + return QUEST_SUCCESS; +} + +//Time on Date +QUESTCOND(018) +{ + //Returns SUCCESS if the system time of the server is between the defined start and end times. Else returns FAILURE. + //Allows for time dependent quests + //byte btDate; pos 0x00 + //byte btHour1; pos 0x01 + //byte btMin1; pos 0x02 + //byte btHour2; pos 0x03 + //byte btMin2; pos 0x04 + GETCONDDATA(018); + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + + if(data->btDate != 0) + { + if(sTIME.wDay != data->btDate) + return QUEST_FAILURE; + } + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin >= wFrom && wMin <= wTo) + return QUEST_SUCCESS; + return QUEST_FAILURE; +} + +//Time on Day +QUESTCOND(019) +{ + //byte btWeekDay; pos 0x00 + //byte btHour1; pos 0x01 + //byte btMin1; pos 0x02 + //byte btHour2; pos 0x03 + //byte btMin2; pos 0x04 + GETCONDDATA(019); + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + if(data->btWeekDay != 0) + { + if(sTIME.wDayOfWeek != data->btWeekDay) + return QUEST_FAILURE; + } + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin >= wFrom && wMin <= wTo) + return QUEST_SUCCESS; + return QUEST_FAILURE; +} + +//Unknown +QUESTCOND(020) +{ + //int iNo1 pos 0x00 + //int iNo2 pos 0x02 + GETCONDDATA(020); + return QUEST_SUCCESS; +} + +//Unknown +QUESTCOND(021) +{ + // byte btSelObjType pos 0x00 + // byte iRadius pos 0x04 + return QUEST_SUCCESS; +} + +//Check Server/Channel +QUESTCOND(022) +{ + //word nX pos 0x00 + //word nY pos 0x02 + return QUEST_SUCCESS; +} + +//In Clan? +QUESTCOND(023) +{ + //byte btReg pos 0x00 + GETCONDDATA(023); + int Clanid = client->Clan->clanid; + if(data->btReg == 0) + { + if(Clanid == 0)return QUEST_SUCCESS; + else return QUEST_FAILURE; + } + else + { + if(Clanid != 0)return QUEST_SUCCESS; + else return QUEST_FAILURE; + } + //btReg should only ever be 1 or 0 but just in case...... + //return QUEST_FAILURE; +} + +//Clan Position +QUESTCOND(024) +{ + //check my current rank in the clan against data->nPOS using btOP + GETCONDDATA(024); + //word nPOS; pos 0x00 + //byte btOP; pos 0x02 + int myRank = client->Clan->clanrank; + switch(data->btOP) + { + case 0: + if(myRank == data->nPOS) + return QUEST_SUCCESS; + break; + case 1: + if(myRank > data->nPOS) + return QUEST_SUCCESS; + break; + case 2: + if(myRank >= data->nPOS) + return QUEST_SUCCESS; + break; + case 3: + if(myRank < data->nPOS) + return QUEST_SUCCESS; + break; + case 4: + if(myRank <= data->nPOS) + return QUEST_SUCCESS; + break; + case 10: + if(myRank != data->nPOS) + return QUEST_SUCCESS; + break; + default: + return QUEST_FAILURE; + break; + } +} + +//Clan Contribution +QUESTCOND(025) +{ + //word nCONT pos 0x00 + //byte btOP pos 0x02 + return QUEST_SUCCESS; +} + +//Clan Grade +QUESTCOND(026) +{ + //possibly compare client's current clan grade with data->nGRD using data->btOp? + GETCONDDATA(026); + //word nGRD; + //byte btOP; + int myGrade = client->Clan->grade; + switch(data->btOP) + { + case 0: + if(myGrade == data->nGRD) + return QUEST_SUCCESS; + break; + case 1: + if(myGrade > data->nGRD) + return QUEST_SUCCESS; + break; + case 2: + if(myGrade >= data->nGRD) + return QUEST_SUCCESS; + break; + case 3: + if(myGrade < data->nGRD) + return QUEST_SUCCESS; + break; + case 4: + if(myGrade <= data->nGRD) + return QUEST_SUCCESS; + break; + case 10: + if(myGrade != data->nGRD) + return QUEST_SUCCESS; + break; + default: + return QUEST_FAILURE; + break; + } +} + +//Clan Points +QUESTCOND(027) +{ + //word nPOINT pos 0x00 + //byte btOP pos 0x02 + GETCONDDATA(027); + if( client->questdebug ) + server->SendPM(client, "QUEST - Clan Points"); + switch(data->btOP) + { + case 0x01: + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x5C ); //action to update clan points (charserver) + ADDWORD ( pak, client->Clan->clanid); + ADDWORD ( pak, client->clientid ); + ADDDWORD ( pak, client->Clan->CP ); + ADDWORD ( pak, 0x00); + GServer->SendISCPacket( &pak ); + break; + //default: + //break; + } + return QUEST_SUCCESS; +} + +//Clan Funds +QUESTCOND(028) +{ + //word iMONEY; pos 0x00 + //byte btOP; pos 0x02 + GETCONDDATA(027); + return QUEST_SUCCESS; +} + +//Clan Members +QUESTCOND(029) +{ + //word nMemberCNT; pos 0x00 + //byte btOP; pos 0x02 + return QUEST_SUCCESS; +} + +//Clan Skills +QUESTCOND(030) +{ + //word nSkill1; pos 0x00 + //word nSkill2; pos 0x02 + //byte btOP; pos 0x05 + GETCONDDATA(030); + //not sure what to do here. Looks like we compare something with two skill ids + if( client->questdebug )server->SendPM(client, "Clan skills. Skill1: %i Skill2 %i btOP: %i",data->nSkill1,data->nSkill2,data->btOP); + return QUEST_SUCCESS; +} diff --git a/Src/World Server/quest/QuestConditions.cpp.wrong b/Src/World Server/quest/QuestConditions.cpp.wrong new file mode 100644 index 0000000..5a06665 --- /dev/null +++ b/Src/World Server/quest/QuestConditions.cpp.wrong @@ -0,0 +1,763 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#include "../WorldServer.h" + +//Check Quest +QUESTCOND(000) +{ + GETCONDDATA(000); + if( client->questdebug ) + server->SendPM(client, "Check quest: %i", data->iQuestSN); + for(dword i = 0; i < 10; i++) + { + if(client->quest.quests[i].QuestID == data->iQuestSN) + { + client->CheckQuest = i; + client->ActiveQuest = data->iQuestSN; + if( client->questdebug ) + server->SendPM(client, "Check Quest # %i Found!", data->iQuestSN); + return QUEST_SUCCESS; + } + } + if( client->questdebug ) + server->SendPM(client, "Check Quest - Not Found!"); + return QUEST_FAILURE; +} + +//Check Quest Variable +QUESTCOND(001) +{ + // int iDataCnt pos 0x00 + // dword iType pos 0x02 + // short m_wVarNO pos 0x02 + // word m_wVarTYPE pos 0x04 + // word nValue pos 0x06 + // byte btOp pos 0x08 + GETCONDDATA(001); + int retVal = 1; + if( client->questdebug )server->SendPM(client, "Data Count for this quest %i",data->iDataCnt); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * sizeof(STR_QUEST_DATA); + address += (dword)data; + address += 4; + STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address; + word nValue = curQst->nValue; + word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO); + //word tempValue = client->quest.quests[curQst->m_wVarTYPE].Variables[curQst->m_wVarNO]; + //retVal = OperateValues(curQst->btOp, &tempValue, curQst->nValue); + switch(curQst->btOp) + { + case 0: + retVal = (tempValue == curQst->nValue); + break; + case 1: + retVal = (tempValue > curQst->nValue); + break; + case 2: + retVal = (tempValue >= curQst->nValue); + break; + case 3: + retVal = (tempValue < curQst->nValue); + break; + case 4: + retVal = (tempValue <= curQst->nValue); + break; + case 10: + retVal = (tempValue != curQst->nValue); + break; + default: + retVal = 0; + Log( MSG_DEBUG, "QuestCond 001 bad value for btOp %i",curQst->btOp ); + break; + } + if( client->questdebug ) + { + server->SendPM(client, "is %i < %i ? retval %i",tempValue, curQst->nValue, retVal); + server->SendPM(client, "Check Quest Var[%04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp); + } + if(retVal == 0) return QUEST_FAILURE; + } + return (retVal == 0)?QUEST_FAILURE:QUEST_SUCCESS; +} + +//Check Quest Variable +QUESTCOND(002) +{ + return QUEST_COND_001(server, client, raw); +} + +//Check Stats +QUESTCOND(003) +{ + //dword iDataCnt; pos 0x00 + //dword iType; pos 0x04 + //dword iValue; pos 0x08 + //byte btOp; pos 0x0c + GETCONDDATA(003); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * 0x0C; + address += (dword)data; + address += 4; + STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Check Stat[%i] %i (Op: %i)", curAbil->iType, curAbil->iValue, curAbil->btOp); + int tempValue; + switch( curAbil->iType ) + { + case sGender: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->Sex, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sJob: + tempValue = client->CharInfo->Job / 100; + if( client->questdebug ) + server->SendPM(client, "Job Tempvalue = %i", tempValue); + //if(!OperateValues(curAbil->btOp, (int*)&tempValue, curAbil->iValue)) + if(!OperateValues(curAbil->btOp, &tempValue, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sUnion: // Unions aren't implemented? - Drakia + if(!OperateValues(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue)) + return QUEST_FAILURE; + break; + case sStrength: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sDexterity: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sIntelligence: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sConcentration: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sCharm: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sSensibility: + if(!OperateValues(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sLevel: + if(!OperateValues(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sStatPoints: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sMoney: + if(!OperateValues(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue)) + return QUEST_FAILURE; + break; + case sEXPRate: + if(!OperateValues(curAbil->btOp, (int*)&client->Stats->xprate, curAbil->iValue)) + return QUEST_FAILURE; + break; + default: + Log(MSG_WARNING, "QuestCond 003: Type Unknown: '%i'", curAbil->iType); + break; + } + } + return QUEST_SUCCESS; +} + +//Check Items +QUESTCOND(004) +{ + GETCONDDATA(004); + for(int i = 0; i < data->iDataCnt; i++) + { + dword address = i * 0x10; + address += (dword)data; + address += 4; + STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address; + if( client->questdebug ) + server->SendPM(client, "Check Item - [Slot:%i][Type:%i]", curItem->iWhere, curItem->uiItemSN); + // Check quest items for a specific amount + if (curItem->iWhere == 13) // it's a quest item + { + // Ok, check the current Check'd quest. + if (client->CheckQuest < 0) return QUEST_SUCCESS; + unsigned int j = client->CheckQuest; + bool Hasitem = false; + bool FoundItem = false; + for (unsigned int k = 0; k < 5; k++) //5 quest item slots + { + if (client->quest.quests[j].Items[k].itemnum == (curItem->uiItemSN % 1000)) + { + FoundItem = true; + if( client->questdebug ) + server->SendPM(client, "Operate - btOp: %i Val1: %i Val2: %i", curItem->btOp, client->quest.quests[j].Items[k].count, curItem->iRequestCnt); + if(OperateValues(curItem->btOp, (int*)(&client->quest.quests[j].Items[k].count), curItem->iRequestCnt)) + Hasitem = true; + } + } + if(FoundItem == false) // didn't find any of the item in any of the quest slots + { + for (unsigned int k = 0; k < 5; k++) //Find the first empty slot + { + if (client->quest.quests[j].Items[k].count == 0 && Hasitem == false) + { + // Since the item doesn't exist, treat it as if there's 0 of them. + int tempInt = 0; + if(OperateValues(curItem->btOp, &tempInt, curItem->iRequestCnt)) + Hasitem = true; + if( client->questdebug ) + server->SendPM(client, "Requested item is not found. Comparing first empty slot instead"); + } + } + } + if(Hasitem == false) + { + if( client->questdebug ) + server->SendPM(client, "Requested item is not present. Returning quest failure"); + return QUEST_FAILURE; + } + } + else //this needs to be recoded. It doesn't do a damn thing right now WTF? + { + //item slot is not 13 + // Check equipped items for a specific item? - Drakia + if(curItem->uiItemSN == 0 && curItem->iWhere != 0) + { + int itemHead = client->items[curItem->iWhere].itemtype; + if(!OperateValues(curItem->btOp, &itemHead, curItem->uiItemSN)) + return QUEST_FAILURE; + } + } + } + if( client->questdebug ) + server->SendPM(client, "QuestCond 004 Returning Quest Success"); + return QUEST_SUCCESS; +} + +//Check Party +QUESTCOND(005) +{ + //byte btIsLeader pos 0x00 + //int iLevel pos 0x04 + //byte btReversed pos 0x08 + GETCONDDATA(005); + return QUEST_SUCCESS; +} + +//Near Point +QUESTCOND(006) +{ + //int iZoneSN; pos 0x00 + //int iX; pos 0x04 + //int iY; pos 0x08 + //int iZ; pos 0x0c + //int iRadius; pos 0x10 + GETCONDDATA(006); + client->UpdatePosition(); + if(data->iZoneSN != 0) + { + if(client->Position->Map != data->iZoneSN) + return QUEST_FAILURE; + } + float dx = client->Position->current.x - (float)(data->iX / 100); + float dy = client->Position->current.y - (float)(data->iY / 100); + float distance = sqrt((dx*dx) + (dy*dy)); + return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE; +} + +//World Time +QUESTCOND(007) +{ + //dword ulTime pos 0x00 + //dword ulEndTime pos 0x04 + return QUEST_SUCCESS; +} + +//Quest Time +QUESTCOND(008) +{ + //dword ulTime pos 0x00 + //byte btOp pos 0x04 + GETCONDDATA(008); + if( client->questdebug ) + server->SendPM(client, "QuestTime - ulTime: %i btOp: %i", data->ulTime, data->btOp); + if (client->CheckQuest < 0) return QUEST_FAILURE; // Not checking a quest, don't return success + long int Time = 0; + if (server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] > 0) + { + Time += client->quest.quests[client->CheckQuest].StartTime; // Start time + Time += server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] * 10; // Time to finish + Time -= time(NULL); // Current time + if (Time < 0) Time = 0; // Time is up + Time /= 10; // Divide to get 10's of seconds + } + if (!OperateValues(data->btOp, (dword*)&Time, data->ulTime)) + return QUEST_FAILURE; + else + return QUEST_SUCCESS; +} + +//Check Skill +QUESTCOND(009) +{ + //int iSkillSN1 pos 0x00 + //int iSkillSN2 pos 0x02 + //byte btOp pos 0x04 + GETCONDDATA(009); + // Line not needed, never used - Drakia + //int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE; + if( client->questdebug ) + server->SendPM(client, "Check Skill: %i", data->iSkillSN1); + bool s1 = false; + bool s2 = false; + for(dword i = 0; i < 30; i++) + { + if(client->bskills[i] == data->iSkillSN1) + s1 = true; + if(client->bskills[i] == data->iSkillSN2) + s2 = true; + if(client->askill[i] == data->iSkillSN1) + s1 = true; + if(client->askill[i] == data->iSkillSN2) + s2 = true; + if(client->pskill[i] == data->iSkillSN1) + s1 = true; + if(client->pskill[i] == data->iSkillSN2) + s2 = true; + } + if(data->btOp == 0) + { + if(s1 == false && s2 == false) + return QUEST_SUCCESS; //true if player does not have either skill + else + return QUEST_FAILURE; + } + else + { + if(s1 == true || s2 == true) + return QUEST_SUCCESS; //true if player has either skill + else + return QUEST_FAILURE; + } + //Log(MSG_DEBUG,"Questcond 009 btOp is neither 0 nor 1."); + //return QUEST_FAILURE; +} + +//random number in range +QUESTCOND(010) +{ + GETCONDDATA(010); + //byte btLowPcnt; pos 0x00 + //byte btHighPcnt; pos 0x01 + int randv = GServer->RandNumber(0,100); + if(randv >= data->btLowPcnt && randv <= data->btHighPcnt)return QUEST_SUCCESS; + return QUEST_FAILURE; +} + +//Object Variable +QUESTCOND(011) +{ + + //byte btWho pos 0x00 + //word nVarNo pos 0x02 + //int iValue pos 0x04 + //byte btOp pos 0x09 + GETCONDDATA(011); + /* + if(data->btWho == 0) + { + CNpc* thisNpc = reinterpret_cast(entity); + thisNpc = thisNpc->SelectedNpc; + if(thisNpc == NULL) return QUEST_FAILURE; + + short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo); + if(OperateValues(data->btOp, &VarValue, (short)data->iValue)) return QUEST_SUCCESS; + }*/ + if(data->btWho == 0) + { + + if(data->nVarNo > 19) return AI_FAILURE; + int refVal = GServer->ObjVar[client->NPCvar][data->nVarNo]; + int tempval = 0; + switch( data->btOp ) + { + case 0: + if(refVal == data->iValue)return QUEST_SUCCESS; + break; + case 1: + if(refVal > data->iValue)return QUEST_SUCCESS; + break; + case 2: + if(refVal >= data->iValue)return QUEST_SUCCESS; + break; + case 3: + if(refVal < data->iValue)return QUEST_SUCCESS; + break; + case 4: + if(refVal <= data->iValue)return QUEST_SUCCESS; + break; + case 10: + if(refVal != data->iValue)return QUEST_SUCCESS; + break; + default: + Log(MSG_WARNING,"Unknown btOp in QuestCond 011"); + break; + } + } + return QUEST_FAILURE; +} + +//select event object +QUESTCOND(012) +{ + GETCONDDATA(012); + //word iZone; pos 0x00 + //dword ulZone; pos 0x00 + //int iX; pos 0x04 + //int iY; pos 0x06 + //int iEventID; pos 0x08 + //dword m_HashEventOBJ; pos 0x08 + //this should be easy enough - PY + + return QUEST_SUCCESS; +} + +//Select NPC +QUESTCOND(013) +{ + //int iNpcNo pos 0x00 + GETCONDDATA(013); + // dammit!!!! This darn function is set up to use the CCharacter class so I can't use entity (CCharacter) + // This is going to be a serious pain in the ass + // OK so we now have a temporary player structure so we shuold be able to do this. + + //Log(MSG_DEBUG,"Setting NPC ref number AICOND(017). NPC Ref = %i",data->iNpcNo); + //CMonster* monster = reinterpret_cast(entity); + //if(monster == NULL) return AI_FAILURE; + //monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar + //Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC); + //not sure this is going to work properly for an NPC. + //NPC only exists as a temporary entity so refNPC may be lost after it returns from here. + + + /*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE; + + CNpc* thisNpc = reinterpret_cast(entity); + std::map::iterator triggerITR = server->NpcList.find(data->iNpcNo); + if(triggerITR == server->NpcList.end()){ + thisNpc->SelectedNpc = NULL; + return QUEST_FAILURE; + } + thisNpc->SelectedNpc = triggerITR->second; + */ + client->NPCvar = data->iNpcNo; + //Actually that was kind of easy.. lol - PY + + //return QUEST_SUCCESS; + // More NPC specific stuff + return QUEST_SUCCESS; +} + +//Check Quest Flag +QUESTCOND(014) +{ + GETCONDDATA(014); + //word nSN; pos 0x00 + //byte btOp; pos 0x02 + bool dataOp = (data->btOp)?true:false; + if( client->questdebug ) + server->SendPM(client, "Check QFlag[%i] == %i", data->nSN, data->btOp); + if(client->quest.GetFlag(data->nSN) == dataOp) + { + if( client->questdebug ) + server->SendPM(client, "Quest cond 014 (check quest flag) returned Success"); + return QUEST_SUCCESS; + } + else + { + if( client->questdebug ) + server->SendPM(client, "Quest cond 014 (check quest flag) returned Failure"); + return QUEST_FAILURE; + } +} + +//Party member count between X and Y - PY +QUESTCOND(015) +{ + + //word nNumber1 pos 0x00 + //word nNumber2 pos 0x02 + GETCONDDATA(015); + + + return QUEST_SUCCESS; +} + +//Zone Time +QUESTCOND(016) +{ + + //byte btWho pos 0x00 + //dword ulWho pos 0x00 + //dword ulTime pos 0x04 + //dword ulEndTime pos 0x08 + GETCONDDATA(016); + + return QUEST_SUCCESS; +} + +//NPC & Obj Variables? +QUESTCOND(017) +{ + //dword iNpcNo pos 0x00 + //word nVarNo pos 0x04 + //word unused pos 0x06 + //dword iNpcNo pos 0x08 + //word nVarNo pos 0x0c + //word unused pos 0x0e + //byte btOp pos 0x0f + return QUEST_SUCCESS; +} + +//Time on Date +QUESTCOND(018) +{ + //Returns SUCCESS if the system time of the server is between the defined start and end times. Else returns FAILURE. + //Allows for time dependent quests + //byte btDate; pos 0x00 + //byte btHour1; pos 0x01 + //byte btMin1; pos 0x02 + //byte btHour2; pos 0x03 + //byte btMin2; pos 0x04 + GETCONDDATA(018); + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + + if(data->btDate != 0) + { + if(sTIME.wDay != data->btDate) + return QUEST_FAILURE; + } + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin >= wFrom && wMin <= wTo) + return QUEST_SUCCESS; + return QUEST_FAILURE; +} + +//Time on Day +QUESTCOND(019) +{ + //byte btWeekDay; pos 0x00 + //byte btHour1; pos 0x01 + //byte btMin1; pos 0x02 + //byte btHour2; pos 0x03 + //byte btMin2; pos 0x04 + GETCONDDATA(019); + SYSTEMTIME sTIME; + GetLocalTime(&sTIME); + if(data->btWeekDay != 0) + { + if(sTIME.wDayOfWeek != data->btWeekDay) + return QUEST_FAILURE; + } + word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); + word wFrom = (data->btHour1 * 60) + data->btMin1; + word wTo = (data->btHour2 * 60) + data->btMin2; + if(wMin >= wFrom && wMin <= wTo) + return QUEST_SUCCESS; + return QUEST_FAILURE; +} + +//Team number between iNo1 and iNo2 +QUESTCOND(020) +{ + //int iNo1 pos 0x00 + //int iNo2 pos 0x02 + GETCONDDATA(020); + return QUEST_SUCCESS; +} + +//Distance check +QUESTCOND(021) +{ + // byte btSelObjType pos 0x00 + // byte iRadius pos 0x04 + return QUEST_SUCCESS; +} + +//Check Server/Channel +QUESTCOND(022) +{ + //word nX pos 0x00 + //word nY pos 0x02 + return QUEST_SUCCESS; +} + +//In Clan? +QUESTCOND(023) +{ + //byte btReg pos 0x00 + GETCONDDATA(023); + int Clanid = client->Clan->clanid; + if(data->btReg == 0) + { + if(Clanid == 0)return QUEST_SUCCESS; + else return QUEST_FAILURE; + } + else + { + if(Clanid != 0)return QUEST_SUCCESS; + else return QUEST_FAILURE; + } + //btReg should only ever be 1 or 0 but just in case...... + //return QUEST_FAILURE; +} + +//Clan Position +QUESTCOND(024) +{ + //check my current rank in the clan against data->nPOS using btOP + GETCONDDATA(024); + //word nPOS; pos 0x00 + //byte btOP; pos 0x02 + int myRank = client->Clan->clanrank; + switch(data->btOP) + { + case 0: + if(myRank == data->nPOS) + return QUEST_SUCCESS; + break; + case 1: + if(myRank > data->nPOS) + return QUEST_SUCCESS; + break; + case 2: + if(myRank >= data->nPOS) + return QUEST_SUCCESS; + break; + case 3: + if(myRank < data->nPOS) + return QUEST_SUCCESS; + break; + case 4: + if(myRank <= data->nPOS) + return QUEST_SUCCESS; + break; + case 10: + if(myRank != data->nPOS) + return QUEST_SUCCESS; + break; + default: + return QUEST_FAILURE; + break; + } +} + +//Clan Contribution +QUESTCOND(025) +{ + //word nCONT pos 0x00 + //byte btOP pos 0x02 + return QUEST_SUCCESS; +} + +//Clan Grade +QUESTCOND(026) +{ + //possibly compare client's current clan grade with data->nGRD using data->btOp? + GETCONDDATA(026); + //word nGRD; + //byte btOP; + int myGrade = client->Clan->grade; + switch(data->btOP) + { + case 0: + if(myGrade == data->nGRD) + return QUEST_SUCCESS; + break; + case 1: + if(myGrade > data->nGRD) + return QUEST_SUCCESS; + break; + case 2: + if(myGrade >= data->nGRD) + return QUEST_SUCCESS; + break; + case 3: + if(myGrade < data->nGRD) + return QUEST_SUCCESS; + break; + case 4: + if(myGrade <= data->nGRD) + return QUEST_SUCCESS; + break; + case 10: + if(myGrade != data->nGRD) + return QUEST_SUCCESS; + break; + default: + return QUEST_FAILURE; + break; + } +} + +//Clan Points +QUESTCOND(027) +{ + //word nPOINT pos 0x00 + //byte btOP pos 0x02 + GETCONDDATA(027); + if( client->questdebug ) + server->SendPM(client, "QUEST - Clan Points"); + switch(data->btOP) + { + case 0x01: + BEGINPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x5C ); //action to update clan points (charserver) + ADDWORD ( pak, client->Clan->clanid); + ADDWORD ( pak, client->clientid ); + ADDDWORD ( pak, client->Clan->CP ); + ADDWORD ( pak, 0x00); + GServer->SendISCPacket( &pak ); + break; + //default: + //break; + } + return QUEST_SUCCESS; +} + +//Clan Funds +QUESTCOND(028) +{ + //word iMONEY; pos 0x00 + //byte btOP; pos 0x02 + GETCONDDATA(027); + return QUEST_SUCCESS; +} + +//Clan Members +QUESTCOND(029) +{ + //word nMemberCNT; pos 0x00 + //byte btOP; pos 0x02 + return QUEST_SUCCESS; +} + +//Clan Skills +QUESTCOND(030) +{ + //word nSkill1; pos 0x00 + //word nSkill2; pos 0x02 + //byte btOP; pos 0x05 + GETCONDDATA(030); + //not sure what to do here. Looks like we compare something with two skill ids + if( client->questdebug )server->SendPM(client, "Clan skills. Skill1: %i Skill2 %i btOP: %i",data->nSkill1,data->nSkill2,data->btOP); + return QUEST_SUCCESS; +} diff --git a/Src/World Server/quest/QuestConditions.h b/Src/World Server/quest/QuestConditions.h new file mode 100644 index 0000000..bba0760 --- /dev/null +++ b/Src/World Server/quest/QuestConditions.h @@ -0,0 +1,239 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +#define QUESTCOND(cond) int QUEST_COND_ ## cond (class CWorldServer* server, class CPlayer* client, byte* raw) +typedef int (*fpQuestCond)(class CWorldServer*, class CPlayer*, byte*); +#define GETCONDDATA(cond) STR_COND_ ## cond * data = (STR_COND_ ## cond *)raw + +QUESTCOND(000);QUESTCOND(001);QUESTCOND(002); +QUESTCOND(003);QUESTCOND(004);QUESTCOND(005); +QUESTCOND(006);QUESTCOND(007);QUESTCOND(008); +QUESTCOND(009);QUESTCOND(010);QUESTCOND(011); +QUESTCOND(012);QUESTCOND(013);QUESTCOND(014); +QUESTCOND(015);QUESTCOND(016);QUESTCOND(017); +QUESTCOND(018);QUESTCOND(019);QUESTCOND(020); +QUESTCOND(021);QUESTCOND(022);QUESTCOND(023); +QUESTCOND(024);QUESTCOND(025);QUESTCOND(026); +QUESTCOND(027);QUESTCOND(028);QUESTCOND(029); +QUESTCOND(030); + +struct STR_COND_000 +{ + int iQuestSN; +}; + +struct STR_COND_001 +{ + dword iDataCnt; + //int iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_COND_002 +{ + dword iDataCnt; + //int iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_COND_003 +{ + dword iDataCnt; //pos 0x00 + STR_ABIL_DATA* CheckData; // pos = variable. it repeats iDataCnt times +}; + +struct STR_COND_004 +{ + dword iDataCnt; //pos 0x00 + STR_ITEM_DATA* CheckData; // pos = variable. it repeats iDataCnt times +}; + +struct STR_COND_005 +{ + byte btIsLeader; + int iLevel; + byte btReversed; +}; + +struct STR_COND_006 +{ + int iZoneSN; //pos 0x00 + int iX; //pos 0x04 + int iY; //pos 0x08 + int iZ; //pos 0x0c + int iRadius; //pos 0x10 +}; + +struct STR_COND_007 +{ + dword ulTime; + dword ulEndTime; +}; + +struct STR_COND_008 +{ + dword ulTime; + byte btOp; +}; + +struct STR_COND_009 +{ + int iSkillSN1; + int iSkillSN2; + byte btOp; +}; + +struct STR_COND_010 +{ + byte btLowPcnt; + byte btHighPcnt; +}; + +struct STR_COND_011 +{ + union + { + byte btWho; //pos 0x00 + word usWho; + }; + word nVarNo; //pos 0x02 + int iValue; //pos 0x04 + byte btOp; //pos 0x09 +}; + +struct STR_COND_012 +{ + union + { + word iZone; + dword ulZone; + }; + int iX; + int iY; + union + { + int iEventID; + dword m_HashEventOBJ; + }; +}; + +struct STR_COND_013 +{ + int iNpcNo; +}; + +struct STR_COND_014 +{ + word nSN; + byte btOp; +}; + +struct STR_COND_015 +{ + word nNumber1; + word nNumber2; +}; + +struct STR_COND_016 +{ + union + { + byte btWho; + dword ulWho; + }; + dword ulTime; + dword ulEndTime; +}; + +struct STR_COND_017 +{ + STR_NPCVAR NpcVar1; + STR_NPCVAR NpcVar2; + byte btOp; +}; + +struct STR_COND_018 +{ + byte btDate; + byte btHour1; + byte btMin1; + byte btHour2; + byte btMin2; +}; + +struct STR_COND_019 +{ + byte btWeekDay; + byte btHour1; + byte btMin1; + byte btHour2; + byte btMin2; +}; + +struct STR_COND_020 +{ + int iNo1; + int iNo2; +}; + +struct STR_COND_021 +{ + union + { + byte btSelObjType; + dword ulSelObjType; + }; + int iRadius; +}; + +struct STR_COND_022 +{ + word nX; + word nY; +}; + +struct STR_COND_023 +{ + byte btReg; +}; + +struct STR_COND_024 +{ + word nPOS; + byte btOP; +}; + +struct STR_COND_025 +{ + word nCONT; + byte btOP; +}; + +struct STR_COND_026 +{ + word nGRD; + byte btOP; +}; + +struct STR_COND_027 +{ + word nPOINT; + byte btOP; +}; + +struct STR_COND_028 +{ + word iMONEY; + byte btOP; +}; + +struct STR_COND_029 +{ + word nMemberCNT; + byte btOP; +}; + +struct STR_COND_030 +{ + word nSkill1; + word nSkill2; + byte btOP; +}; diff --git a/Src/World Server/quest/QuestTrigger.h b/Src/World Server/quest/QuestTrigger.h new file mode 100644 index 0000000..0b72461 --- /dev/null +++ b/Src/World Server/quest/QuestTrigger.h @@ -0,0 +1,98 @@ +// Props to ExJam for this code :D Ported to OSpRose by Drakia +class CQuestTrigger { +public: + struct SQuestDatum { + int size; + int opcode; + byte* data; + }; + + strings TriggerName; + dword TriggerHash; + + byte CheckNext; + dword id; + + dword ConditionCount; + SQuestDatum** Conditions; + + dword ActionCount; + SQuestDatum** Actions; +private: +}; + +struct STR_NPCVAR { + dword iNpcNo; + word nVarNo; + word unused; +}; + +struct STR_ITEM_DATA { + dword uiItemSN; + dword iWhere; + dword iRequestCnt; + byte btOp; +}; + +struct STR_QUEST_DATA +{ + union + { + dword iType; + struct + { + short m_wVarNO; + word m_wVarTYPE; + }; + }; + word nValue; + byte btOp; +}; + +struct STR_ABIL_DATA +{ + dword iType; + int iValue; + byte btOp; +}; + +#define QUEST_FAILURE 6 +#define QUEST_SUCCESS 5 +template int OperateValues(byte btOp, T* value1PTR, T value2) +{ + T value1 = *value1PTR; + switch(btOp) + { + case 0: + return (value1 == value2); + case 1: + return (value1 > value2); + case 2: + return (value1 >= value2); + case 3: + return (value1 < value2); + case 4: + return (value1 <= value2); + case 5: + (*value1PTR) = value2; + return 1; + case 6: + (*value1PTR) = value1 + value2; + return 1; + case 7: + (*value1PTR) = value1 - value2; + return 1; + case 9: + (*value1PTR) += 1; + return 1; + case 10: + return (value1 != value2); + default: + Log(MSG_WARNING, "OperateValues unknown btOp='%u'", btOp); + return 0; + } +} +dword GetRewardValue(dword function, dword amount, CPlayer* client, word nDupCNT); + +#include "QuestConditions.h" +#include "QuestActions.h" diff --git a/Src/World Server/quest/Quests.cpp b/Src/World Server/quest/Quests.cpp new file mode 100644 index 0000000..aa7d74f --- /dev/null +++ b/Src/World Server/quest/Quests.cpp @@ -0,0 +1,157 @@ +// Props to ExJam for this code :D +#include "../WorldServer.h" +#include "QuestActions.h" + +void CWorldServer::ReadQSD(strings path, dword index){ + CRoseFile* fh = new CRoseFile(path, FM_READ | FM_BINARY); + + if(fh->IsOpen()) + { // goto done; + + //Log(MSG_LOAD, "Loading %s ", path); + + fh->Seek(4, SEEK_CUR); + dword BlockCount = fh->Get(); + fh->Seek(fh->Get(), SEEK_CUR); + for(dword i = 0; i < BlockCount; i++) + { + dword RecordCount = fh->Get(); + fh->Seek(fh->Get(), SEEK_CUR); + for(dword j = 0; j < RecordCount; j++) + { + CQuestTrigger* trigger = new CQuestTrigger(); + trigger->id=((index*0x10000)+(i*0x100)+j); + trigger->CheckNext = fh->Get(); + trigger->ConditionCount = fh->Get(); + trigger->ActionCount = fh->Get(); + dword len = fh->Get(); + trigger->TriggerName = new char[len+1]; + fh->Read(trigger->TriggerName, len, 1); + trigger->TriggerName[len] = 0; + + if(trigger->ConditionCount > 0) + { + trigger->Conditions = new CQuestTrigger::SQuestDatum*[trigger->ConditionCount]; + for(dword k = 0; k < trigger->ConditionCount; k++) + { + CQuestTrigger::SQuestDatum* data = new CQuestTrigger::SQuestDatum(); + data->size = fh->Get(); + data->opcode = fh->Get(); + data->data = new byte[data->size - 8]; + fh->Read(data->data, data->size - 8, 1); + trigger->Conditions[k] = data; + } + } + else + { + trigger->Conditions = NULL; + } + + if(trigger->ActionCount > 0) + { + trigger->Actions = new CQuestTrigger::SQuestDatum*[trigger->ActionCount]; + for(dword k = 0; k < trigger->ActionCount; k++) + { + CQuestTrigger::SQuestDatum* data = new CQuestTrigger::SQuestDatum(); + data->size = fh->Get(); + data->opcode = fh->Get() - 0x01000000; + data->data = new byte[data->size - 8]; + fh->Read(data->data, data->size - 8, 1); + trigger->Actions[k] = data; + } + } + else + { + trigger->Actions = NULL; + } + + trigger->TriggerHash = MakeStrHash(trigger->TriggerName); + TriggerList.push_back( trigger ); + } + } + } + else + Log( MSG_ERROR, "QSD File: '%s' did not load properly", path ); + + fh->Close(); + delete fh; +} + +void CWorldServer::LoadQuestData(){ + CStrStb* stbQuest = new CStrStb("3DDATA\\STB\\LIST_QUESTDATA.STB"); + Log(MSG_INFO, "Loading QSD files"); + for(dword i = 1; i < stbQuest->Rows(); i++){ + if(stbQuest->Data(i, 0)){ + GServer->ReadQSD(stbQuest->Data(i, 0),i); + } + } + + Log(MSG_INFO, "Finished loading quest data "); + + qstCondFunc[0] = &QUEST_COND_000; + qstCondFunc[1] = &QUEST_COND_001; + qstCondFunc[2] = &QUEST_COND_002; + qstCondFunc[3] = &QUEST_COND_003; + qstCondFunc[4] = &QUEST_COND_004; + qstCondFunc[5] = &QUEST_COND_005; + qstCondFunc[6] = &QUEST_COND_006; + qstCondFunc[7] = &QUEST_COND_007; + qstCondFunc[8] = &QUEST_COND_008; + qstCondFunc[9] = &QUEST_COND_009; + qstCondFunc[10] = &QUEST_COND_010; + qstCondFunc[11] = &QUEST_COND_011; + qstCondFunc[12] = &QUEST_COND_012; + qstCondFunc[13] = &QUEST_COND_013; + qstCondFunc[14] = &QUEST_COND_014; + qstCondFunc[15] = &QUEST_COND_015; + qstCondFunc[16] = &QUEST_COND_016; + qstCondFunc[17] = &QUEST_COND_017; + qstCondFunc[18] = &QUEST_COND_018; + qstCondFunc[19] = &QUEST_COND_019; + qstCondFunc[20] = &QUEST_COND_020; + qstCondFunc[21] = &QUEST_COND_021; + qstCondFunc[22] = &QUEST_COND_022; + qstCondFunc[23] = &QUEST_COND_023; + qstCondFunc[24] = &QUEST_COND_024; + qstCondFunc[25] = &QUEST_COND_025; + qstCondFunc[26] = &QUEST_COND_026; + qstCondFunc[27] = &QUEST_COND_027; + qstCondFunc[28] = &QUEST_COND_028; + qstCondFunc[29] = &QUEST_COND_029; + qstCondFunc[30] = &QUEST_COND_030; + + qstRewdFunc[0] = &QUEST_REWD_000; + qstRewdFunc[1] = &QUEST_REWD_001; + qstRewdFunc[2] = &QUEST_REWD_002; + qstRewdFunc[3] = &QUEST_REWD_003; + qstRewdFunc[4] = &QUEST_REWD_004; + qstRewdFunc[5] = &QUEST_REWD_005; + qstRewdFunc[6] = &QUEST_REWD_006; + qstRewdFunc[7] = &QUEST_REWD_007; + qstRewdFunc[8] = &QUEST_REWD_008; + qstRewdFunc[9] = &QUEST_REWD_009; + qstRewdFunc[10] = &QUEST_REWD_010; + qstRewdFunc[11] = &QUEST_REWD_011; + qstRewdFunc[12] = &QUEST_REWD_012; + qstRewdFunc[13] = &QUEST_REWD_013; + qstRewdFunc[14] = &QUEST_REWD_014; + qstRewdFunc[15] = &QUEST_REWD_015; + qstRewdFunc[16] = &QUEST_REWD_016; + qstRewdFunc[17] = &QUEST_REWD_017; + qstRewdFunc[18] = &QUEST_REWD_018; + qstRewdFunc[19] = &QUEST_REWD_019; + qstRewdFunc[20] = &QUEST_REWD_020; + qstRewdFunc[21] = &QUEST_REWD_021; + qstRewdFunc[22] = &QUEST_REWD_022; + qstRewdFunc[23] = &QUEST_REWD_023; + qstRewdFunc[24] = &QUEST_REWD_024; + qstRewdFunc[25] = &QUEST_REWD_025; + qstRewdFunc[26] = &QUEST_REWD_026; + qstRewdFunc[27] = &QUEST_REWD_027; + qstRewdFunc[28] = &QUEST_REWD_028; + qstRewdFunc[29] = &QUEST_REWD_029; + qstRewdFunc[30] = &QUEST_REWD_030; + //qstRewdFunc[50] = &QUEST_REWD_050; + + delete stbQuest; +} diff --git a/Src/World Server/quest/strhashing.cpp b/Src/World Server/quest/strhashing.cpp new file mode 100644 index 0000000..eb8492d --- /dev/null +++ b/Src/World Server/quest/strhashing.cpp @@ -0,0 +1,17 @@ +#include "../WorldServer.h" + +unsigned long MakeStrHash(const char *str) +{ + register unsigned long result = 0xDEADC0DE; + register unsigned long tkey = 0x7FED7FED; + register char c = 0; + while (*(str)){ + c = (*str<'a'||*str>'z')?*str:*str-32; + result += tkey; + tkey *= 0x21; + result ^= keys[c]; + tkey += result + c + 3; + str++; + } + return result; +} diff --git a/Src/World Server/quest/strhashing.h b/Src/World Server/quest/strhashing.h new file mode 100644 index 0000000..c05baa0 --- /dev/null +++ b/Src/World Server/quest/strhashing.h @@ -0,0 +1,20 @@ +const unsigned long keys[256] = { + 0x697A5, 0x6045C, 0xAB4E2, 0x409E4, 0x71209, 0x32392, 0xA7292, 0xB09FC, 0x4B658, 0xAAAD5, 0x9B9CF, 0xA326A, 0x8DD12, 0x38150, 0x8E14D, 0x2EB7F, + 0xE0A56, 0x7E6FA, 0xDFC27, 0xB1301, 0x8B4F7, 0xA7F70, 0xAA713, 0x6CC0F, 0x6FEDF, 0x2EC87, 0xC0F1C, 0x45CA4, 0x30DF8, 0x60E99, 0xBC13E, 0x4E0B5, + 0x6318B, 0x82679, 0x26EF2, 0x79C95, 0x86DDC, 0x99BC0, 0xB7167, 0x72532, 0x68765, 0xC7446, 0xDA70D, 0x9D132, 0xE5038, 0x2F755, 0x9171F, 0xCB49E, + 0x6F925, 0x601D3, 0x5BD8A, 0x2A4F4, 0x9B022, 0x706C3, 0x28C10, 0x2B24B, 0x7CD55, 0xCA355, 0xD95F4, 0x727BC, 0xB1138, 0x9AD21, 0xC0ACA, 0xCD928, + 0x953E5, 0x97A20, 0x345F3, 0xBDC03, 0x7E157, 0x96C99, 0x968EF, 0x92AA9, 0xC2276, 0xA695D, 0x6743B, 0x2723B, 0x58980, 0x66E08, 0x51D1B, 0xB97D2, + 0x6CAEE, 0xCC80F, 0x3BA6C, 0xB0BF5, 0x9E27B, 0xD122C, 0x48611, 0x8C326, 0xD2AF8, 0xBB3B7, 0xDED7F, 0x4B236, 0xD298F, 0xBE912, 0xDC926, 0xC873F, + 0xD0716, 0x9E1D3, 0x48D94, 0x9BD91, 0x5825D, 0x55637, 0xB2057, 0xBCC6C, 0x460DE, 0xAE7FB, 0x81B03, 0x34D8F, 0xC0528, 0xC9B59, 0x3D260, 0x6051D, + 0x93757, 0x8027F, 0xB7C34, 0x4A14E, 0xB12B8, 0xE4945, 0x28203, 0xA1C0F, 0xAA382, 0x46ABB, 0x330B9, 0x5A114, 0xA754B, 0xC68D0, 0x9040E, 0x6C955, + 0xBB1EF, 0x51E6B, 0x9FF21, 0x51BCA, 0x4C879, 0xDFF70, 0x5B5EE, 0x29936, 0xB9247, 0x42611, 0x2E353, 0x26F3A, 0x683A3, 0xA1082, 0x67333, 0x74EB7, + 0x754BA, 0x369D5, 0x8E0BC, 0xABAFD, 0x6630B, 0xA3A7E, 0xCDBB1, 0x8C2DE, 0x92D32, 0x2F8ED, 0x7EC54, 0x572F5, 0x77461, 0xCB3F5, 0x82C64, 0x35FE0, + 0x9203B, 0xADA2D, 0xBAEBD, 0xCB6AF, 0xC8C9A, 0x5D897, 0xCB727, 0xA13B3, 0xB4D6D, 0xC4929, 0xB8732, 0xCCE5A, 0xD3E69, 0xD4B60, 0x89941, 0x79D85, + 0x39E0F, 0x6945B, 0xC37F8, 0x77733, 0x45D7D, 0x25565, 0xA3A4E, 0xB9F9E, 0x316E4, 0x36734, 0x6F5C3, 0xA8BA6, 0xC0871, 0x42D05, 0x40A74, 0x2E7ED, + 0x67C1F, 0x28BE0, 0xE162B, 0xA1C0F, 0x2F7E5, 0xD505A, 0x9FCC8, 0x78381, 0x29394, 0x53D6B, 0x7091D, 0xA2FB1, 0xBB942, 0x29906, 0xC412D, 0x3FCD5, + 0x9F2EB, 0x8F0CC, 0xE25C3, 0x7E519, 0x4E7D9, 0x5F043, 0xBBA1B, 0x6710A, 0x819FB, 0x9A223, 0x38E47, 0xE28AD, 0xB690B, 0x42328, 0x7CF7E, 0xAE108, + 0xE54BA, 0xBA5A1, 0xA09A6, 0x9CAB7, 0xDB2B3, 0xA98CC, 0x5CEBA, 0x9245D, 0x5D083, 0x8EA21, 0xAE349, 0x54940, 0x8E557, 0x83EFD, 0xDC504, 0xA6059, + 0xB85C9, 0x9D162, 0x7AEB6, 0xBED34, 0xB4963, 0xE367B, 0x4C891, 0x9E42C, 0xD4304, 0x96EAA, 0xD5D69, 0x866B8, 0x83508, 0x7BAEC, 0xD03FD, 0xDA122 +}; + +unsigned long MakeStrHash(const char *str); \ No newline at end of file diff --git a/Src/World Server/questactions.h.wrong b/Src/World Server/questactions.h.wrong new file mode 100644 index 0000000..382e9c2 --- /dev/null +++ b/Src/World Server/questactions.h.wrong @@ -0,0 +1,196 @@ +#define QUESTREWD(reward) int QUEST_REWD_ ## reward ## (class CWorldServer* server, class CPlayer* client, byte* raw) +typedef int (*fpQuestRewd)(class CWorldServer*, class CPlayer*, byte*); +#define GETREWDDATA(rewd) STR_REWD_ ## rewd ## * data = (STR_REWD_ ## rewd ## *)raw; + +QUESTREWD(000);QUESTREWD(001);QUESTREWD(002); +QUESTREWD(003);QUESTREWD(004);QUESTREWD(005); +QUESTREWD(006);QUESTREWD(007);QUESTREWD(008); +QUESTREWD(009);QUESTREWD(010);QUESTREWD(011); +QUESTREWD(012);QUESTREWD(013);QUESTREWD(014); +QUESTREWD(015);QUESTREWD(016);QUESTREWD(017); +QUESTREWD(018);QUESTREWD(019);QUESTREWD(020); +QUESTREWD(021);QUESTREWD(022);QUESTREWD(023); +QUESTREWD(024);QUESTREWD(025);QUESTREWD(026); +QUESTREWD(027);QUESTREWD(028); + +struct STR_REWD_000 { + dword iQuestSN; + byte btOp; +}; + +struct STR_REWD_001 { + dword uiItemSN; + union { + byte btOp; + word nOp; + }; + word nDupCNT; + byte btPartyOpt; +}; + +struct STR_REWD_002 { + dword iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_REWD_003 { + dword iDataCnt; + STR_ABIL_DATA* CheckData; + byte btPartyOpt; +}; + +struct STR_REWD_004 { + dword iDataCnt; + STR_QUEST_DATA* CheckData; +}; + +struct STR_REWD_005 { + byte btTarget; + union { + byte btEquation; + word nEquation; + }; + int iValue; + dword iItemSN; + union { + byte btPartyOpt; + word nPartyOpt; + }; + word nItemOpt; +}; + +struct STR_REWD_006 { + dword iPercentOfHP; + dword iPercentOfMP; + byte btPartyOpt; +}; + +struct STR_REWD_007 { + dword iZoneSN; + dword iX; + dword iY; + byte btPartyOpt; +}; + +struct STR_REWD_008 { + dword iMonsterSN; + dword iHowMany; + union { + byte btWho; + int iWho; + }; + dword iZoneSN; + dword iX; + dword iY; + dword iRange; + dword iTeamNo; +}; + +struct STR_REWD_009 { + word shNameLen; + string szNextTriggerSN; +}; + +struct STR_REWD_010 { +}; + +//UNKNOWN STRUCT FOR 11 OMG OMG OMG +struct STR_REWD_011 { +}; + +struct STR_REWD_012 { + union { + byte btMsgType; + int iMsgType; + }; + dword iStrID; +}; + +struct STR_REWD_013 { + union { + byte btWho; + int iWho; + }; + dword iSec; + word nNameLength; + string szTriggerName; + dword m_HashNextTrigger; +}; + +struct STR_REWD_014 { + union { + byte btOp; + int iOp; + }; + dword iSkillNo; +}; + +struct STR_REWD_015 { + word nSN; + byte btOp; +}; + +struct STR_REWD_016 { + word nGroupSN; +}; + +struct STR_REWD_017 { +}; + +struct STR_REWD_018 { + dword iStrID; + word nCnt; + string Data; +}; + +struct STR_REWD_019 { + word nZoneNo; + word nTeamNo; + word nTriggerLength; + string TriggerName; + dword m_HashTrigger; +}; + +struct STR_REWD_020 { + byte btNoType; +}; + +struct STR_REWD_021 { + dword iX; + dword iY; +}; + +struct STR_REWD_022 { + word nZonNo; + byte btOp; +}; + +struct STR_REWD_023 { +}; + +struct STR_REWD_024 { + int iMONEY; + byte btOP; +}; + +struct STR_REWD_025 { + short nPOINT; + byte btOP; +}; + +struct STR_REWD_026 { + word nSkillNo; + byte btOP; +}; + +struct STR_REWD_027 { + short nCONT; + byte btOP; +}; + +struct STR_REWD_028 { + dword iRange; + word nZoneNo; + dword iX; + dword iY; +}; diff --git a/Src/World Server/startup.cpp b/Src/World Server/startup.cpp new file mode 100644 index 0000000..5346e4a --- /dev/null +++ b/Src/World Server/startup.cpp @@ -0,0 +1,1297 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" +bool CWorldServer::LoadSTBData( ) +{ + Log( MSG_LOAD, "STB Data " ); + STBStoreData( "3DData\\STB\\LIST_NPC.STB", &STB_NPC ); + STBStoreData( "3DData\\STB\\LIST_SKILL.STB", &STB_SKILL ); + STBStoreData( "3DData\\STB\\LIST_STATUS.STB", &STB_STATUS ); + STBStoreData( "3DData\\STB\\LIST_QUEST.STB", &STB_QUEST ); + STBStoreData( "3DData\\STB\\LIST_FACEITEM.STB", &STB_ITEM[0] ); + STBStoreData( "3DData\\STB\\LIST_CAP.STB", &STB_ITEM[1] ); + STBStoreData( "3DData\\STB\\LIST_BODY.STB", &STB_ITEM[2] ); + STBStoreData( "3DData\\STB\\LIST_ARMS.STB", &STB_ITEM[3] ); + STBStoreData( "3DData\\STB\\LIST_FOOT.STB", &STB_ITEM[4] ); + STBStoreData( "3DData\\STB\\LIST_BACK.STB", &STB_ITEM[5] ); + STBStoreData( "3DData\\STB\\LIST_JEWEL.STB", &STB_ITEM[6] ); + STBStoreData( "3DData\\STB\\LIST_WEAPON.STB", &STB_ITEM[7] ); + STBStoreData( "3DData\\STB\\LIST_SUBWPN.STB", &STB_ITEM[8] ); + STBStoreData( "3DData\\STB\\LIST_USEITEM.STB", &STB_ITEM[9] ); + STBStoreData( "3DData\\STB\\LIST_JEMITEM.STB", &STB_ITEM[10] ); + STBStoreData( "3DData\\STB\\LIST_NATURAL.STB", &STB_ITEM[11] ); + STBStoreData( "3DData\\STB\\LIST_QUESTITEM.STB", &STB_ITEM[12] ); + STBStoreData( "3DData\\STB\\LIST_PAT.STB", &STB_ITEM[13] ); + STBStoreData( "3DData\\STB\\LIST_PRODUCT.STB", &STB_PRODUCT ); + STBStoreData( "3DData\\STB\\LIST_BREAK.STB", &STB_BREAK ); + STBStoreData( "3DData\\STB\\LIST_SELL.STB", &STB_SELL ); + STBStoreData( "3DData\\STB\\LIST_ZONE.STB", &STB_ZONE ); + //STBStoreData( "3DData\\STB\\ITEM_DROP.STB", &STB_DROP ); + STBStoreData("3DData\\STB\\LIST_GRADE.STB", &ListGrade); + STBStoreData("3DData\\STB\\LIST_UPGRADE.STB", &ListUpGrade); + return true; +} + +bool CWorldServer::LoadNPCData( ) +{ + Log( MSG_INFO, "Loading NPC Data" ); + for (unsigned int i = 0; iid = i; + newnpc->life = STB_NPC.rows[i][1]; + newnpc->stance = mRUNNING; + newnpc->wspeed = STB_NPC.rows[i][2]; + newnpc->rspeed = STB_NPC.rows[i][3]; + newnpc->dspeed = STB_NPC.rows[i][4]; + newnpc->weapon = STB_NPC.rows[i][5]; + newnpc->subweapon = STB_NPC.rows[i][6]; + newnpc->level = STB_NPC.rows[i][7]; + newnpc->hp = STB_NPC.rows[i][8]; + newnpc->atkpower = STB_NPC.rows[i][9]; + //reduce attack power by 10% to improve combat + newnpc->atkpower = (int)(newnpc->atkpower * 0.9); + newnpc->hitrate = STB_NPC.rows[i][10]; + newnpc->defense = STB_NPC.rows[i][11]; + newnpc->magicdefense = STB_NPC.rows[i][12]; + newnpc->dodge = STB_NPC.rows[i][13]; + //reduce dodge by 20% to improve combat + newnpc->dodge = (int)(newnpc->dodge * 0.8); + newnpc->atkspeed = STB_NPC.rows[i][14]; + newnpc->magicattack = STB_NPC.rows[i][15]; + newnpc->AI = STB_NPC.rows[i][16]; + newnpc->exp = STB_NPC.rows[i][17]; + newnpc->dropid = STB_NPC.rows[i][18]; + newnpc->money = STB_NPC.rows[i][19]; + newnpc->item = STB_NPC.rows[i][20]; + newnpc->tab1 = STB_NPC.rows[i][21]; + newnpc->tab2 = STB_NPC.rows[i][22]; + newnpc->tab3 = STB_NPC.rows[i][23]; + newnpc->specialtab = STB_NPC.rows[i][24]; + newnpc->atkdistance = STB_NPC.rows[i][26]/100; + newnpc->aggresive = STB_NPC.rows[i][27]; + newnpc->die_quest = STB_NPC.rows[i][41]; //this contains the hash of the quest trigger.We can use it to run quest triggers directly + newnpc->shp = STB_NPC.rows[i][42]; //PY: What the heck. This column of the STB defines the height of a flying monster such as a bee + newnpc->dialogid = 0; + //Log( MSG_DEBUG, "NPC %i lvl %i with AI %i", newnpc->id, newnpc->level, newnpc->AI); + NPCData.push_back( newnpc ); + } + return true; +} + +bool CWorldServer::LoadStatusData( ) +{ + Log( MSG_INFO, "Loading Stats Data " ); + for (unsigned int i = 0; i < STB_STATUS.rowcount; i++) + { + CStatus* newstatus = new (nothrow) CStatus; + if (newstatus == NULL) + { + Log(MSG_WARNING, "\nError allocing memory: status_data"); + return false; + } + newstatus->id = i; + newstatus->type = STB_STATUS.rows[i][1]; // Unk + newstatus->duplication = STB_STATUS.rows[i][2]; // Are results stackable? + newstatus->dir = STB_STATUS.rows[i][3]; // 0: Up 1: Down 2: ? + newstatus->repeat = STB_STATUS.rows[i][4]; // 1: Effect over time 2: Once only (eg. Increase max Stat) 3: Status Effect (taunt, fainted etc.) + newstatus->ability[0] = STB_STATUS.rows[i][5]; // Status # to call? + newstatus->amount[0] = STB_STATUS.rows[i][6]; // Amount + newstatus->ability[1] = STB_STATUS.rows[i][7]; // Second Status + newstatus->amount[1] = STB_STATUS.rows[i][8]; // Second amount + newstatus->decrease = STB_STATUS.rows[i][17]; // 0: Increase 1: Decrease + + StatusList.push_back(newstatus); + } + return true; +} + +bool CWorldServer::LoadSkillData( ) +{ + Log( MSG_INFO, "Loading Skills data " ); + for (unsigned int i = 0; iid=i; + newskill->skillid = STB_SKILL.rows[i][1]; // Skillid. Should be the same for all levels of a given skill + newskill->level = STB_SKILL.rows[i][2]; // Skills Level + newskill->sp = STB_SKILL.rows[i][3]; // Cost to get skill + newskill->type = STB_SKILL.rows[i][4]; // + newskill->skilltype = STB_SKILL.rows[i][5]; // Type of skill + newskill->range = STB_SKILL.rows[i][6]/100; // Range of skill + newskill->target = STB_SKILL.rows[i][7]; // Skill Target + newskill->aoerange = STB_SKILL.rows[i][8]/100; // AOE Range + newskill->atkpower = STB_SKILL.rows[i][9]; // Attack Power + newskill->status[0] = STB_SKILL.rows[i][11]; // status + newskill->status[1] = STB_SKILL.rows[i][12]; // status + newskill->success = STB_SKILL.rows[i][13]; // Success Rate + if(newskill->success == 0) + newskill->success = 100; //success rate is stored as NULL where it is 100%. eg dual scratch + newskill->duration = STB_SKILL.rows[i][14]; // Duration + newskill->costtype[0] = STB_SKILL.rows[i][16]; //not all costs are in MP + newskill->costamount[0] = STB_SKILL.rows[i][17]; //some are in HP or Stamina + newskill->mp = STB_SKILL.rows[i][17]; + newskill->costtype[1] = STB_SKILL.rows[i][18]; // All driveby skills have 2 costs + newskill->costamount[1] = STB_SKILL.rows[i][19]; // The second is normally cart gauge + newskill->cooldown = STB_SKILL.rows[i][20]; // The time it takes to be able to use the skill again. + newskill->buff[0] = STB_SKILL.rows[i][21]; // Stat + //if(newskill->id == 1403) + // Log(MSG_DEBUG,"Skill 1403 buff[0] value is %i",newskill->buff[0]); + newskill->value1[0] = STB_SKILL.rows[i][22]; // Int Value + newskill->value2[0] = STB_SKILL.rows[i][23]; // % Value + newskill->buff[1] = STB_SKILL.rows[i][24]; // Stat + //if(newskill->id == 1403) + // Log(MSG_DEBUG,"Skill 1403 buff[1] value is %i",newskill->buff[1]); + newskill->value1[1] = STB_SKILL.rows[i][25]; // Int Value + newskill->value2[1] = STB_SKILL.rows[i][26]; // % Value + //newskill->success=STB_SKILL.rows[i][69]; //Dunno what this field is really but it sure isn't success rate <_< + newskill->weapon[0] = STB_SKILL.rows[i][30]; // Item Type must be equipped (Only 1) + //if(newskill->weapon[0] != 0 && newskill->id < 1500) + // Log(MSG_DEBUG,"Skill %i weapon 1 id is %i",newskill->id,newskill->weapon[0]); + newskill->weapon[1] = STB_SKILL.rows[i][31]; // Item Type must be equipped + //if(newskill->weapon[1] != 0 && newskill->id < 1500) + // Log(MSG_DEBUG,"Skill %i weapon 2 id is %i",newskill->id,newskill->weapon[1]); + newskill->weapon[2] = STB_SKILL.rows[i][32]; // Item Type must be equipped + //if(newskill->weapon[2] != 0 && newskill->id < 1500) + // Log(MSG_DEBUG,"Skill %i weapon 3 id is %i",newskill->id,newskill->weapon[2]); + newskill->weapon[3] = STB_SKILL.rows[i][33]; // Item Type must be equipped + //if(newskill->weapon[3] != 0 && newskill->id < 1500) + // Log(MSG_DEBUG,"Skill %i weapon 4 id is %i",newskill->id,newskill->weapon[3]); + newskill->weapon[4] = STB_SKILL.rows[i][34]; // Item Type must be equipped + //if(newskill->weapon[4] != 0 && newskill->id < 1500) + // Log(MSG_DEBUG,"Skill %i weapon 5 id is %i",newskill->id,newskill->weapon[4]); + newskill->c_class[0] = STB_SKILL.rows[i][35]; // Required Class (Only One) + newskill->c_class[1] = STB_SKILL.rows[i][36]; // Required Class + newskill->c_class[2] = STB_SKILL.rows[i][37]; // Required Class + newskill->c_class[3] = STB_SKILL.rows[i][38]; // Required Class + newskill->c_class[4] = 0; + newskill->rskill[0] = STB_SKILL.rows[i][39]; // Required Skill + newskill->rskill[1] = STB_SKILL.rows[i][41]; // Required Skill + newskill->rskill[2] = STB_SKILL.rows[i][43]; // Required Skill + newskill->lskill[0] = STB_SKILL.rows[i][40]; // Required Skill Level + newskill->lskill[1] = STB_SKILL.rows[i][42]; // Required Skill Level + newskill->lskill[2] = STB_SKILL.rows[i][44]; // Required Skill Level + + + + //WTF are these for??? + //newskill->buff[2]=0;STB_SKILL.rows[i][11];//for damage support + //newskill->value1[2]=0;STB_SKILL.rows[i][9];//for damage support + //newskill->value2[2]=0; + + newskill->req[0] = STB_SKILL.rows[i][45]; //the requirement type (usually 31 = level) + newskill->clevel = STB_SKILL.rows[i][46]; //requirement amount + newskill->reqam[0] = STB_SKILL.rows[i][46]; //this need not always be level but is usually + newskill->req[1] = STB_SKILL.rows[i][47]; //the requirement type (in most cases blank) + newskill->reqam[1] = STB_SKILL.rows[i][48]; //requirement amount + newskill->zuly = (STB_SKILL.rows[i][85] * 100); // Required zuly + if ((STB_SKILL.rows[i][8])>0) + { + newskill->aoe = 1; + newskill->aoeradius = STB_SKILL.rows[i][8]/100; + } + else + { + newskill->aoe = 0; + newskill->aoeradius = 0; + } + if ((STB_SKILL.rows[i][28])>0) + { + newskill->script = 1; + newskill->svalue1 = STB_SKILL.rows[i][28]; + } + else + { + newskill->script=0; + newskill->svalue1=0; + } + newskill->nbuffs = 0; + if(newskill->buff[0] != 0 || newskill->status[0] != 0) + newskill->nbuffs += 1; + if(newskill->buff[1]!= 0 || newskill->status[1] != 0) + newskill->nbuffs += 2; + + + SkillList.push_back( newskill ); + } + return true; +} + +bool CWorldServer::LoadTeleGateData( ) +{ + Log( MSG_INFO, "Loading Telegates data " ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id, srcx, srcy, srcmap, destx, desty, destmap FROM list_telegates"); + if(result==NULL) return false; + while( row = mysql_fetch_row(result) ) + { + CTeleGate* thisgate = new (nothrow) CTeleGate; + if(thisgate==NULL) + { + Log(MSG_ERROR, "Error allocing memory " ); + DB->QFree( ); + return false; + } + thisgate->id = atoi(row[0]); + thisgate->src.x = (float)atof(row[1]); + thisgate->src.y = (float)atof(row[2]); + thisgate->srcMap = atoi(row[3]); + thisgate->dest.x = (float)atof(row[4]); + thisgate->dest.y = (float)atof(row[5]); + thisgate->destMap = atoi(row[6]); + TeleGateList.push_back( thisgate ); + } + DB->QFree( ); + return true; +} + +bool CWorldServer::LoadRespawnData( ) +{ + Log( MSG_INFO, "Loading RespawnZones data " ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id,x,y,map,radius,type FROM list_respawnzones"); + if(result==NULL) return false; + while( row = mysql_fetch_row(result) ) + { + CRespawnPoint* thisrespawnpoint = new (nothrow) CRespawnPoint; + if(thisrespawnpoint==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + DB->QFree( ); + return false; + } + thisrespawnpoint->id = atoi(row[0]); + thisrespawnpoint->dest.x = (float)atof(row[1]); + thisrespawnpoint->dest.y = (float)atof(row[2]); + thisrespawnpoint->destMap = atoi(row[3]); + thisrespawnpoint->radius = atoi(row[4]); + thisrespawnpoint->masterdest = (atoi(row[5]) == 1); + MapList.Index[thisrespawnpoint->destMap]->RespawnList.push_back( thisrespawnpoint ); + } + DB->QFree( ); + return true; +} + + +bool CWorldServer::LoadMobGroups() +{ + if(GServer->Config.SpawnType == 0) + { + Log(MSG_INFO, "Loading MobGroups data " ); + vector mobGroups; + MYSQL_ROW row; + bool flag = true; + char* tmp = NULL; + MYSQL_RES *result = DB->QStore("SELECT id, map, x, y, `range`, respawntime, `limit`, tacticalpoints, moblist FROM list_mobgroups"); + if (result == NULL) return false; + while (row = mysql_fetch_row(result)) + { + CMobGroup* thisgroup = new (nothrow) CMobGroup; + if (thisgroup == NULL) { + Log(MSG_ERROR, "Error allocating memory"); + DB->QFree(); + return false; + } + thisgroup->id = atoi(row[0]); + thisgroup->map = atoi(row[1]); + thisgroup->point.x = atof(row[2]); + thisgroup->point.y = atof(row[3]); + thisgroup->range = atoi(row[4]); + thisgroup->respawntime = atoi(row[5]); + thisgroup->limit = atoi(row[6]); + thisgroup->tacticalpoints = atoi(row[7]); + char* mobList = row[8]; + + thisgroup->lastRespawnTime = clock(); + thisgroup->active = 0; + thisgroup->basicKills = 0; + thisgroup->curTac = 0; + thisgroup->curBasic = 0; + + thisgroup->basicMobs.clear(); + thisgroup->tacMobs.clear(); + + // Fill in basic/tac mobs + tmp = strtok(mobList, ",|"); + while (tmp != NULL) { + int mobId = atoi(tmp); + tmp = strtok(NULL, ",|"); + if (tmp == NULL) { + Log(MSG_ERROR, "MobGroup %i is invalid", thisgroup->id); + flag = false; + break; + } + int amount = atoi(tmp); + tmp = strtok(NULL, ",|"); + if (tmp == NULL) { + Log(MSG_ERROR, "MobGroup %i is invalid", thisgroup->id); + flag = false; + break; + } + int tactical = atoi(tmp); + CMob *thismob = new (nothrow) CMob; + if (thismob == NULL) { + Log(MSG_ERROR, "Error allocating memory"); + DB->QFree(); + return false; + } + thismob->amount = amount; + thismob->tactical = (unsigned int)tactical/2; + thismob->mobId = mobId; + thismob->thisnpc = GetNPCDataByID( thismob->mobId ); + thismob->mapdrop = GetDropData( thisgroup->map ); //hmm. We don't even use these drops anymore + thismob->mobdrop = GetDropData( thismob->thisnpc->dropid ); + if (thismob->thisnpc == NULL) { + Log(MSG_WARNING, "Invalid mobId - Mob %i will not be added", atoi(row[0])); + delete thismob; + continue; + } + if (thismob->tactical) + thisgroup->tacMobs.push_back(thismob); + else + thisgroup->basicMobs.push_back(thismob); + tmp = strtok(NULL, ",|"); + } + if (!flag) { + delete thisgroup; + continue; + } + MapList.Index[thisgroup->map]->MobGroupList.push_back(thisgroup); + mobGroups.push_back(thisgroup); + } + DB->QFree( ); + } + return true; +} + +bool CWorldServer::LoadMonsterSpawn( ) +{ + Log( MSG_INFO, "Loading SpawnZones data" ); + //clear the respawns data first + + for(int i=0;iMonsterSpawnList.clear(); + } + MYSQL_ROW row; + //MYSQL_RES *result = DB->QStore("SELECT id,map,montype,min,max,respawntime,points, spawntype, triggermontype, triggerammount, agressive, areatrigger, lim, spawnk FROM list_spawnareas"); + MYSQL_RES *result = DB->QStore("SELECT id, map, montype, min, max, respawntime, x, y, radius, spawntype, triggermontype, triggerammount, agressive, areatrigger, lim, spawnk, aip, refvar, refval FROM list_special_spawns"); + + if(result == NULL) + { + DB->QFree( ); + return false; + } + Log( MSG_INFO, "Loaded database data" ); + while(row = mysql_fetch_row(result)) + { + bool flag = true; + char* tmp; + CSpawnArea* thisspawn = new (nothrow) CSpawnArea; + if(thisspawn==NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + DB->QFree( ); + return false; + } + thisspawn->id = atoi(row[0]); + if(thisspawn->id > GServer->maxSpawnId) + GServer->maxSpawnId = thisspawn->id; + thisspawn->map = atoi(row[1]); + thisspawn->montype = atoi(row[2]); + thisspawn->min = 0;//atoi(row[3]); + thisspawn->max = atoi(row[4]); + thisspawn->respawntime = atoi(row[5]); + thisspawn->point.x = atoi(row[6]); + if(thisspawn->map == 3) + thisspawn->point.x -= 100; //for some reason some JP map has an X offset. + thisspawn->point.y = atoi(row[7]); + thisspawn->radius = atoi(row[8]); + if (thisspawn->respawntime == 0) + thisspawn->respawntime=5; + thisspawn->amon = 0; + thisspawn->lastRespawnTime = clock(); + thisspawn->type = atoi(row[9]); + thisspawn->triggertype = atoi(row[10]); + thisspawn->triggeramount = atoi(row[11]); + thisspawn->aggressive = atoi(row[12]); + thisspawn->typeoftrigger = atoi(row[13]); + thisspawn->limit = atoi(row[14]); + thisspawn->spawnkey = atoi(row[15]); + thisspawn->AI = atoi(row[16]); + thisspawn->RefVar = atoi(row[17]); + thisspawn->RefVal = atoi(row[18]); + thisspawn->triggercount = 0; + //if(thisspawn->id >= 10000) //tower defense spawn + // thisspawn->triggercount = 10000; //disables the spawn until it is reset to 0 + //if(flag) + //{ + thisspawn->thisnpc = GetNPCDataByID( thisspawn->montype ); + if(thisspawn->thisnpc == NULL) + { + Log( MSG_WARNING, "Invalid montype - Spawn %i will not be added", thisspawn->id ); + delete thisspawn; + continue; + } + MapList.Index[thisspawn->map]->MonsterSpawnList.push_back( thisspawn ); + //} + } + DB->QFree( ); + return true; +} + +bool CWorldServer::LoadNPCs( ) +{ + Log( MSG_INFO, "Loading NPC spawn " ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT type,map,dir,x,y,dialogid FROM list_npcs"); + if(result == NULL) return false; + while(row = mysql_fetch_row(result)) + { + CNPC* thisnpc = new (nothrow) CNPC; + if(thisnpc == NULL) + { + Log(MSG_ERROR, "Error allocing memory" ); + DB->QFree( ); + return false; + } + thisnpc->clientid = GetNewClientID(); + thisnpc->npctype = atoi(row[0]); + thisnpc->posMap = atoi(row[1]); + thisnpc->dir = (float)atof(row[2]); + thisnpc->pos.x = (float)atof(row[3]); + thisnpc->pos.y = (float)atof(row[4]); + thisnpc->thisnpc = GetNPCDataByID( thisnpc->npctype ); + thisnpc->thisnpc->_EntityType = ENTITY_NPC; + if( thisnpc->thisnpc == NULL) + { + + delete thisnpc; + continue; + } + thisnpc->thisnpc->dialogid = atoi(row[5]); // This is global to NPC type + //Log( MSG_INFO, "creating npc type %i",thisnpc->npctype ); + MapList.Index[thisnpc->posMap]->AddNPC( thisnpc ); + thisnpc->thisnpc->eventid = 0; + } + DB->QFree( ); + return true; +} + +bool CWorldServer::LoadPYDropsData( ) +{ + Log( MSG_INFO, "Loading PYDrops Data" ); + MDropList.clear(); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id,type,min_level,max_level,prob,mob,map,alt FROM item_drops"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + while(row = mysql_fetch_row(result)) + { + CMDrops* newdrop = new (nothrow) CMDrops; + assert(newdrop); + newdrop->itemnum = atoi(row[0]); + newdrop->itemtype = atoi(row[1]); + newdrop->level_min = atoi(row[2]); + newdrop->level_max = atoi(row[3]); + newdrop->prob = atoi(row[4]); + newdrop->mob = atoi(row[5]); + newdrop->map = atoi(row[6]); + char *tmp; + if((tmp = strtok( row[7] , "|"))==NULL) + newdrop->alt[0]=0; + else + newdrop->alt[0]=atoi(tmp); + for(unsigned int i=1;i<8; i++) + { + if((tmp = strtok( NULL , "|"))==NULL) + newdrop->alt[i]=0; + else + newdrop->alt[i]=atoi(tmp); + } + //if(newdrop->mob != 0) + //Log( MSG_INFO, "found a non zero mob value %i",newdrop->mob ); + MDropList.push_back( newdrop ); + } + DB->QFree( ); + Log( MSG_INFO, "PYDrops loaded" ); + return true; +} + +bool CWorldServer::LoadNewDrops( ) +{ + Log( MSG_INFO, "Loading New Drops Data" ); + MDropList.clear(); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT * FROM new_drops"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + while(row = mysql_fetch_row(result)) + { + CMDrops* newdrop = new (nothrow) CMDrops; + assert(newdrop); + + UINT readIndex = 1; + newdrop->Dropid = atoi(row[0]); + for(int i=0;i<50;i++) + { + newdrop->item[i] = atoi(row[readIndex]); + readIndex++; + newdrop->chance[i] = atoi(row[readIndex]); + readIndex++; + if(i == 0) + { + newdrop->RunningTotal[i] = newdrop->chance[i]; + } + else + { + newdrop->RunningTotal[i] = newdrop->RunningTotal[i-1] + newdrop->chance[i]; + } + //if(newdrop->Dropid == 26) + // Log( MSG_INFO, "item %i chance %i total %i",newdrop->item[i],newdrop->chance[i],newdrop->RunningTotal[i] ); + + } + newdrop->TotalChance = newdrop->RunningTotal[49]; + MDropList.push_back( newdrop ); + } + DB->QFree( ); + Log( MSG_INFO, "New Drops loaded" ); + return true; +} + +bool CWorldServer::LoadSkillBookDropsData( ) +{ + Log( MSG_INFO, "Loading Skillbook data" ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id,itemtype,min,max,prob FROM list_skillbooks"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + int c = 0; + while(row = mysql_fetch_row(result)) + { + + c++; + CMDrops* newdrop = new (nothrow) CMDrops; + assert(newdrop); + newdrop->itemnum = atoi(row[0]); + newdrop->itemtype = atoi(row[1]); + newdrop->level_min = atoi(row[2]); + newdrop->level_max = atoi(row[3]); + newdrop->prob = atoi(row[4]); + SkillbookList.push_back( newdrop ); + } + DB->QFree( ); + Log( MSG_INFO, "Skillbook Data loaded" ); + return true; +} + +bool CWorldServer::LoadLTB( ) +{ + Log(MSG_INFO, "Loading LTB strings"); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id,npc,ltbstring,questnpc,questltb FROM ltb"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + int index = 0; + while( row = mysql_fetch_row(result) ) + { + strcpy(GServer->Ltbstring[atoi(row[0])].NPCname,row[1]); + strcpy(GServer->Ltbstring[atoi(row[0])].LTBstring,row[2]); + strcpy(GServer->Ltbstring[atoi(row[0])].QuestNpcName,row[3]); + strcpy(GServer->Ltbstring[atoi(row[0])].QuestLTB,row[4]); + } + DB->QFree( ); + Log( MSG_INFO, "LTB Data Loaded" ); + return true; +} + +bool CWorldServer::LoadConfig( ) +{ + Log( MSG_INFO, "Loading database config" ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT exp_rate, drop_rate, zuly_rate, blue_chance, stat_chance, slot_chance, refine_chance, rare_refine, kill_on_fail, spawntype, player_damage, monster_damage, player_acc, monster_acc, pvp_acc, skill_damage, dropmultiple, attkmult, skillmult, dropdelay, basedroprate, ktpointrate, movespeedmod, maxlevel FROM list_config"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + if (mysql_num_rows(result) == 0) + { + DB->QFree( ); + return false; + } + while( row = mysql_fetch_row(result) ) + { + GServer->Config.EXP_RATE = atoi(row[0]); + GServer->Config.DROP_RATE = atoi(row[1]); + GServer->Config.ZULY_RATE = atoi(row[2]); + GServer->Config.BlueChance = atoi(row[3]); + GServer->Config.StatChance = atoi(row[4]); + GServer->Config.SlotChance = atoi(row[5]); + GServer->Config.RefineChance = atoi(row[6]); + GServer->Config.Rare_Refine = atoi(row[7]); + GServer->Config.KillOnFail = atoi(row[8]); + GServer->Config.SpawnType = atoi(row[9]); + GServer->Config.PlayerDmg = atoi(row[10]); + GServer->Config.MonsterDmg = atoi(row[11]); + // Not implemented in server yet - Drakia + //GServer->Config.PlayerAcc = atoi(row[12]); + //GServer->Config.MonsterAcc = atoi(row[13]); + //GServer->Config.PvpAcc = atoi(row[14]); + //GServer->Config.SkillDmg = atoi(row[15]); + GServer->Config.DropMultiple = atoi(row[16]); + GServer->Config.AttkMult = atoi(row[17]); + GServer->Config.SkillMult = atoi(row[18]); + GServer->Config.DropDelay = atoi(row[19]); + GServer->Config.BASE_DROP_CHANCE = atoi(row[20]); + GServer->Config.KTPointRate = atoi(row[21]); + GServer->MOVE_SPEED_MOD = atoi(row[22]); + GServer->Config.MaxLevel = atoi(row[23]); + } + DB->QFree( ); + Log( MSG_INFO, "Config Data Loaded" ); + return true; +} + + +bool CWorldServer::LoadMonsters( ) +{ + Log( MSG_INFO, "Monsters Spawn" ); + // Do our monster spawnin + + //old method + /*for(UINT i=0;iMonsterSpawnList.size();j++) + { + CSpawnArea* thisspawn = thismap->MonsterSpawnList.at(j); + thisspawn->mapdrop = GetDropData( thisspawn->map ); + thisspawn->mobdrop = GetDropData( thisspawn->thisnpc->dropid ); + for(UINT k=0;kmax;k++) + { + fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount ); + thismap->AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id ); + } + } + }*/ + + //PY method + if(GServer->Config.SpawnType == 1) + { + Log( MSG_INFO,"PY method"); + for(UINT i=0;iMonsterSpawnList.size();j++) + { + CSpawnArea* thisspawn = thismap->MonsterSpawnList.at(j); + if(thisspawn->id >= 10000)continue; //don't spawn the tower def monsters + //thisspawn->mapdrop = GetDropData( thisspawn->map ); + //thisspawn->mobdrop = GetDropData( thisspawn->thisnpc->dropid ); + for(UINT k=0;kmax;k++) + { + if( thisspawn->type == 1 ) + { + fPoint position = RandInCircle( thisspawn->point, thisspawn->radius ); + //fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount ); + thismap->AddMonster( thisspawn->montype, position, 0, thisspawn->id, thisspawn->aggressive, false, thisspawn->limit); + } + } + } + } + //Log( MSG_INFO, "Finished Monsters Spawns" ); + return true; + } + + //IFO method + if(GServer->Config.SpawnType == 0) + { + Log(MSG_LOAD,"IFO method"); + for (UINT i = 0; i < MapList.Map.size(); i++) + { + CMap *thismap = MapList.Map.at(i); + for (UINT j = 0; j < thismap->MobGroupList.size(); j++) + { + CMobGroup* thisgroup = thismap->MobGroupList.at(j); + // Load some basic mobs onto map + for (UINT k = 0; k < thisgroup->limit; k++) + { + CMob* thismob = thisgroup->basicMobs.at(thisgroup->curBasic); + thisgroup->curBasic++; + if (thisgroup->curBasic >= thisgroup->basicMobs.size()) thisgroup->curBasic = 0; + for (UINT l = 0; l < thismob->amount; l++) + { + fPoint position = RandInCircle( thisgroup->point, thisgroup->range ); + thismap->AddMonster( thismob->mobId, position, 0, thisgroup->id,1 ); + } + } + } + } + } + return true; +} + +/* +bool CWorldServer::LoadUpgrade( )//for succes rate +{ + Log( MSG_INFO, "Loading Refine Data " ); + FILE* fh = NULL; + fh = fopen("data/refine_data.csv", "r"); + if(fh==NULL) + { + Log(MSG_ERROR, "\nError loading file data/refine_data.csv" ); + return false; + } + char line[50]; + fgets( line, 50, fh );// this is the column name + while(!feof(fh)) + { + memset( &line, '\0', 50 ); + fgets( line, 50, fh ); + upgrade[GetUIntValue(",",&line)] = GetUIntValue(","); + } + fclose(fh); + return true; +} */ + +bool CWorldServer::LoadUpgrade( )//Load success rates for Bindrune and Talismans from STB +{ + Log( MSG_INFO, "Loading Upgrades data " ); + for (unsigned int i = 0; iQExecute("UPDATE accounts set online=false"); + return true; +} + +bool CWorldServer::LoadEquip( ) +{ + Log( MSG_INFO, "Loading Equip Data " ); + for(int j=0;j<9;j++) + { + EquipList[j+1].STBMax = STB_ITEM[j].rowcount; + for(unsigned int i=0;iid = i; + newequip->itemtype = (j+1); + newequip->equiptype = (j+1); + //get back to this later. It's a pain in the ass reading a string out of the STB + //strcpy(newequip->name, STB_ITEM[j].rows[i][0]); + newequip->type = STB_ITEM[j].rows[i][4]; + newequip->price = STB_ITEM[j].rows[i][5]; + newequip->pricerate = STB_ITEM[j].rows[i][6]; + newequip->weight = STB_ITEM[j].rows[i][7]; + newequip->quality = STB_ITEM[j].rows[i][8]; + newequip->level = STB_ITEM[j].rows[i][13]; + newequip->material = STB_ITEM[j].rows[i][14]; + newequip->unionpoint = STB_ITEM[j].rows[i][15]; + newequip->durability = STB_ITEM[j].rows[i][29]; + newequip->defense = STB_ITEM[j].rows[i][31]; + newequip->magicresistence = STB_ITEM[j].rows[i][32]; + newequip->attackdistance = STB_ITEM[j].rows[i][33];//Speed of travel/Range + if(newequip->equiptype == SHOE) + { + newequip->movespeed = STB_ITEM[j].rows[i][33]; + } + else + { + newequip->movespeed = 0; + } + + if (STB_ITEM[j].fieldcount>35) + { + newequip->attackpower = STB_ITEM[j].rows[i][35]; + newequip->attackspeed = STB_ITEM[j].rows[i][36]; + newequip->magicattack = STB_ITEM[j].rows[i][37]; //new. Staffs and wands are designated as magic weapons. + if(newequip->magicattack > 1)newequip->magicattack = 0; //all the blue named spears have weird numbers here. Maybe why NA had a massive damage glitch with them. + } + else + { + newequip->attackpower = 0; + newequip->attackspeed = 0; + newequip->magicattack = 0; + } + + + for(int k=0;k<3;k++) + newequip->occupation[k] = STB_ITEM[j].rows[i][(16+k)]; + for(int k=0;k<2;k++) + newequip->condition1[k] = STB_ITEM[j].rows[i][(19+k)]; + for(int k=0;k<2;k++) + newequip->condition2[k] = STB_ITEM[j].rows[i][(21+k)]; + for(int k=0;k<2;k++) + newequip->stat1[k] = STB_ITEM[j].rows[i][(24+k)]; + for(int k=0;k<2;k++) + newequip->stat2[k] = STB_ITEM[j].rows[i][(27+k)]; + + newequip->refinetype = STB_ITEM[j].rows[i][45]; //new this is the recipe for refining the item + newequip->itemgrade = STB_ITEM[j].rows[i][46]; + newequip->raretype = STB_ITEM[j].rows[i][47]; + + if(newequip->id > 4999) + { + //Log(MSG_WARNING,"Equipment type %i ID %i is outside of accepted range",newequip->equiptype, newequip->id ); + //item not added + //return false; + } + else + { + EquipList[newequip->equiptype].Data.push_back( newequip ); + EquipList[newequip->equiptype].Index[newequip->id] = newequip; // Index to read more quickly the data + } + //delete newequip; + } + } + return true; +} + +bool CWorldServer::LoadJemItem( )//STB_ITEM[10] +{ + Log( MSG_INFO, "Loading Jem Data " ); + EquipList[11].STBMax = STB_ITEM[10].rowcount; + for(unsigned int i=0;iid = i; + thisjem->itemtype = 11; + thisjem->type = STB_ITEM[10].rows[i][4]; + thisjem->price = STB_ITEM[10].rows[i][5]; + thisjem->pricerate = STB_ITEM[10].rows[i][6]; + thisjem->weight = STB_ITEM[10].rows[i][7]; + thisjem->quality = STB_ITEM[10].rows[i][8]; + thisjem->material = STB_ITEM[10].rows[i][14]; + thisjem->unionpoint = STB_ITEM[10].rows[i][15]; + thisjem->stat1[0] = STB_ITEM[10].rows[i][16]; + thisjem->stat1[1] = STB_ITEM[10].rows[i][17]; + thisjem->stat2[0] = STB_ITEM[10].rows[i][18]; + thisjem->stat2[1] = STB_ITEM[10].rows[i][19]; + if(thisjem->id > 999) + { + Log(MSG_WARNING,"Gem ID %i outside of accepted range",thisjem->id ); + return false; + } + JemList.Data.push_back( thisjem ); + JemList.Index[thisjem->id] = thisjem; + } + return true; +} + +bool CWorldServer::LoadNaturalItem( )//STB_ITEM[11] +{ + Log( MSG_INFO, "Loading Natural Data " ); + for(unsigned int i=0;iid = i; + thisnatural->itemtype = 12; + thisnatural->type = STB_ITEM[11].rows[i][4]; + thisnatural->price = STB_ITEM[11].rows[i][5]; + thisnatural->pricerate = STB_ITEM[11].rows[i][6]; + thisnatural->weight = STB_ITEM[11].rows[i][7]; + thisnatural->quality = STB_ITEM[11].rows[i][8]; + thisnatural->unionpoint = STB_ITEM[11].rows[i][15]; + thisnatural->pricevalue = STB_ITEM[11].rows[i][16]; + if(thisnatural->id > 999) + { + Log(MSG_WARNING,"Natural item ID %i outside of accepted range",thisnatural->id ); + return false; + } + NaturalList.Data.push_back( thisnatural ); + NaturalList.Index[thisnatural->id] = thisnatural; + } + return true; +} + +bool CWorldServer::LoadPatItem( ) +{ + Log( MSG_INFO, "Loading PAT Data " ); + EquipList[14].STBMax = STB_ITEM[13].rowcount; + for(unsigned int i=0;iid = i; + newpat->itemtype = 14; + newpat->type = STB_ITEM[13].rows[i][4]; + newpat->price = STB_ITEM[13].rows[i][5]; + newpat->pricerate = STB_ITEM[13].rows[i][6]; + newpat->weight = STB_ITEM[13].rows[i][7]; + newpat->quality = STB_ITEM[13].rows[i][8]; + newpat->material = STB_ITEM[13].rows[i][14]; + newpat->unionpoint = STB_ITEM[13].rows[i][15]; + newpat->partversion = STB_ITEM[13].rows[i][17]; + newpat->level = STB_ITEM[13].rows[i][22]; // extra field added rev 70 + newpat->condition[1] = STB_ITEM[13].rows[i][24]; // extra field added rev 70 + newpat->modifier[1] = STB_ITEM[13].rows[i][25]; // extra field added rev 70 + newpat->condition[2] = STB_ITEM[13].rows[i][27]; // extra field added rev 70 + newpat->modifier[2] = STB_ITEM[13].rows[i][28]; // extra field added rev 70 + newpat->durability = STB_ITEM[13].rows[i][29]; + newpat->maxfuel = STB_ITEM[13].rows[i][31]; + newpat->fuelcons = STB_ITEM[13].rows[i][32]; + newpat->speed = STB_ITEM[13].rows[i][33]; + newpat->attackdistance = STB_ITEM[13].rows[i][35]; + newpat->attackpower = STB_ITEM[13].rows[i][36]; + newpat->attackspeed = STB_ITEM[13].rows[i][37]; + if(newpat->id > 999) + { + Log(MSG_WARNING,"PAT item ID %i outside of accepted range",newpat->id ); + return false; + } + PatList.Data.push_back( newpat ); + PatList.Index[newpat->id] = newpat; + } + return true; +} + +bool CWorldServer::LoadBreak( ) //STB_BREAK +{ + Log( MSG_INFO, "Bypassing Break Data till we actually add some"); + return true; + unsigned int STBcol = 3; + unsigned int Ind = 1; + for(unsigned int i=0;iid = i; + newbreak->ItemId = STB_BREAK.rows[i][2]; + for(unsigned int j=1;j<=20;j++) + { + if(i > 700) + Log( MSG_INFO, "STB row %i item %i",i,j); + for(unsigned int k=1;k<=4;k++) + { + newbreak->Mat[Ind]=STB_BREAK.rows[i][STBcol]; + newbreak->Min[Ind]=STB_BREAK.rows[i][STBcol+3]; + newbreak->Max[Ind]=STB_BREAK.rows[i][STBcol+4]; + newbreak->Chance[Ind]=STB_BREAK.rows[i][STBcol+5]; + } + STBcol += 4; + } + newbreak->MinDis=STB_BREAK.rows[i][STBcol]; + newbreak->MaxDis=STB_BREAK.rows[i][STBcol+1]; + newbreak->ItemCount=STB_BREAK.rows[i][STBcol+2]; + if(newbreak->id > 5000) + { + Log(MSG_WARNING,"Break item ID %i outside of accepted range",newbreak->id ); + return false; + } + BreakList.Data.push_back( newbreak ); + BreakList.Index[newbreak->id] = newbreak; + } + Log( MSG_INFO, "Loaded Break Data "); + return true; +} + +bool CWorldServer::LoadProductItem( )//STB_PRODUCT +{ + Log( MSG_INFO, "Loading Product Data " ); + for(unsigned int i=0;iid = i; + newproduct->matType=STB_PRODUCT.rows[i][1]; + newproduct->item[0]=STB_PRODUCT.rows[i][2]; + newproduct->itemtype[0]=(int)(newproduct->item[0] / 1000); + newproduct->itemid[0]=newproduct->item[0] - (newproduct->itemtype[0] * 1000); + newproduct->amount[0]=STB_PRODUCT.rows[i][3]; + newproduct->item[1]=STB_PRODUCT.rows[i][4]; + newproduct->itemtype[1]=(int)(newproduct->item[1] / 1000); + newproduct->itemid[1]=newproduct->item[1] - (newproduct->itemtype[1] * 1000); + newproduct->amount[1]=STB_PRODUCT.rows[i][5]; + newproduct->item[2]=STB_PRODUCT.rows[i][6]; + newproduct->itemtype[2]=(int)(newproduct->item[2] / 1000); + newproduct->itemid[2]=newproduct->item[2] - (newproduct->itemtype[2] * 1000); + newproduct->amount[2]=STB_PRODUCT.rows[i][7]; + newproduct->item[3]=STB_PRODUCT.rows[i][8]; + newproduct->itemtype[3]=(int)(newproduct->item[3] / 1000); + newproduct->itemid[3]=newproduct->item[3] - (newproduct->itemtype[3] * 1000); + newproduct->amount[3]=STB_PRODUCT.rows[i][9]; + if(newproduct->id > 1999) + { + Log(MSG_WARNING,"Product ID %i outside of accepted range",newproduct->id ); + return false; + } + ProductList.Data.push_back( newproduct ); + ProductList.Index[newproduct->id] = newproduct; + } + return true; +} + +bool CWorldServer::LoadSellData( )//STB_SELL +{ + Log( MSG_INFO, "Loading Sell Data" ); + //Log(MSG_WARNING,"STB rowcount: %i",STB_SELL.rowcount ); + for(unsigned int i=0;iid = i; + for(unsigned int j=2;jitem[j-2] = STB_SELL.rows[i][j]; + } + if(newsell->id > 999) + { + Log(MSG_WARNING,"List_Sell ID %i outside of accepted range",newsell->id ); + return false; + } + SellList.Data.push_back( newsell ); + SellList.Index[newsell->id] = newsell; + } + Log( MSG_INFO, "Loaded Sell Data" ); + return true; +} + +bool CWorldServer::LoadConsItem( ) +{ + Log( MSG_INFO, "Loading Consumable Data " ); + for(unsigned int i=0;iid = i; + newuse->itemtype = 10; + newuse->restriction = STB_ITEM[9].rows[i][3]; + newuse->type = STB_ITEM[9].rows[i][4]; + newuse->price = STB_ITEM[9].rows[i][5]; + newuse->pricerate = STB_ITEM[9].rows[i][6]; + newuse->weight = STB_ITEM[9].rows[i][7]; + newuse->quality = STB_ITEM[9].rows[i][8]; + newuse->unionpoint = STB_ITEM[9].rows[i][15]; + newuse->pricevalue = STB_ITEM[9].rows[i][16]; + newuse->usecondition[0]= STB_ITEM[9].rows[i][17]; + newuse->usecondition[1]= STB_ITEM[9].rows[i][18]; + newuse->useeffect[0]= STB_ITEM[9].rows[i][19]; + newuse->useeffect[1]= STB_ITEM[9].rows[i][20]; + //if(newuse->id == 285) + // Log(MSG_WARNING,"Usitem useeffect[1] = %i",newuse->useeffect[1] ); + if(newuse->id > 1999) + { + Log(MSG_WARNING,"Usitem ID %i outside of accepted range",newuse->id ); + return false; + } + UseList.Data.push_back( newuse ); + UseList.Index[newuse->id] = newuse; + } + return true; +} + +bool CWorldServer::LoadWayPoints() +{ + Log( MSG_INFO, "Loading Waypoints" ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT mapid,wpnum,mapx,mapy,wptype FROM list_waypoints"); + if(result==NULL) + { + DB->QFree( ); + return false; + } + if (mysql_num_rows(result) == 0) + { + DB->QFree( ); + Log( MSG_INFO, "list_waypoints currently contains no data" ); + return true; + } + // first clear the waypoint list + for(int i=0;i<100;i++) + { + for(int j=0;j<100;j++) + { + GServer->WPList[i][j].pos.x = 0; + GServer->WPList[i][j].pos.y = 0; + GServer->WPList[i][j].WPType = 0; + } + } + while( row = mysql_fetch_row(result) ) + { + int thismap = atoi(row[0]); + int thisindex = atoi(row[1]); + GServer->WPList[thismap][thisindex].pos.x = atof(row[2]); + GServer->WPList[thismap][thisindex].pos.y = atof(row[3]); + GServer->WPList[thismap][thisindex].WPType = atoi(row[4]); + } + DB->QFree( ); + return true; +} + +bool CWorldServer::LoadZoneData( ) +{ + Log( MSG_INFO, "Loading Zone Data " ); + + for(unsigned int i=0;iid = i; + newzone->dungeon = (STB_ZONE.rows[i][4] == 1); + newzone->dayperiod = STB_ZONE.rows[i][13]; + newzone->morningtime = STB_ZONE.rows[i][14]; + newzone->daytime = STB_ZONE.rows[i][15]; + newzone->eveningtime = STB_ZONE.rows[i][16]; + newzone->nighttime = STB_ZONE.rows[i][17]; + newzone->allowpvp = STB_ZONE.rows[i][18]; + newzone->allowpat = STB_ZONE.rows[i][30]==0?true:false; + if (i==20||i==22) + newzone->ghost = 0; + else + newzone->ghost = 1; + newzone->MapTime = 0; + //TD stuff + newzone->TDNextMon = 0; + newzone->TDNextSpawn = 0; + newzone->lastTDSpawnTime = clock(); + newzone->LastUpdate = clock( ); + newzone->CurrentTime = 0; + newzone->TDMobDelay = 1500; + for(unsigned int j=0;j<=100;j++) + { + newzone->TDMobList[j] = 0; + } + //end TD stuff + newzone->MonsterSpawnList.clear(); + if(GServer->Config.SpawnType == 0) + newzone->MobGroupList.clear(); + newzone->crystalhealth = 100; + newzone->mapXPRate = 2; //always 2 as long as the crystal is alive + MapList.Map.push_back(newzone); + MapList.Index[newzone->id] = newzone; + } + return true; +} + +bool CWorldServer::LoadItemStats( ) +{ + Log( MSG_INFO, "Loading Item Stats " ); + for(unsigned int i=0;irowcount, 4, 1, fh ); + fread( &data->fieldcount, 4, 1, fh ); + data->rowcount--; + data->fieldcount--; + // Read the data + fseek( fh, dataoffset, SEEK_SET ); + int* tmp = new int[data->rowcount*data->fieldcount]; + data->rows = new int*[data->rowcount]; + for( unsigned i = 0; i < data->rowcount; i++ ) data->rows[i] = &tmp[i*data->fieldcount]; + for( unsigned j = 0; j < data->rowcount*data->fieldcount; j++ ) { + fread( &fieldlen, 2, 1, fh ); + fread( tmpfield, 1, fieldlen, fh ); + tmpfield[fieldlen]=0; + tmp[j] = atoi( tmpfield ); + } + } + + return 0; +} + +//void STBFreeData( CSTBData* data ) +//{ +// delete [] data->rows[0]; +// delete [] data->rows; +//} diff --git a/Src/World Server/stbhandler.cpp b/Src/World Server/stbhandler.cpp new file mode 100644 index 0000000..8df1cc4 --- /dev/null +++ b/Src/World Server/stbhandler.cpp @@ -0,0 +1,53 @@ +// Written by Brett19 +#include "worldserver.h" + +// ----------------------------------------------------------------------------------------- +// STB reading shit +// ----------------------------------------------------------------------------------------- +int STBStoreData( const char* filename, CSTBData* data ) +{ + char tmpfield[512]; + unsigned dataoffset = 0; + unsigned short fieldlen = 0; + + FILE* fh = fopen( filename, "rb" ); + if ( fh == 0 ) + { + Log( MSG_ERROR, "Could not load STB '%s'", filename ); + return 1; + } + else + { + // Read the header + fseek( fh, 4, SEEK_SET ); + fread( &dataoffset, 4, 1, fh ); + fread( &data->rowcount, 4, 1, fh ); + fread( &data->fieldcount, 4, 1, fh ); + data->rowcount--; + data->fieldcount--; + // Read the data + fseek( fh, dataoffset, SEEK_SET ); + int* tmp = new int[data->rowcount * data->fieldcount]; + data->rows = new int*[data->rowcount]; + for( unsigned i = 0; i < data->rowcount; i++ ) + { + data->rows[i] = &tmp[i * data->fieldcount]; + } + for( unsigned j = 0; j < data->rowcount * data->fieldcount; j++ ) + { + fread( &fieldlen, 2, 1, fh ); + fread( tmpfield, 1, fieldlen, fh ); + tmpfield[fieldlen]=0; + tmp[j] = atoi( tmpfield ); + } + fclose( fh ); + } + + return 0; +} + +void STBFreeData( CSTBData* data ) +{ + delete [] data->rows[0]; + delete [] data->rows; +} diff --git a/Src/World Server/stbhandler.h b/Src/World Server/stbhandler.h new file mode 100644 index 0000000..b15ec6a --- /dev/null +++ b/Src/World Server/stbhandler.h @@ -0,0 +1,11 @@ +// Written by Brett19 +#include "worldserver.h" + +struct CSTBData { + unsigned rowcount; + unsigned fieldcount; + int** rows; +}; + +int STBStoreData( const char* filename, CSTBData* data ); +void STBFreeData( CSTBData* data ); diff --git a/Src/World Server/stlhandler.cpp b/Src/World Server/stlhandler.cpp new file mode 100644 index 0000000..23e375e --- /dev/null +++ b/Src/World Server/stlhandler.cpp @@ -0,0 +1,137 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +//LMA: Code taken from IFONPCDUMPER from Drakia and adpated. + +#include "worldserver.h" + + + +//Reading and storing a STL in a single map :) +int STLStoreData( const char* filename, int family) +{ + entry** index; + map table; + char tempfield[512]; + UINT entryCount; + UINT langCount; + int temp=0; + + + FILE* fh = fopen( filename, "rb" ); + if (fh==0) + { + Log( MSG_ERROR, "Could not load STL '%s'", filename ); + return 0; + } + + fread(&temp,1,1,fh); + fread(tempfield,1,temp,fh); + fread(&entryCount,4,1,fh); + + if(family == 6) + Log(MSG_DEBUG,"%s:: %i",filename,entryCount); + + index = new entry*[entryCount]; + for (UINT i = 0; i < entryCount; i++) + { + entry item; + temp=0; + fread(&temp,1,1,fh); + fread(tempfield,1,temp,fh); + tempfield[temp]=0; + + for(int k=0;k=48&&tempfield[k]<=57) + break; + tempfield[k]=48; + } + + item.stringId=atoi(tempfield); + fread(&item.id,4,1,fh); + table[item.stringId] = item; + index[i] = &table[item.stringId]; + } + + vector langOffset; + fread(&langCount,4,1,fh); + + for (UINT i = 0; i < langCount; i++) + { + temp=0; + fread(&temp,4,1,fh); + langOffset.push_back(temp); + } + + + UINT i=STL_ENGLISH; + fseek( fh, langOffset.at(i), SEEK_SET ); + + for (UINT j = 0; j < entryCount; j++) + { + entry* Entry = index[j]; + language lang; + temp=0; + fread(&temp,4,1,fh); + lang.offset=temp; + Entry->lang.push_back(lang); + } + + for (UINT j = 0; j < entryCount; j++) + { + entry* Entry = index[j]; + //language *item = &Entry->lang.at(i); + language *item = &Entry->lang.at(0); + fseek( fh, item->offset, SEEK_SET ); + temp=0; + fread(&temp,1,1,fh); + + //no text? + if(temp==0) + { + continue; + } + + fread(item->text,1,temp,fh); + item->text[temp]=0; + + //LMA: let's do the map ^_^ + UINT mapid=family*100000+index[j]->stringId; + if(GServer->STLNameList.find(mapid)!=GServer->STLNameList.end()) + { + Log(MSG_WARNING,"STL ID %u (line %i) already in map %s",mapid,j,GServer->STLNameList[mapid]); + continue; + } + + char* new_text=new char[temp+1]; + memcpy(new_text,item->text,temp); + new_text[temp]=0; + GServer->STLNameList[mapid]=new_text; + } + + fclose( fh ); + delete[] index; + + + return 1; +} + diff --git a/Src/World Server/stlhandler.h b/Src/World Server/stlhandler.h new file mode 100644 index 0000000..d64581d --- /dev/null +++ b/Src/World Server/stlhandler.h @@ -0,0 +1,42 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +//LMA: Code taken from IFONPCDUMPER from Drakia and adpated. +#define STL_ENGLISH 1 +#include "worldserver.h" + + +int STLStoreData(const char* filename, int family); + +struct language +{ + char text[512]; + UINT offset; +}; + +struct entry +{ + UINT id; + UINT stringId; + vector lang; +}; + + diff --git a/Src/World Server/strhashing.cpp b/Src/World Server/strhashing.cpp new file mode 100644 index 0000000..e90aa2b --- /dev/null +++ b/Src/World Server/strhashing.cpp @@ -0,0 +1,19 @@ +// Written by Brett19 + +#include "worldserver.h" + +unsigned long MakeStrHash(const char *str) +{ + register unsigned long result = 0xDEADC0DE; + register unsigned long tkey = 0x7FED7FED; + register char c = 0; + while (*(str)){ + c = (*str<'a'||*str>'z')?*str:*str-32; + result += tkey; + tkey *= 0x21; + result ^= keys[c]; + tkey += result + c + 3; + str++; + } + return result; +} diff --git a/Src/World Server/strhashing.h b/Src/World Server/strhashing.h new file mode 100644 index 0000000..14eff75 --- /dev/null +++ b/Src/World Server/strhashing.h @@ -0,0 +1,20 @@ +const unsigned long keys[256] = { + 0x697A5, 0x6045C, 0xAB4E2, 0x409E4, 0x71209, 0x32392, 0xA7292, 0xB09FC, 0x4B658, 0xAAAD5, 0x9B9CF, 0xA326A, 0x8DD12, 0x38150, 0x8E14D, 0x2EB7F, + 0xE0A56, 0x7E6FA, 0xDFC27, 0xB1301, 0x8B4F7, 0xA7F70, 0xAA713, 0x6CC0F, 0x6FEDF, 0x2EC87, 0xC0F1C, 0x45CA4, 0x30DF8, 0x60E99, 0xBC13E, 0x4E0B5, + 0x6318B, 0x82679, 0x26EF2, 0x79C95, 0x86DDC, 0x99BC0, 0xB7167, 0x72532, 0x68765, 0xC7446, 0xDA70D, 0x9D132, 0xE5038, 0x2F755, 0x9171F, 0xCB49E, + 0x6F925, 0x601D3, 0x5BD8A, 0x2A4F4, 0x9B022, 0x706C3, 0x28C10, 0x2B24B, 0x7CD55, 0xCA355, 0xD95F4, 0x727BC, 0xB1138, 0x9AD21, 0xC0ACA, 0xCD928, + 0x953E5, 0x97A20, 0x345F3, 0xBDC03, 0x7E157, 0x96C99, 0x968EF, 0x92AA9, 0xC2276, 0xA695D, 0x6743B, 0x2723B, 0x58980, 0x66E08, 0x51D1B, 0xB97D2, + 0x6CAEE, 0xCC80F, 0x3BA6C, 0xB0BF5, 0x9E27B, 0xD122C, 0x48611, 0x8C326, 0xD2AF8, 0xBB3B7, 0xDED7F, 0x4B236, 0xD298F, 0xBE912, 0xDC926, 0xC873F, + 0xD0716, 0x9E1D3, 0x48D94, 0x9BD91, 0x5825D, 0x55637, 0xB2057, 0xBCC6C, 0x460DE, 0xAE7FB, 0x81B03, 0x34D8F, 0xC0528, 0xC9B59, 0x3D260, 0x6051D, + 0x93757, 0x8027F, 0xB7C34, 0x4A14E, 0xB12B8, 0xE4945, 0x28203, 0xA1C0F, 0xAA382, 0x46ABB, 0x330B9, 0x5A114, 0xA754B, 0xC68D0, 0x9040E, 0x6C955, + 0xBB1EF, 0x51E6B, 0x9FF21, 0x51BCA, 0x4C879, 0xDFF70, 0x5B5EE, 0x29936, 0xB9247, 0x42611, 0x2E353, 0x26F3A, 0x683A3, 0xA1082, 0x67333, 0x74EB7, + 0x754BA, 0x369D5, 0x8E0BC, 0xABAFD, 0x6630B, 0xA3A7E, 0xCDBB1, 0x8C2DE, 0x92D32, 0x2F8ED, 0x7EC54, 0x572F5, 0x77461, 0xCB3F5, 0x82C64, 0x35FE0, + 0x9203B, 0xADA2D, 0xBAEBD, 0xCB6AF, 0xC8C9A, 0x5D897, 0xCB727, 0xA13B3, 0xB4D6D, 0xC4929, 0xB8732, 0xCCE5A, 0xD3E69, 0xD4B60, 0x89941, 0x79D85, + 0x39E0F, 0x6945B, 0xC37F8, 0x77733, 0x45D7D, 0x25565, 0xA3A4E, 0xB9F9E, 0x316E4, 0x36734, 0x6F5C3, 0xA8BA6, 0xC0871, 0x42D05, 0x40A74, 0x2E7ED, + 0x67C1F, 0x28BE0, 0xE162B, 0xA1C0F, 0x2F7E5, 0xD505A, 0x9FCC8, 0x78381, 0x29394, 0x53D6B, 0x7091D, 0xA2FB1, 0xBB942, 0x29906, 0xC412D, 0x3FCD5, + 0x9F2EB, 0x8F0CC, 0xE25C3, 0x7E519, 0x4E7D9, 0x5F043, 0xBBA1B, 0x6710A, 0x819FB, 0x9A223, 0x38E47, 0xE28AD, 0xB690B, 0x42328, 0x7CF7E, 0xAE108, + 0xE54BA, 0xBA5A1, 0xA09A6, 0x9CAB7, 0xDB2B3, 0xA98CC, 0x5CEBA, 0x9245D, 0x5D083, 0x8EA21, 0xAE349, 0x54940, 0x8E557, 0x83EFD, 0xDC504, 0xA6059, + 0xB85C9, 0x9D162, 0x7AEB6, 0xBED34, 0xB4963, 0xE367B, 0x4C891, 0x9E42C, 0xD4304, 0x96EAA, 0xD5D69, 0x866B8, 0x83508, 0x7BAEC, 0xD03FD, 0xDA122 +}; + +unsigned long MakeStrHash(const char *str); diff --git a/Src/World Server/worldmap.cpp b/Src/World Server/worldmap.cpp new file mode 100644 index 0000000..27b6fa0 --- /dev/null +++ b/Src/World Server/worldmap.cpp @@ -0,0 +1,379 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ + +#include "worldmap.h" + +CMap::CMap( ) +{ + id = 0; + dayperiod = 0; + morningtime = 0; + daytime = 0; + eveningtime = 0; + nighttime = 0; + allowpvp = 0; + allowpat = 0; + MapTime = 0; + LastUpdate = clock( ); + CurrentTime = 0; + RespawnList.clear(); + MonsterSpawnList.clear(); + //if(GServer->Config.SpawnType == 0) + MobGroupList.clear(); + MonsterList.clear(); + DropsList.clear(); + PlayerList.clear(); + NPCList.clear(); + TeleGateList.clear(); + MonsterMutex = PTHREAD_MUTEX_INITIALIZER; + DropMutex = PTHREAD_MUTEX_INITIALIZER; +} + +CMap::~CMap( ) +{ + for(UINT i=0;iConfig.SpawnType == 0) + { + for(UINT i = 0; i < MobGroupList.size(); i++) + { + CMobGroup *thisgroup = MobGroupList.at(i); + for (UINT j = 0; j < thisgroup->basicMobs.size(); j++) + delete thisgroup->basicMobs.at(j); + for (UINT j = 0; j < thisgroup->tacMobs.size(); j++) + delete thisgroup->tacMobs.at(j); + delete thisgroup; + } + } + for(UINT i=0;iClearClientID( player->clientid ); + if(clearobject) + { + BEGINPACKET( pak, 0x794 ); + ADDWORD ( pak, player->clientid ); + GServer->SendToVisible( &pak, player, false ); + } + for(UINT i=0;iGetNPCDataByID( montype ); + if(thisnpc == NULL) + { + Log( MSG_WARNING, "Add Monster:: Invalid montype %u", montype ); + return NULL; + } + //if(AI == 999) Log( MSG_WARNING, "AddMonster Cast CNPCData " ); + CMonster* monster = new (nothrow) CMonster( position, montype, this->id, owner, spawnid ); + if(monster == NULL) + { + Log( MSG_WARNING, "Add Monster %u:: Error allocing memory",montype); + return NULL; + } + //if(AI == 999) Log( MSG_WARNING, "AddMonster Cast CMonster " ); + monster->thisnpc = thisnpc; + monster->Stats->MaxHP = monster->GetMaxHP( ); + monster->Stats->HP = monster->Stats->MaxHP; + monster->Stats->MaxMP = monster->GetMaxMP( ); + monster->Stats->MP = monster->Stats->MaxMP; + monster->thisnpc->_EntityType = ENTITY_MONSTER; + monster->Status->Stance = 0; // WTF is Status->Stance even used for???? + monster->Stats->stance = 0; //mWalking + monster->Status->spawnid = spawnid; //this way we can easily find which spawn a mob belongs to + monster->SetStats( ); + //if(AI == 999) Log( MSG_WARNING, "AddMonster Stats set " ); + if(AI != 0 && monster->monAI != 999) + { + //Set monster's AI to the defined value + //if(AI == 999) Log( MSG_WARNING, "AddMonster Setting monAI " ); + monster->monAI = AI; + CAip* script = NULL; + //if(AI == 999) Log( MSG_WARNING, "AddMonster Created CAip script " ); + if(monster->monAI == 999) AI = 30; //should create an AIP script that does nothing + for(unsigned j=0; j < GServer->AipList.size(); j++) + { + if (GServer->AipList.at(j)->AInumber == AI) + { + script = GServer->AipList.at(j); + break; + } + } + //if(monster->monAI == 999) Log( MSG_WARNING, "AddMonster finished scanning the AIPList " ); + if(script == NULL && monster->monAI != 999) + { + Log( MSG_WARNING, "Invalid AI script for monster type %i using AI %i", montype,monster->monAI ); + monster->AItimer = 4000; + } + else + { + //Log( MSG_WARNING, "Setting timer for monster type %i using AI %i", montype,monster->monAI ); + if(monster->monAI == 999) + { + monster->AItimer = 4000; + } + else + { + monster->AItimer = script->minTime * 1000; //set AI timer value for this monster + } + //Log( MSG_WARNING, "Set the timer from the script" ); + } + if (monster->AItimer == 0) + monster->AItimer = 4000; + //Log( MSG_WARNING, "AddMonster end of main clause " ); + } + else + { + //Set monster's AI to the default value for this monster type + monster->monAI = monster->thisnpc->AI; + CAip* script = NULL; + for(unsigned j=0; j < GServer->AipList.size(); j++) + { + if (GServer->AipList.at(j)->AInumber == monster->monAI) + { + script = GServer->AipList.at(j); + break; + } + } + if(script == NULL && monster->monAI != 999) //don't give error messag eif it's 999 + { + //Log( MSG_WARNING, "Invalid AI script for monster type %i using AI %i", montype,AI ); + monster->AItimer = 4000; + } + else + { + monster->AItimer = script->minTime * 1000; //set AI timer value for this monster + } + if (monster->AItimer == 0) + monster->AItimer = 4000; + //if(AI == 999) Log( MSG_WARNING, "AddMonster end of ELSE clause " ); + } + if(IsTD) + { + + //monster->thisnpc->AI = 331; + monster->monAI = 999; + monster->thisnpc->aggresive = 0; + monster->NextWayPoint = 1; + monster->Position->destiny = monster->Position->current; + monster->AItimer = 1000; //shorter testing time because it doesn't run an AIP + } + + if(limit == 0) + { + monster->Status->dayonly = false; + monster->Status->nightonly = false; + } + if(limit == 1) + { + monster->Status->dayonly = true; + monster->Status->nightonly = false; + } + if(limit == 2) + { + monster->Status->dayonly = false; + monster->Status->nightonly = true; + } + if(aggro != 1) + monster->thisnpc->aggresive = aggro; + monster->Stats->HP = monster->Stats->MaxHP; + /* BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x8005 ); + GServer->SendToVisible( &pak, monster);*/ + MonsterList.push_back( monster ); + if(AI == 999) Log( MSG_WARNING, "Added to monster list " ); + if(spawnid != 0) + { + if(GServer->Config.SpawnType == 1) + { + CSpawnArea* thisspawn = GServer->GetSpawnArea( spawnid, this->id ); + if(thisspawn!=NULL) + thisspawn->amon++; + } + if(GServer->Config.SpawnType == 0) + { + CMobGroup* thisgroup = GServer->GetMobGroup( spawnid, this->id ); + if(thisgroup != NULL) + thisgroup->active++; + } + } + monster->activecycle = GServer->RandNumber(1, 10); + monster->SpawnTime = clock( ); + monster->lastAiUpdate = clock(); + monster->hitcount = 0xFF; + //monster->OnSpawn( false ); + monster->hitcount = 0; + return monster; +} + + +// Delete a monster +bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i ) +{ + if(monster == NULL) return false; + GServer->ClearClientID( monster->clientid ); + if(monster->Position->respawn != 0) + { + if(GServer->Config.SpawnType == 1) + { + CSpawnArea* thisspawn = GServer->GetSpawnArea( monster->Position->respawn, monster->Position->Map ); + if(thisspawn != NULL) + { + if(thisspawn->amon >= thisspawn->max)// reset spawn timer if the spawn is full + thisspawn->lastRespawnTime = clock(); + thisspawn->amon--; + UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 2); + } + } + if(GServer->Config.SpawnType == 0) + { + CMobGroup* thisgroup = GServer->GetMobGroup( monster->Position->respawn, monster->Position->Map ); + if(thisgroup!=NULL) + { + if(thisgroup->active >= thisgroup->limit)// reset spawn timer if the spawn is full + thisgroup->lastRespawnTime = clock(); + thisgroup->active--; + thisgroup->basicKills++; + } + } + } + if(clearobject) // is this for clearing Summons? + { + //Log( MSG_INFO, "Deleting monster %i (clearobject)",monster->montype); + BEGINPACKET( pak, 0x799 ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, monster->clientid ); + ADDWORD ( pak, 0x8005 ); + //ADDDWORD ( pak, monster->Stats->HP ); + //ADDDWORD ( pak, 16 ); + GServer->SendToVisible( &pak, monster ); + } + if(i != 0) + { + //Log( MSG_INFO, "Deleting monster %i from monster list. montype: %i",i,monster->montype); + MonsterList.erase( MonsterList.begin()+i ); + //Log( MSG_INFO, "erased from list"); + delete monster; + //Log( MSG_INFO, "deleted"); + return true; + } + for(UINT j=0;jmontype); + MonsterList.erase( MonsterList.begin() + j ); + delete monster; + return true; + } + } + //Log( MSG_INFO, "Deleting monster %i from monster list anyway",monster->montype); + delete monster; + return false; +} + +//add a new drop +bool CMap::AddDrop( CDrop* drop ) +{ + drop->droptime = time(NULL); + DropsList.push_back( drop ); + return true; +} + +bool CMap::DeleteDrop( CDrop* drop ) +{ + BEGINPACKET( pak, 0x794 ); + ADDWORD ( pak, drop->clientid ); + GServer->SendToVisible( &pak, drop ); + GServer->ClearClientID( drop->clientid ); + for(UINT j=0;jclientid ); + GServer->SendToMap( &pak, npc->posMap ); + GServer->ClearClientID( npc->clientid ); + Log(MSG_INFO,"Sent message to players in map to delete NPC"); + for(UINT j=0;j RespawnList; // RespawnZones List + vector MonsterSpawnList; // Monster spawn in this map +#ifdef USEIFO + vector MobGroupList; // Spawn "Zones" +#endif + vector MonsterList; // Monster List in this map + vector DropsList; // Droped Items + vector PlayerList; // Client list in this map; + vector NPCList; // NPC in this map + vector TeleGateList; // Telegates from this map + std::vector PointList; + + pthread_mutex_t MonsterMutex, DropMutex; +}; + +#endif diff --git a/Src/World Server/worldmap.h.bak b/Src/World Server/worldmap.h.bak new file mode 100644 index 0000000..2386ab2 --- /dev/null +++ b/Src/World Server/worldmap.h.bak @@ -0,0 +1,119 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + depeloped with Main erose/hrose source server + some change from the original eich source +*/ +#ifndef __ROSE_MAP__ +#define __ROSE_MAP__ +#include "worldserver.h" +#include "player.h" +#include "character.h" +#include "worldmonster.h" +#include "datatypes.h" +class CMonster; + +class CMap +{ + public: + CMap( ); + ~CMap( ); + + bool AddPlayer( CPlayer* player ); + bool RemovePlayer( CPlayer* player, bool clearobject=true ); + + //CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, bool GetDropData=false ); + CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, UINT aggro = 0, bool IsTD = false, UINT limit = 0, UINT AI = 0); + bool DeleteMonster( CMonster*, bool clearobject=false, UINT i=0 ); + bool AddDrop( CDrop* drop ); + bool DeleteDrop( CDrop* drop ); + bool AddNPC( CNPC* npc ); + bool DeleteNPC( CNPC* npc ); + + CMonster* ConverToMonster( CMonster* monster, UINT newmonttype, bool heal=true ); + CMonster* ConverToMonster( CNPC* npc, UINT newmonttype ); + + bool TeleportPlayer( CPlayer* player, fPoint coord, bool TelePassenger=true ); + bool pakTeleport( CPlayer* player, unsigned int Map, fPoint coord, bool TelePassenger ); + + CRespawnPoint* GetNearRespawn( CPlayer* player ); + CRespawnPoint* GetFirstRespawn( ); + CRespawnPoint* GetStartRespawn( ); + + CCharacter* GetCharInMap( unsigned int id ); + CPlayer* GetPlayerInMap( UINT id ); + CMonster* GetMonsterInMap( UINT id ); + CPlayer* GetCharIDInMap( UINT id ); + CDrop* GetDropInMap( UINT id ); + CNPC* GetNPCInMap( UINT id ); + void UpdateTime( ); + void CleanDrops( ); + void RespawnMonster( ); + void UpdateSpawnCounter( UINT montype, UINT spawnid, UINT key ); + bool IsNight( ); + + void LoadZon(strings zon); + void LoadData(strings zonpath, CStrStb* stbEvent); + void AddIfo(strings ifo, CStrStb* stbEvent); + + UINT id; // id from map + UINT dayperiod; // duration of 1 day + UINT morningtime; // morning start time + UINT daytime; // day start time + UINT eveningtime; // evening start time + UINT nighttime; // nighttime start time + UINT ZoneTime; // how many 6 minute segments since start of the day + UINT ghost; // ghosttime? + BYTE allowpvp; // pvp allowed? + bool dungeon; // Map is a dungeon? + bool allowpat; // pat allowed? + // Time values + UINT MapTime; // Past time in this map + UINT Date; + UINT hour; + UINT minute; + UINT ObjVar[20]; //for storing map variables used in AIP + clock_t LastUpdate;// Last time update in the map + UINT CurrentTime; // 0 = morning | 1 = day | 2 = evening | 3 = night | 4+ = invalid + //TDMobList + UINT TDMobList[100]; //List of TD monsters waiting to be spawned + UINT TDNextSpawn; //index pointer for next spawn in TDMobList + UINT TDNextMon; //index pointer for next addition to TDMobList + clock_t lastTDSpawnTime; //time of the last spawn + UINT TDMobDelay; // delay between spawns (ms) + UINT crystalhealth; // current health of the base crystal + UINT portalhealth; // current health of the spawn portal + UINT mapXPRate; // exp rate for this map + + + + vector RespawnList; // RespawnZones List + vector MonsterSpawnList; // Monster spawn in this map +#ifdef USEIFO + vector MobGroupList; // Spawn "Zones" +#endif + vector MonsterList; // Monster List in this map + vector DropsList; // Droped Items + vector PlayerList; // Client list in this map; + vector NPCList; // NPC in this map + vector TeleGateList; // Telegates from this map + std::vector PointList; + + pthread_mutex_t MonsterMutex, DropMutex; +}; + +#endif diff --git a/Src/World Server/worldpackets.cpp b/Src/World Server/worldpackets.cpp new file mode 100644 index 0000000..d495295 --- /dev/null +++ b/Src/World Server/worldpackets.cpp @@ -0,0 +1,4598 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Send Characters information +void CWorldServer::pakPlayer( CPlayer *thisclient ) +{ + unsigned cbyte = 0; + CRespawnPoint* thisrespawn = GetRespawnByID( thisclient->Position->respawn ); + if(thisclient->Position->saved == 20&&thisrespawn->destMap!=20) + thisclient->Position->saved = 22; + if(thisrespawn != NULL) + { + thisclient->Position->destiny = thisrespawn->dest; + thisclient->Position->current = thisclient->Position->destiny; + //Log(MSG_DEBUG,"(pakPlayer) Destiny and current set to X: %f Y: %f.",thisclient->Position->current.x,thisclient->Position->current.y); + thisclient->Position->Map = thisrespawn->destMap; + } + else + { + thisclient->Position->destiny.x = 5300; + thisclient->Position->destiny.y = 5200; + thisclient->Position->current = thisclient->Position->destiny; + thisclient->Position->Map = 1; + } + CMap* map = MapList.Index[thisclient->Position->Map]; + map->AddPlayer( thisclient ); + BEGINPACKET(pak, 0x715); + ADDBYTE( pak, thisclient->CharInfo->Sex ); // Sex + ADDWORD( pak, thisclient->Position->Map ); // Map + ADDFLOAT( pak, thisclient->Position->current.x*100 ); // Pos X + ADDFLOAT( pak, thisclient->Position->current.y*100 ); // Pos Y + ADDWORD( pak, thisclient->Position->saved ); + ADDDWORD( pak, thisclient->CharInfo->Face ); // Face + ADDDWORD( pak, thisclient->CharInfo->Hair ); // Hair + ADDDWORD( pak, BuildItemShow(thisclient->items[2]) ); // Cap + ADDDWORD( pak, BuildItemShow(thisclient->items[3]) ); // Body + ADDDWORD( pak, BuildItemShow(thisclient->items[5]) ); // Gloves + ADDDWORD( pak, BuildItemShow(thisclient->items[6]) ); // Boots + ADDDWORD( pak, BuildItemShow(thisclient->items[1]) ); // Face + ADDDWORD( pak, BuildItemShow(thisclient->items[4]) ); // Back + ADDDWORD( pak, BuildItemShow(thisclient->items[7]) ); // Weapon + ADDDWORD( pak, BuildItemShow(thisclient->items[8]) ); // SubWeapon + ADDBYTE( pak, 0x01 ); // Birth Stone + ADDBYTE( pak, 0x00 ); + ADDBYTE( pak, 0x00 ); + ADDWORD( pak, thisclient->CharInfo->Job ); // Job + ADDBYTE( pak, thisclient->Union_s->unionvar[0]); // Union Variable [0] + ADDBYTE( pak, 0x00 ); //rank + ADDBYTE( pak, 0x00 ); //fame + ADDWORD( pak, thisclient->Attr->Str ); // Str + ADDWORD( pak, thisclient->Attr->Dex ); // Dex + ADDWORD( pak, thisclient->Attr->Int ); // Int + ADDWORD( pak, thisclient->Attr->Con ); // Con + ADDWORD( pak, thisclient->Attr->Cha ); // Cha + ADDWORD( pak, thisclient->Attr->Sen ); // Sen + ADDWORD( pak, thisclient->Stats->HP ); // Current HP + ADDWORD( pak, thisclient->Stats->MP ); // Current MP + ADDWORD( pak, thisclient->CharInfo->Exp ); // Exp + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, thisclient->Stats->Level ); // Level + ADDWORD( pak, thisclient->CharInfo->StatPoints ); // Stat Points + ADDWORD( pak, thisclient->CharInfo->SkillPoints ); // Skill Points + ADDBYTE( pak, 0x64 ); // Body Size + ADDBYTE( pak, 0x64 ); // Head Size + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, thisclient->Union_s->unionvar[1] );//union points Junon Order + ADDWORD( pak, thisclient->Union_s->unionvar[2] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[3] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[4] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[5] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[6] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[7] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[8] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[9] );//union points + ADDWORD( pak, thisclient->Union_s->unionvar[10] );//union point + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDBYTE( pak, 0x00 ); + ADDWORD( pak, thisclient->CharInfo->stamina ); // Stamina + + for(short i=0; i<326; i++) + ADDBYTE( pak, 0x00 ); + for (char i=0; i<30; i++) + ADDWORD( pak, thisclient->bskills[i] );//Basic skills + for (char i=0; i<30; i++) + ADDWORD( pak, thisclient->askill[i]+thisclient->askilllvl[i] );//Active skills + for (char i=0; i<30; i++) + ADDWORD( pak, thisclient->pskill[i]+thisclient->pskilllvl[i] );//Passive skills + for(short i=0; i<60; i++)//unused ???? + ADDBYTE( pak, 0 ); + for (char i=0; i<32; i++) + ADDWORD( pak, thisclient->quickbar[i] ); // QUICKBAR ROW 1 STARTS + ADDWORD( pak, thisclient->CharInfo->charid ); + ADDWORD( pak, 0x00 ); + for(unsigned j = 0; j < strlen(thisclient->CharInfo->charname); j++) + { + ADDBYTE(pak, thisclient->CharInfo->charname[j] ); + } + + ADDBYTE( pak, 0x00 ); + ADDBYTE( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); +} + +// Send Inventory Information +void CWorldServer::pakInventory( CPlayer *thisclient ) +{ + unsigned cbyte=0; + STARTPACKET(pak, 0x716, 854 ); + SETQWORD( pak, 0x000, thisclient->CharInfo->Zulies ); + cbyte += 8; + for(unsigned j=0; j<140; j++) + { + //Log(MSG_INFO,"slot: %i itemid: %i type: %i count: %i",j, thisclient->items[j].itemnum,thisclient->items[j].itemtype,thisclient->items[j].count); + SETWORD( pak, cbyte+0, BuildItemHead( thisclient->items[j] ) ); + SETDWORD( pak, cbyte+2, BuildItemData( thisclient->items[j] ) ); + cbyte += 6; + } + cbyte-= 6; + thisclient->client->SendPacket( &pak ); +} + +// Send Quest information +void CWorldServer::pakQuestData( CPlayer *thisclient ) +{ + //Log( MSG_DEBUG, "Starting pakQuestData"); + CPacket pakout; + //Log( MSG_DEBUG, "creating packet"); + pakout.StartPacket( 0x71b ); + //Log( MSG_DEBUG, "Starting packet"); + for(dword i = 0; i < 5; i++) pakout.AddWord(thisclient->quest.EpisodeVar[i]); + //Log( MSG_DEBUG, "added episode vars"); + for(dword i = 0; i < 3; i++) pakout.AddWord(thisclient->quest.JobVar[i]); + //Log( MSG_DEBUG, "added job vars"); + for(dword i = 0; i < 7; i++) pakout.AddWord(thisclient->quest.PlanetVar[i]); + //Log( MSG_DEBUG, "added planet vars"); + for(dword i = 0; i < 10; i++) pakout.AddWord(thisclient->quest.UnionVar[i]); + //Log( MSG_DEBUG, "added union vars"); + for( unsigned i = 0; i < 10; i++ ) + { + //Log( MSG_DEBUG, "starting main loop %i",i); + pakout.AddWord( thisclient->quest.quests[i].QuestID ); + //Log( MSG_DEBUG, "added quest ID"); + long int Time = 0; + //Log( MSG_DEBUG, "Initialized Time"); + //Log( MSG_DEBUG, "This Quest quest id = %i",thisclient->quest.quests[i].QuestID); + //Log( MSG_DEBUG, "This Quest STB quest id = %i",STB_QUEST.rows[thisclient->quest.quests[i].QuestID][1]); + if (thisclient->quest.quests[i].QuestID > 0 && STB_QUEST.rows[thisclient->quest.quests[i].QuestID][1] > 0) + { + //Log( MSG_DEBUG, "client has a timed quest"); + Time += thisclient->quest.quests[i].StartTime; // Start time + Time += STB_QUEST.rows[thisclient->quest.quests[i].QuestID][1] * 10; // Time to finish + Time -= time(NULL); // Current time + if (Time < 0) Time = 0; // Time is up + Time /= 10; // Divide to get 10's of seconds + //Log( MSG_DEBUG, "Timed variables set %i",Time); + } + //Log( MSG_DEBUG, "end of time quest conditional"); + pakout.AddDWord( Time ); // Time Left + //Log( MSG_DEBUG, "Time added"); + + for(dword j = 0; j < 10; j++) pakout.AddWord(thisclient->quest.quests[i].Variables[j]); + //Log( MSG_DEBUG, "added quest vars"); + for(dword j = 0; j < 4; j++) pakout.AddByte(thisclient->quest.quests[i].Switches[j]); + //Log( MSG_DEBUG, "added quest switches"); + for(dword j = 0; j < 6; j++) + { + if (thisclient->quest.quests[i].Items[j].itemnum != 0) + { + pakout.AddWord( thisclient->quest.quests[i].Items[j].GetPakHeader() ); + pakout.AddDWord( thisclient->quest.quests[i].Items[j].GetPakData() ); + } + else + { + pakout.AddWord( 0 ); + pakout.AddDWord( 0 ); + } + } + } + //Log( MSG_DEBUG, "finished main quest loop"); + // testing out new PY quest flag method + for( unsigned i = 0; i < 0x40; i++ ) + { + byte tempval = 0; + for(int j=0;j<8;j++) + { + tempval += (byte)thisclient->quest.Qflags[i][j] * (int)pow(2.0,(double)j); + } + pakout.AddByte(tempval); + //pakout.AddByte( thisclient->quest.flags[i] ); + } + //Log( MSG_DEBUG, "finished quest flags"); + // Wishlist? - Not Implemented + for (unsigned i = 0; i < 180; i++) + pakout.AddByte( 0 ); + //Log( MSG_DEBUG, "Finished loading all the stuff into pakout"); + thisclient->client->SendPacket( &pakout ); +} + +// Handle quest triggers +bool CWorldServer::pakQuestTrigger( CPlayer* thisclient, CPacket* pak ) +{ + byte action = pak->GetByte( 0 ); + // action 1 = send text + // action 2 = delete quest + // action 3 = get quest + // action 4 = unknown + // action 5 = success + + byte slot = pak->GetByte( 1 ); + dword hash = pak->GetDWord( 2 ); + + if( thisclient->questdebug ) SendPM( thisclient, "Event Trigger recieved [%08x] Action %i", hash, action); + + if (action == 2) + { + if( thisclient->questdebug ) SendPM( thisclient, "Delete quest - Slot %i", slot); + for (dword i = slot; i < 9; i++) thisclient->quest.quests[i] = thisclient->quest.quests[i+1]; + thisclient->quest.quests[9].Clear(); + CPacket pakout(0x730); + pakout.AddByte(0x03); + pakout.AddByte(slot); + pakout.AddDWord(hash); + thisclient->client->SendPacket(&pakout); + return true; + } + if (action != 3) return false; + int success = thisclient->ExecuteQuestTrigger(hash); + CPacket pakout(0x730); + pakout.AddByte(success); + pakout.AddByte(0); + pakout.AddDWord(hash); + thisclient->client->SendPacket(&pakout); + return true; +} + +// Get this user set up with the encryption and stuff +bool CWorldServer::pakDoIdentify( CPlayer *thisclient, CPacket *P ) +{ + MYSQL_ROW row; + thisclient->Session->userid = GETDWORD((*P), 0); + memcpy( thisclient->Session->password, &P->Buffer[4], 32 ); + MYSQL_RES *result = DB->QStore("SELECT username,lastchar,accesslevel,zulystorage,ktpoints,newpoints,logtime,totlogtime FROM accounts WHERE id=%i AND password='%s'", thisclient->Session->userid, thisclient->Session->password); + if(result==NULL) return false; + if (mysql_num_rows( result ) != 1) + { + Log( MSG_HACK, "Someone tried to connect to world server with an invalid account" ); + DB->QFree( ); + return false; + } + Log( MSG_DEBUG, "Loading Session Data" ); + row = mysql_fetch_row(result); + strncpy_s( thisclient->Session->username, row[0],16 ); + strncpy_s( thisclient->CharInfo->charname, row[1],16 ); + thisclient->Session->accesslevel = atoi(row[2]); + thisclient->CharInfo->Storage_Zulies = atoi( row[3] ); + thisclient->Session->KTPoints = atoi(row[4]); //load KTPoints + thisclient->Session->NewPoints = atoi(row[5]); //load newPoints. This is where additional KTPoints are added when they are purchased + thisclient->Session->logtime = atoi(row[6]); //current log time in hours. + thisclient->Session->TotalLogTime = atoi(row[7]); //total log time for this account + thisclient->Session->award = 0; + thisclient->Session->RespawnChoice = 0; + thisclient->Session->Respawned = 0; + thisclient->Session->inGame = true; + DB->QFree( ); + if(thisclient->Session->NewPoints != 0) + { + // Add NewPoints to KTPoints + //Log( MSG_DEBUG, "Loaded up some new KTPoints and stuff" ); + thisclient->Session->KTPoints += thisclient->Session->NewPoints; + // Set newpoints to zero both here and in the database + thisclient->Session->NewPoints = 0; + GServer->DB->QExecute("UPDATE accounts SET newpoints=%i, ktpoints=%i WHERE id=%i AND password='%s'", + thisclient->Session->NewPoints,thisclient->Session->KTPoints,thisclient->Session->userid, thisclient->Session->password); + //GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + // thisclient->Session->KTPoints,thisclient->Session->userid, thisclient->Session->password); + } + if (thisclient->Session->logtime >= Config.KTPointRate) //make the KTPoint reward system variable + { + while(thisclient->Session->logtime > Config.KTPointRate) + { + thisclient->Session->logtime -= Config.KTPointRate; + thisclient->Session->award ++; + } + thisclient->Session->KTPoints += thisclient->Session->award; + //thisclient->Union_s->unionvar[9] = thisclient->Session->KTPoints; + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i,logtime=%i WHERE id=%i AND password='%s'", + thisclient->Session->KTPoints, thisclient->Session->logtime, thisclient->Session->userid, thisclient->Session->password); + } + + if(!thisclient->loaddata( )) return false; + Log( MSG_INFO, "User '%s'(#%i) logged in with character '%s'", thisclient->Session->username, thisclient->Session->userid, thisclient->CharInfo->charname); + BEGINPACKET( pak, 0x070c ); + ADDBYTE ( pak, 0 ); + ADDDWORD ( pak, 0x87654321 ); + ADDDWORD ( pak, 0x00000000 ); + thisclient->client->SendPacket( &pak ); + pakPlayer(thisclient); + //Log( MSG_DEBUG, "ran pakPlayer"); + pakInventory(thisclient); + //Log( MSG_DEBUG, "ran pakInventory"); + pakQuestData(thisclient); + //Log( MSG_DEBUG, "ran pakQuestData"); + + RESETPACKET( pak, 0x7de ); + ADDWORD ( pak, 0x1001 ); // 0x1001 to 0x1013 (game plan?) + ADDDWORD ( pak, 2 ); // options (plan time?) [2 = unlimited] + thisclient->client->SendPacket( &pak ); + + // Welcome message to the server + RESETPACKET( pak, 0x702 ); + ADDSTRING ( pak, Config.WELCOME_MSG ); + ADDBYTE ( pak, 0 ); + thisclient->client->SendPacket( &pak ); + SendSysMsg( thisclient, "Pre-Evolution server presented by SpiritFox productions and Kuro-Tejina " ); + thisclient->SetStats( ); + + if(thisclient->Session->award != 0) + { + char buffer[200]; + sprintf ( buffer, "You have been awarded %i KTPoints for time logged into the server",thisclient->Session->award); + SendSysMsg(thisclient, buffer); + + } + return true; + +} + + +// Give the user an ID +bool CWorldServer::pakDoID( CPlayer* thisclient, CPacket* P ) +{ + // Assign a new id to this person + thisclient->clientid = GetNewClientID(); + if (thisclient->clientid <= 0) + { + Log( MSG_WARNING, "User '%s'(#%i) denied access. Server is full.", thisclient->Session->username, thisclient->Session->userid ); + return false; + } + Log( MSG_INFO, "User '%s'(#%i) assigned id #%i", thisclient->Session->username, thisclient->Session->userid, thisclient->clientid ); + //Log( MSG_DEBUG, "User Session Ingame = %i", thisclient->Session->inGame); + //thisclient->Session->inGame = true; + if( thisclient->Party->party ) + { + BEGINPACKET( pak, 0x7d5 ); + ADDDWORD ( pak, thisclient->CharInfo->charid ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisclient->GetMaxHP( ) ); + ADDWORD ( pak, thisclient->Stats->HP ); + ADDDWORD ( pak, 0x01000000 ); + ADDDWORD ( pak, 0x0000000f ); + ADDWORD ( pak, 0x1388 ); + thisclient->Party->party->SendToMembers( &pak, thisclient ); + } + BEGINPACKET( pak, 0x721 ); + ADDWORD ( pak, 0x0022 ); + ADDWORD ( pak, 0x0002 ); + ADDWORD ( pak, 0x0000 ); + thisclient->client->SendPacket( &pak ); + //Log( MSG_DEBUG, "Packet 0x721 sent"); + RESETPACKET( pak, 0x753 ); + ADDWORD ( pak, thisclient->clientid ); // USER ID + //Log( MSG_DEBUG, "User ID added"); + ADDWORD ( pak, thisclient->Stats->HP ); // CURRENT HP + //Log( MSG_DEBUG, "Current HP added"); + ADDWORD ( pak, thisclient->Stats->MP ); // CURRENT MP + //Log( MSG_DEBUG, "Current MP added"); + ADDDWORD ( pak, thisclient->CharInfo->Exp ); // CURRENT EXP + ADDDWORD ( pak, 0x00000000 ); // LVL EXP (UNSUSED) + //Log( MSG_DEBUG, "Current XP added"); + //Log( MSG_DEBUG, "Basic info added"); + // thanks to StrikeX to post this source + //[economy] + /* + ADDWORD ( pak, 0x0063 ); // World Rate + ADDBYTE ( pak, 0x70 ); + ADDBYTE ( pak, 0x69 ); + ADDBYTE ( pak, 0x68 ); + ADDBYTE ( pak, 0x67 ); + ADDWORD ( pak, 0x0062 ); // Town rate + ADDBYTE ( pak, 0x61 ); // misc rate + ADDBYTE ( pak, 0x32 ); //1 + ADDBYTE ( pak, 0x32 ); //2 + ADDBYTE ( pak, 0x32 ); //3 + ADDBYTE ( pak, 0x32 ); //4 + ADDBYTE ( pak, 0x32 ); //5 + ADDBYTE ( pak, 0x32 ); //6 + ADDBYTE ( pak, 0x32 ); //7 + ADDBYTE ( pak, 0x32 ); //8 + ADDBYTE ( pak, 0x32 ); //9 + ADDBYTE ( pak, 0x32 ); //10 + ADDBYTE ( pak, 0x32 ); //11 + */ + ADDWORD ( pak, 0x0064 ); //world rate + ADDDWORD ( pak, 0x0C1F4B79);//0x1841f50b ); + ADDWORD ( pak, 0x0064 ); //town rate + ADDDWORD ( pak, 0x3232cd50 ); + ADDDWORD ( pak, 0x32323235 ); + ADDDWORD ( pak, 0x35323232 ); + //Log( MSG_DEBUG, "rates added"); + CMap* map = MapList.Index[thisclient->Position->Map]; + //Log( MSG_DEBUG, "position added"); + if(map->allowpvp!=0) + ADDWORD(pak, 0x0001)//player vs player map + else + ADDWORD(pak, 0x0000)//non player vs player map + ADDWORD (pak, 0x0000 );//?? + // Map Time + ADDDWORD( pak, map->MapTime ); + //ADDBYTE ( pak, 0xde );// (new) + //ADDWORD ( pak, 0x0008 );//this change something in the mapicon (time) (new) + if(map->allowpvp==1) + { + ADDWORD(pak, 0x0000 );//red icon (map) + ADDWORD(pak, 0x0005 );//red icon (map) + } // pvp all vs all + else if(map->allowpvp==2) // pvp group vs group + { + /*ADDWORD(pak, thisclient->Clan->clanid );*/ + ADDWORD(pak, 51); + } + else + { + ADDWORD(pak, 0x0002 );//white icon (map) + ADDWORD(pak, 0x0000 );//white icon (map) + } + thisclient->client->SendPacket( &pak ); + //Log( MSG_DEBUG, "Sent 0x753 packet"); + // Sort out weight stuff, make user not able to run if overweight + int weight = ( thisclient->GetCurrentWeight() / thisclient->GetMaxWeight() ) * 100; + if(weight>110) + thisclient->Status->CanAttack = false; + else + thisclient->Status->CanAttack = true; + if(weight>100) + thisclient->Status->CanRun = false; + else + thisclient->Status->CanRun = true; + + thisclient->Stats->Move_Speed = thisclient->GetMoveSpeed( ); + RESETPACKET( pak, 0x762 ); + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, weight ); + thisclient->client->SendPacket( &pak ); + // set speed + RESETPACKET(pak, 0x782 ); + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, thisclient->Status->Stance ); + ADDWORD ( pak, thisclient->Stats->Mspd_base ); + SendToVisible( &pak, thisclient ); + thisclient->CleanPlayerVector( ); + thisclient->Session->inGame = true; + thisclient->Session->Respawned = 0; + //Log( MSG_DEBUG, "Completed DoID"); + return true; +} + +// Move Characters in map +bool CWorldServer::pakMoveChar( CPlayer* thisclient, CPacket* P ) +{ + if( thisclient->Shop->open || (!thisclient->Ride->Drive && thisclient->Ride->Ride) || !thisclient->Status->CanMove ) + return true; + thisclient->UpdatePosition( ); + thisclient->Position->lastMoveTime = clock(); + if( thisclient->Status->Stance == SITTING ) + thisclient->Status->Stance = RUNNING; + ClearBattle( thisclient->Battle ); + thisclient->Battle->target = GETWORD((*P), 0x00 ); + thisclient->Position->destiny.x = GETFLOAT((*P), 0x02 )/100; + thisclient->Position->destiny.y = GETFLOAT((*P), 0x06 )/100; + if(thisclient->Ride->Drive) //update the destination for anyone riding in the back of my cart + { + CPlayer* otherclient = GServer->GetClientByCID( thisclient->Ride->charid ); + if(otherclient != NULL) + { + otherclient->Stats->Move_Speed = thisclient->Stats->Move_Speed; + otherclient->Position->destiny.x = GETFLOAT((*P), 0x02 )/100; + otherclient->Position->destiny.y = GETFLOAT((*P), 0x06 )/100; + otherclient->UpdatePosition( ); + //hmm not sending an update packet regarding otherclient are we? Do we need a packet here? + BEGINPACKET( pak1, 0x79a ); + ADDWORD ( pak1, otherclient->clientid ); // USER ID + ADDWORD ( pak1, otherclient->Battle->target ); // TARGET + //LMA: it's in fact the distance from current to destiny and not the base speed... + ADDWORD ( pak1, (DWORD) (distance(otherclient->Position->current,otherclient->Position->destiny)*100)); + //ADDWORD ( pak, thisclient->Stats->Move_Speed ); // MSPEED + ADDFLOAT ( pak1, GETFLOAT((*P), 0x02 ) ); // POSITION X + ADDFLOAT ( pak1, GETFLOAT((*P), 0x06 ) ); // POSITION Y + ADDWORD ( pak1, GETWORD((*P), 0x0a ) ); // POSITION Z (NOT USED) + SendToVisible( &pak1, otherclient ); + } + } + BEGINPACKET( pak, 0x79a ); + ADDWORD ( pak, thisclient->clientid ); // USER ID + ADDWORD ( pak, thisclient->Battle->target ); // TARGET + //LMA: it's in fact the distance from current to destiny and not the base speed... + ADDWORD ( pak, (DWORD) (distance(thisclient->Position->current,thisclient->Position->destiny)*100)); + //ADDWORD ( pak, thisclient->Stats->Move_Speed ); // MSPEED + ADDFLOAT ( pak, GETFLOAT((*P), 0x02 ) ); // POSITION X + ADDFLOAT ( pak, GETFLOAT((*P), 0x06 ) ); // POSITION Y + ADDWORD ( pak, GETWORD((*P), 0x0a ) ); // POSITION Z (NOT USED) + SendToVisible( &pak, thisclient ); + //Log(MSG_DEBUG,"Recieved move command and set destiny to X: %f Y: %f.",thisclient->Position->destiny.x,thisclient->Position->destiny.y); + return true; +} + +// Stop Character movement +bool CWorldServer::pakStopChar( CPlayer* thisclient, CPacket* P ) +{ + ClearBattle( thisclient->Battle ); + thisclient->Position->destiny.x = GETFLOAT((*P), 0x00 )/100; + thisclient->Position->destiny.y = GETFLOAT((*P), 0x04 )/100; + thisclient->Position->current=thisclient->Position->destiny; + BEGINPACKET( pak, 0x770 ); + ADDWORD ( pak, thisclient->clientid ); // USER ID + ADDFLOAT ( pak, thisclient->Position->destiny.x*100 ); // POSITION X + ADDFLOAT ( pak, thisclient->Position->destiny.y*100 ); // POSITION Y + ADDWORD ( pak, GETWORD((*P), 0x08 ) ); // POSITION Z (NOT USED) + SendToVisible( &pak, thisclient ); + return true; +} + +// Spawn NPC +bool CWorldServer::pakSpawnNPC( CPlayer* thisclient, CNPC* thisnpc ) +{ + //Log(MSG_DEBUG,"spawning NPC type %i at X: %f Y: %f.",thisnpc->npctype,thisnpc->pos.x,thisnpc->pos.y); + BEGINPACKET( pak, 0x791 ); + ADDWORD( pak, thisnpc->clientid ); + ADDFLOAT( pak, thisnpc->pos.x*100 ); + ADDFLOAT( pak, thisnpc->pos.y*100 ); + ADDWORD( pak, 0xcdcd ); + ADDWORD( pak, 0xcdcd ); + ADDWORD( pak, 0xcdcd ); + ADDWORD( pak, 0xcdcd ); + ADDBYTE( pak, 0x00 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x03e8 ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x0001 ); + ADDWORD( pak, 0x0000 );//Buffs + ADDWORD( pak, 0x0000 );//Buffs + ADDWORD( pak, 0x0000 );//buffs + ADDWORD( pak, thisnpc->npctype ); + if(thisnpc->thisnpc->dialogid != 0) + { + ADDWORD( pak, thisnpc->thisnpc->dialogid ); + } + else + {// fixed by tomiz [npc dialogs fixed [still not all]] + unsigned int factor = 900; + //if (thisnpc->npctype >= 1000 && thisnpc->npctype <=1041 || thisnpc->npctype >= 1043 && thisnpc->npctype <=1084 || thisnpc->npctype >= 1086 && thisnpc->npctype <=1119 || thisnpc->npctype >= 1131 && thisnpc->npctype <=1199 || thisnpc->npctype >= 1207 && thisnpc->npctype <=1299) factor=900; + //else if (thisnpc->npctype >= 1200 && thisnpc->npctype <=1203) factor=896; // event npc + //else if (thisnpc->npctype == 1042 ) factor=899; // Storage Adventure Plain -> Fabrizio - Junon Clan Field -> Nell + //else if (thisnpc->npctype == 1081 ) factor=300; + //else if (thisnpc->npctype == 1085 ) factor=901; + //else if (thisnpc->npctype == 1205 ) factor=901; //santa + //else if (thisnpc->npctype == 1206 ) factor=1019; //Loelsch + //else if (thisnpc->npctype == 1120 ) factor=896; // dunno if that dialog are corect 1120 [Clan Clerk] Regina + //else if (thisnpc->npctype == 1204 ) factor=897; // event npc another warp to zant dialog npc 1203 [Event Info] Lucielle Fete + //else if (thisnpc->npctype == 1473 ) factor=1457; // should be ok 1473 Melendino adventure plain + //else if (thisnpc->npctype == 1752 ) factor=1528; // Clan Merchant Aliche Patt ver 141 + //else if (thisnpc->npctype >= 1122 && thisnpc->npctype <=1130) factor=905; + //else if (thisnpc->npctype == 1502 ) factor=1198; //santa + //else if (thisnpc->npctype >= 1503 && thisnpc->npctype <=1599) factor=1210; + //else if (thisnpc->npctype >= 1750 && thisnpc->npctype <=1755 || !thisnpc->npctype == 1752 ) factor=1000; //should be ok + ADDWORD( pak, thisnpc->npctype - factor ); + } + ADDFLOAT( pak, thisnpc->dir ); + //if(thisnpc->npctype == 1115) + //{ + // ADDBYTE( pak, GServer->Config.Cfmode ); // Burland Clan Field open/close + // ADDBYTE ( pak, 0 ); + //} + //else + //{ + if(thisnpc->thisnpc->eventid != 0) + { + ADDWORD (pak, thisnpc->thisnpc->eventid); + } + else + { + ADDWORD( pak, 0 ); + } + //} + thisclient->client->SendPacket( &pak ); + return true; +} + +// Changes stance +bool CWorldServer::pakChangeStance( CPlayer* thisclient, CPacket* P ) +{ + /* #define ATTACKING 1 + #define WALKING 2 + #define RUNNING 3 + #define DRIVING 4 + #define SITTING + run-walk-run stancenum == 0 + sit-stand-sit stancenum == 1 + drive-no drive-drive == 2 +*/ + if(thisclient->Shop->open) + return true; + BYTE stancenum = GETBYTE((*P),0x00); + BYTE newstancenum = stancenum; + switch (stancenum) + { + case 0: + if(thisclient->Status->Stance == RUNNING) //change 3 to 2 running to walking + { + newstancenum = 2; + thisclient->Status->Stance = WALKING; + } + else if(thisclient->Status->Stance == WALKING) // Running + { + newstancenum = 3; + thisclient->Status->Stance = RUNNING; + } + break; + case 1: + if (thisclient->Status->Stance == RUNNING||thisclient->Status->Stance == WALKING)//>Status->Stance!=DRIVING && thisclient->Status->Stance>0x01) + { + newstancenum = 1; + thisclient->Status->Stance = SITTING; + } + else if (thisclient->Status->Stance == SITTING)//(thisclient->Status->Stance<0x04) + { + newstancenum = 3; + thisclient->Status->Stance = RUNNING; + } + break; + case 2: + if (thisclient->Status->Stance==RUNNING || thisclient->Status->Stance==WALKING) //getting in a cart + {// Clean Buff + newstancenum = 4; + thisclient->Status->Stance = DRIVING; + thisclient->Ride->Ride = false; + thisclient->Ride->Drive = true; + thisclient->Ride->charid = 0; + } + else if (thisclient->Status->Stance==DRIVING) //getting out of cart + {// Clean Buff + newstancenum = 3; + thisclient->Status->Stance = RUNNING; + thisclient->Ride->Ride = false; + thisclient->Ride->Drive = false; + thisclient->Ride->charid = 0; + } + break; + default: + newstancenum = 3; + thisclient->Status->Stance = RUNNING; + break; + } +// if(!thisclient->Status->CanMove) +// thisclient->Status->Stance = RUNNING; + if (!thisclient->Status->CanRun && newstancenum == 3) { + newstancenum = 2; + thisclient->Status->Stance = WALKING; + } + thisclient->Stats->Move_Speed = thisclient->GetMoveSpeed( ); + BEGINPACKET( pak, 0x782 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, newstancenum ); + //ADDWORD( pak, thisclient->Stats->Move_Speed ); + SendToVisible( &pak, thisclient ); + + //Log( MSG_INFO, "Stancenum %i newstancenum %i Stance %i", stancenum, newstancenum, thisclient->Status->Stance); + return true; +} + +// Spawn Drop +bool CWorldServer::pakSpawnDrop( CPlayer* thisclient, CDrop* thisdrop ) +{ + BEGINPACKET( pak, 0x7a6 ); + ADDFLOAT( pak, thisdrop->pos.x*100 ); + ADDFLOAT( pak, thisdrop->pos.y*100 ); + if (thisdrop->type==1) + { + // -- ZULY -- + ADDWORD( pak, 0xccdf );//0xccccccdf ); + ADDDWORD( pak, thisdrop->amount ); + ADDWORD( pak, thisdrop->clientid ); + ADDWORD( pak, 0x0000 ); + ADDWORD( pak, 0x5f90 ); + } + else + { + // -- ITEM -- + ADDWORD( pak, BuildItemHead( thisdrop->item ) ); + ADDDWORD( pak, thisdrop->amount ); + ADDWORD( pak, thisdrop->clientid ); + ADDDWORD( pak, BuildItemData( thisdrop->item ) ); + } + thisclient->client->SendPacket( &pak ); + return true; +} + +// Player Left +void CWorldServer::pakClearUser( CPlayer* thisclient ) +{ + BEGINPACKET( pak, 0x794 ); + ADDWORD( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient, false ); +} + +// Drop items on map +bool CWorldServer::pakDoDrop( CPlayer* thisclient, CPacket* P ) +{ + BYTE itemid = GETBYTE((*P), 0x0); + if(!CheckInventorySlot(thisclient, itemid )) + return false; + DWORD amount = GETDWORD((*P), 0x1); + if (itemid == 0) + { + if ( amount<1 ) return true; + if ( thisclient->CharInfo->Zulies < amount ) return true; + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GetNewClientID(); + thisdrop->type = 1; // ZULY + thisdrop->pos = RandInCircle( thisclient->Position->current, 3 ); + thisdrop->posMap = thisclient->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = amount; + thisdrop->owner = 0; + CMap* map = MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + thisclient->CharInfo->Zulies -= amount; + BEGINPACKET( pak, 0x71d ); + ADDQWORD( pak, thisclient->CharInfo->Zulies ); + thisclient->client->SendPacket( &pak ); + } + else + { + CDrop* thisdrop = new CDrop; + assert(thisdrop); + thisdrop->clientid = GetNewClientID(); + thisdrop->type = 2; // ITEM + thisdrop->pos = RandInCircle( thisclient->Position->current, 3 ); + thisdrop->posMap = thisclient->Position->Map; + thisdrop->droptime = time(NULL); + thisdrop->amount = amount; + thisdrop->item = thisclient->items[itemid]; + thisdrop->item.count = amount; + thisdrop->owner = 0; + CMap* map = MapList.Index[thisdrop->posMap]; + map->AddDrop( thisdrop ); + // fixed by tomiz [item count drop correctly and dissaper from inventory] + if(thisclient->items[itemid].itemtype >=10 && thisclient->items[itemid].itemtype <= 13) + { + if(thisclient->items[itemid].countitems[itemid].count -= amount; + if(thisclient->items[itemid].count <= 0) ClearItem(thisclient->items[itemid]); + } + else + { + ClearItem(thisclient->items[itemid]); + } + thisclient->UpdateInventory( itemid ); + } + return true; +} + +// Picks up item +bool CWorldServer::pakPickDrop( CPlayer* thisclient, CPacket* P ) +{ + try + { + if(thisclient->Shop->open) + return true; + thisclient->RestartPlayerVal( ); + WORD dropid = GETWORD((*P), 0x00); + CDrop* thisdrop = GetDropByID( dropid, thisclient->Position->Map ); + if (thisdrop == NULL) + { + SendSysMsg( thisclient, "That is not a valid drop" ); + return true; + } + if (thisdrop->item.itemnum > 250 && thisdrop->item.itemnum < 272 && thisdrop->item.itemtype == 7)//&&thisdrop->item.itemtype<174)//251/261/271/socket jewel 171Ring 172Necklace 173Earrings + { + thisdrop->item.socketed = true; + thisdrop->item.durability = 100; + thisdrop->item.lifespan = 100; + thisdrop->item.refine = 0; + thisdrop->item.stats = 0; + } + //now find the item type for crafting + if(thisdrop->item.itemtype > 0 && thisdrop->item.itemtype < 10) + thisdrop->item.type = GServer->EquipList[thisdrop->item.itemtype].Index[thisdrop->item.itemnum]->type; + if(thisdrop->item.itemtype == 10) + thisdrop->item.type = GServer->UseList.Index[thisdrop->item.itemnum]->type; + if(thisdrop->item.itemtype == 11) + thisdrop->item.type = GServer->JemList.Index[thisdrop->item.itemnum]->type; + if(thisdrop->item.itemtype == 12) + thisdrop->item.type = GServer->NaturalList.Index[thisdrop->item.itemnum]->type; + if(thisdrop->item.itemtype == 14) + thisdrop->item.type = GServer->PatList.Index[thisdrop->item.itemnum]->type; + + bool flag = false; + BEGINPACKET( pak, 0x7a7 ); + ADDWORD ( pak, thisdrop->clientid ); + CPlayer* dropowner = 0; + if( (thisdrop->owner == 0 || thisdrop->owner == thisclient->CharInfo->charid || time(NULL)-thisdrop->droptime >= 30 ) || ( thisclient->Party->party!=NULL && thisclient->Party->party == thisdrop->thisparty )) + { + if( thisclient->Party->party!=NULL ) + { + unsigned int dropparty = thisclient->Party->party->Option/0x10; + if( dropparty == 8 ) // Orderly + { + unsigned int num = 0; + bool dpflag = false; + if( thisdrop->type == 1 ) + { + num = thisclient->Party->party->LastZulies; + thisclient->Party->party->LastZulies++; + if( thisclient->Party->party->LastZulies>=thisclient->Party->party->Members.size() ) + thisclient->Party->party->LastZulies = 0; + } + else + if( thisdrop->type == 2 ) + { + num = thisclient->Party->party->LastItem; + thisclient->Party->party->LastItem++; + if( thisclient->Party->party->LastItem>=thisclient->Party->party->Members.size() ) + thisclient->Party->party->LastItem = 0; + } + unsigned int n = 0; + while( !dpflag ) + { + n++; + if( num>=thisclient->Party->party->Members.size() ) + num = 0; + dropowner = thisclient->Party->party->Members.at( num ); + if( dropowner == NULL ) + num++; + else + { + dpflag = true; + num++; + } + if(n > 20) // not founded yet? >.> + { + dropowner = thisclient; + dpflag = true; + } + } + } + else // Equal Loot + { + dropowner = thisclient; + } + } + else + { + dropowner = thisclient; + } + if( thisdrop->type == 1 ) //Zulie + { + dropowner->CharInfo->Zulies += thisdrop->amount; + ADDWORD( pak, 0 ); + ADDBYTE( pak, 0 ); + ADDWORD( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + dropowner->client->SendPacket( &pak ); + flag = true; + } + else + if( thisdrop->type == 2 ) // Item + { + if (thisdrop->item.type == 320) //instant effect item + { + RESETPACKET( pak,0x7a3 ); + ADDWORD ( pak, dropowner->clientid ); + ADDWORD ( pak, thisdrop->item.itemnum ); + SendToVisible( &pak, dropowner ); + flag = true; + + switch (UseList.Index[thisdrop->item.itemnum]->useeffect[0]) + { + case 16: + dropowner->Stats->HP += (UseList.Index[thisdrop->item.itemnum]->useeffect[1]); + if(dropowner->Stats->HP > dropowner->Stats->MaxHP ) + dropowner->Stats->HP = dropowner->Stats->MaxHP; + break; + case 17: + dropowner->Stats->MP += (UseList.Index[thisdrop->item.itemnum]->useeffect[1]); + if(dropowner->Stats->MP > dropowner->Stats->MaxMP ) + dropowner->Stats->MP = dropowner->Stats->MaxMP; + break; + case 76: + dropowner->CharInfo->stamina += (UseList.Index[thisdrop->item.itemnum]->useeffect[1]); + if(dropowner->CharInfo->stamina > 5000) + dropowner->CharInfo->stamina = 5000; + break; + case 92: + if(dropowner->Clan->clanid>0) + { + dropowner->Clan->CP += (UseList.Index[thisdrop->item.itemnum]->useeffect[1]); + DB->QExecute( "UPDATE list_clan set cp=%i where id=%i", dropowner->Clan->CP,dropowner->Clan->clanid); + RESETPACKET( pak, 0x7e0 ); + ADDBYTE ( pak, 0x5C ); //action to update clan points (charserver) + ADDWORD ( pak, dropowner->Clan->clanid); + ADDWORD ( pak, dropowner->clientid ); + ADDDWORD ( pak, dropowner->Clan->CP ); + ADDWORD ( pak, 0x00); + SendISCPacket( &pak ); + } + else + { + Log( MSG_WARNING, "Player collected clanpoints but is not in a clan"); + } + break; + default: + Log( MSG_WARNING, "Unknown autoconsume type: %i %i",(UseList.Index[thisdrop->item.itemnum]->usecondition[0]),(UseList.Index[thisdrop->item.itemnum]->usecondition[1])); + break; + } + } + else + { + unsigned int tempslot = dropowner->AddItem( thisdrop->item ); + if(tempslot != 0xffff)// we have slot + { + unsigned int slot1 = tempslot; + unsigned int slot2 = 0xffff; + if(tempslot > MAX_INVENTORY) + { + slot1 = tempslot / 1000; + slot2 = tempslot % 1000; + } + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, slot1 ); + ADDBYTE ( pak, 0x00 ); + ADDWORD ( pak, BuildItemHead( dropowner->items[slot1] ) ); + ADDDWORD ( pak, BuildItemData( dropowner->items[slot1] ) ); + dropowner->client->SendPacket( &pak ); + dropowner->UpdateInventory( slot1, slot2 ); + flag = true; + } + else + { + ADDBYTE (pak, 0x03); + dropowner->client->SendPacket(&pak); + } + } + } + } + else + { + ADDBYTE (pak, 0x02); + thisclient->client->SendPacket(&pak); + } + if( flag ) + { + if( thisclient->Party->party!=NULL ) + { + unsigned int dropparty = thisclient->Party->party->Option/0x10; + if( dropparty == 8 ) + { + RESETPACKET( pak, 0x7d3 ); + ADDWORD ( pak, dropowner->clientid ); + if( thisdrop->type == 1 ) + { + ADDWORD ( pak, 0xccdf ); + ADDDWORD( pak, thisdrop->amount ); + } + else + if( thisdrop->type == 2 ) + { + ADDWORD ( pak, BuildItemHead( thisdrop->item ) ); + ADDDWORD ( pak, BuildItemData( thisdrop->item ) ); + } + thisclient->Party->party->SendToMembers( &pak, dropowner ); + } + } + CMap* map = MapList.Index[thisdrop->posMap]; + pthread_mutex_lock( &map->DropMutex ); + map->DeleteDrop( thisdrop ); + pthread_mutex_unlock( &map->DropMutex ); + } + return true; + } + catch(...) + { + Log( MSG_ERROR, "Error in pakpickdrop"); + return false; + } +} + +// Changes equipment +bool CWorldServer::pakChangeEquip( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + WORD srcslot = GETWORD((*P), 0); + WORD destslot = GETWORD((*P), 2); + if(!CheckInventorySlot(thisclient, srcslot)) + return false; + if(!CheckInventorySlot(thisclient, destslot)) + return false; + if( destslot == 0 ) destslot = thisclient->GetNewItemSlot( thisclient->items[srcslot] ); + if( destslot == 0xffff ) return true; + /* + if( srcslot==7 || destslot==7 ) + { + // clear the buffs if we change the weapon + for(UINT i=0;i<15;i++) + { + switch(thisclient->MagicStatus[i].Buff) + { + case A_ATTACK: + case A_ACCUR: + case A_HASTE: + case A_CRITICAL: + thisclient->MagicStatus[i].Duration = 0; + break; + } + } + } + */ + if(destslot == 8) //trying to equip a shield + { + // first we will check if it is possible. Do we have a two handed weapon equipped? + UINT weapontype = EquipList[WEAPON].Index[thisclient->items[7].itemnum]->type; + switch(weapontype) + { + case TWO_HAND_SWORD: + case SPEAR: + case TWO_HAND_AXE: + case BOW: + case GUN: + case LAUNCHER: + case STAFF: + case KATAR: + case DOUBLE_SWORD: + case DUAL_GUN: + { + //yes we do. OK reject the shield then. + SendSysMsg( thisclient, "Cannot equip shield at this time" ); + return true; + } + break; + default: + break; + } + } + CItem tmpitm; + tmpitm = thisclient->items[srcslot]; + thisclient->items[srcslot] = thisclient->items[destslot]; + thisclient->items[destslot] = tmpitm; +// thisclient->UpdateInventory( srcslot, destslot ); + BEGINPACKET( pak,0x7a5 ); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, srcslot ); + ADDDWORD( pak, BuildItemShow(thisclient->items[srcslot])); // ITEM + ADDWORD ( pak, thisclient->Stats->Move_Speed); + SendToVisible( &pak, thisclient ); + + if( srcslot == 7 || destslot == 7 ) + { + // if is two hand weapon, we have to check if have shield and unequip it + UINT weapontype = EquipList[WEAPON].Index[thisclient->items[7].itemnum]->type; + switch(weapontype) + { + case TWO_HAND_SWORD: + case SPEAR: + case TWO_HAND_AXE: + case BOW: + case GUN: + case LAUNCHER: + case STAFF: + case KATAR: + case DOUBLE_SWORD: + case DUAL_GUN: + { + if(thisclient->items[8].itemnum != 0) + { // we should unequip the shield + UINT newslot = thisclient->GetNewItemSlot( thisclient->items[8] ); + if(newslot == 0xffff) + { + // we have no slot for the shield, we have to change back the items + tmpitm = thisclient->items[destslot]; + thisclient->items[destslot] = thisclient->items[srcslot]; + thisclient->items[srcslot] = tmpitm; + SendSysMsg( thisclient, "You don't have free slot" ); + return true; + } + thisclient->items[newslot] = thisclient->items[8]; + ClearItem( thisclient->items[8] ); + thisclient->UpdateInventory( newslot, 8 ); + RESETPACKET( pak, 0x7a5); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, 8 ); + ADDWORD ( pak, thisclient->items[8].itemnum ); + ADDWORD ( pak, BuildItemRefine( thisclient->items[8] ) ); + ADDWORD ( pak, thisclient->Stats->Move_Speed ); + SendToVisible( &pak, thisclient ); + + /* STARTPACKET(pak2, 0x782, 11); + SETWORD( pak2, 0x00,thisclient->clientid ); + SETBYTE( pak2, 0x02, thisclient->Status->Stance ); + SETWORD( pak2, 0x03, thisclient->Stats->Move_Speed ); + SendToVisible( &pak2, thisclient );*/ + } + } + break; + default: + break; + } + } + + STARTPACKET(pak3,0x718,21); + SETBYTE ( pak3, 0x00, 2); // ITEMS TO UPDATE + SETBYTE ( pak3, 0x01, srcslot); + SETWORD ( pak3, 0x02, BuildItemHead( thisclient->items[srcslot] ) ); + SETDWORD ( pak3, 0x04, BuildItemData( thisclient->items[srcslot] ) ); + SETBYTE ( pak3, 0x08, destslot); + SETWORD ( pak3, 0x09, BuildItemHead( thisclient->items[destslot] ) ); + SETDWORD ( pak3, 0x0b, BuildItemData( thisclient->items[destslot] ) ); + thisclient->client->SendPacket(&pak3); + + + //thisclient->UpdateInventory( srcslot, destslot ); + thisclient->SetStats( ); + return true; +} + +// Construct a cart/change parts +bool CWorldServer::pakChangeCart( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + WORD cartslot = GETWORD((*P), 0); + WORD srcslot = GETWORD((*P), 0) + 135; + WORD destslot = GETWORD((*P), 2); + if(!CheckInventorySlot( thisclient, srcslot )) + return false; + if(!CheckInventorySlot( thisclient, destslot )) + return false; + if( destslot == 0 ) destslot=thisclient->GetNewItemSlot( thisclient->items[srcslot] ); + if( destslot == 0xffff ) return true; + + CItem tmpitm = thisclient->items[srcslot]; + thisclient->items[srcslot] = thisclient->items[destslot]; + thisclient->items[destslot] = tmpitm; + + BEGINPACKET( pak, 0x7ca ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, cartslot); + ADDWORD ( pak, thisclient->items[srcslot].itemnum); + ADDWORD ( pak, BuildItemRefine( thisclient->items[srcslot] ) ); + ADDWORD ( pak, thisclient->Stats->Move_Speed ); + SendToVisible( &pak, thisclient ); + + thisclient->UpdateInventory( srcslot, destslot ); + thisclient->Stats->Move_Speed = thisclient->GetMoveSpeed( ); + return true; +} + +// Start attacking a monster or player +bool CWorldServer::pakStartAttack( CPlayer* thisclient, CPacket* P ) +{ + WORD clientid = GETWORD((*P),0x00); + if ( thisclient->Battle->target == clientid ) return true; //no targetting yourself + + int weapontype = EquipList[WEAPON].Index[thisclient->items[7].itemnum]->type; + if( weapontype == 231 && thisclient->items[132].count<1 ) //Bow and no arrows + return true; + else + if( weapontype == 232 && thisclient->items[133].count<1 ) //Gun and no bullets + return true; + else + if( weapontype == 233 && thisclient->items[134].count<1 ) //Launcher and no shells + return true; + else + if( weapontype == 271 && thisclient->items[132].count<1 ) //Crossbow and no arrows + return true; + + CMap* map = MapList.Index[thisclient->Position->Map]; + CCharacter* character = map->GetCharInMap( clientid ); + if(character == NULL) return true; + if(character->IsMonster( )) + { + if(map->allowpvp == 0 && character->IsSummon( )) return true; + if(!character->IsSummon()) + character->AddDamage((CCharacter*)thisclient, 0); + //} + //if(character->IsMonster()) + //{ + // SET MONSTER HEALTH + BEGINPACKET( pak, 0x79f ); + ADDWORD ( pak, character->clientid ); + ADDDWORD ( pak, character->Stats->HP ); + thisclient->client->SendPacket( &pak ); + } + thisclient->StartAction( character, NORMAL_ATTACK, 0 ); + thisclient->Battle->contatk = true; + return true; +} + +// Goto next map through gate +bool CWorldServer::pakGate( CPlayer* thisclient, CPacket* P ) +{ + thisclient->Session->inGame = false; + word GateID = GETWORD((*P), 0x00); + CTeleGate* thisgate = GetTeleGateByID( GateID ); + fPoint position; + UINT map = 0; + // I'm setting this at 50 distance from the point of teleport. Increase if you run into + // broken telegates. We might need to dump the scale and work from that - Drakia + if( thisgate == NULL || thisclient->Position->Map != thisgate->srcMap || distance(thisclient->Position->current, thisgate->src) > 150 ) + { + Log( MSG_HACK, "Player %s[Map: %i X: %f Y: %f] - Gate Hacking[ID: %i]", + thisclient->CharInfo->charname, thisclient->Position->Map, + thisclient->Position->current.x, thisclient->Position->current.y, GateID ); + return true; + } + + + map = thisgate->destMap; + position = thisgate->dest; + + MapList.Index[map]->TeleportPlayer( thisclient, position ); + thisclient->saveinventory(); + thisclient->quicksave(); + thisclient->Session->inGame = true; + return true; +} + +// Emotions +bool CWorldServer::pakDoEmote( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + thisclient->RestartPlayerVal( ); + BEGINPACKET( pak, 0x781 ); + ADDWORD( pak, GETWORD((*P), 0) ); + ADDWORD( pak, GETWORD((*P), 2) ); + ADDWORD( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient ); + return true; +} + +// This function is for increasing your stats +bool CWorldServer::pakAddStats( CPlayer* thisclient, CPacket* P ) +{ + BYTE statid = GETBYTE((*P), 0); + int statval = -1; + int nstatval = 0; + switch(statid) + { + case 0: nstatval=(int)floor((float)thisclient->Attr->Str/5); break; + case 1: nstatval=(int)floor((float)thisclient->Attr->Dex/5); break; + case 2: nstatval=(int)floor((float)thisclient->Attr->Int/5); break; + case 3: nstatval=(int)floor((float)thisclient->Attr->Con/5); break; + case 4: nstatval=(int)floor((float)thisclient->Attr->Cha/5); break; + case 5: nstatval=(int)floor((float)thisclient->Attr->Sen/5); break; + default: + break; + } + if (thisclient->CharInfo->StatPoints>=nstatval && nstatval < ((Config.MaxStat/5)+1)) + { + switch(statid) + { + case 0: statval=++thisclient->Attr->Str; break; + case 1: statval=++thisclient->Attr->Dex; break; + case 2: statval=++thisclient->Attr->Int; break; + case 3: statval=++thisclient->Attr->Con; break; + case 4: statval=++thisclient->Attr->Cha; break; + case 5: statval=++thisclient->Attr->Sen; break; + default: + break; + } + } + if (statval>0) + { + thisclient->CharInfo->StatPoints-=nstatval; + BEGINPACKET( pak, 0x7a9 ); + ADDBYTE( pak, statid ); + ADDWORD( pak, (unsigned short)statval ); + thisclient->client->SendPacket( &pak ); + } + thisclient->SetStats( ); + return true; +} + +// Normal Chat +bool CWorldServer::pakNormalChat( CPlayer* thisclient, CPacket* P ) +{ + if (P->Buffer[0]=='/') + { + return pakGMCommand( thisclient, P ); + //std::cout << thisclient->CharInfo->charname << ": " << P->Buffer << "\n"; + + } + else + { + BEGINPACKET( pak, 0x783 ); + ADDWORD( pak, thisclient->clientid ); + ADDSTRING( pak, P->Buffer ); + ADDBYTE( pak, 0 ); + SendToVisible( &pak, thisclient ); + } + return true; +}; + +// Exit rose +bool CWorldServer::pakExit( CPlayer* thisclient, CPacket* P ) +{ + BEGINPACKET( pak, 0x707 ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + thisclient->client->isActive = false; + return true; +} + +// move skill to function keys +bool CWorldServer::pakMoveSkill ( CPlayer* thisclient, CPacket* P ) +{ + BYTE slotid = GETBYTE((*P), 0x0 ); + WORD itemid = GETWORD((*P), 0x1 ); + if(slotid>47) return true; + thisclient->quickbar[slotid] = itemid; + BEGINPACKET( pak, 0x7aa ); + ADDBYTE( pak, slotid ); + ADDWORD( pak, itemid ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// User is checking up on the server +bool CWorldServer::pakPing ( CPlayer* thisclient, CPacket* P ) +{ + BEGINPACKET( pak, 0x0700 ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// GameGuard +bool CWorldServer::pakGameGuard ( CPlayer* thisclient, CPacket* P ) +{ + thisclient->lastGG = clock(); + return true; +} + +// User Died +bool CWorldServer::pakUserDied ( CPlayer* thisclient, CPacket* P ) +{ + thisclient->Session->inGame = false; + thisclient->Session->Respawned = 1; + //BYTE respawn = GETBYTE((*P),0); + thisclient->Session->RespawnChoice = GETBYTE((*P),0); + + //PY lets try moving all this stuff into the main loop because we are currently outside of the player mutex + //It's all in pakRespawned now. + //thisclient->Session->inGame is just used as a toggle trigger now + + /* + Log( MSG_INFO, "RespawnZones choise: %i", respawn ); + //1 - Current / 2 - save point/dungeon entrance + CMap* map = MapList.Index[thisclient->Position->Map]; + CRespawnPoint* thisrespawn = NULL; + thisclient->Stats->HP = thisclient->Stats->MaxHP * 10 / 100; + if(respawn == 1 || map->dungeon) + { + if (respawn != 1) // This is a dungeon map, spawn at start + thisrespawn = map->GetStartRespawn( ); + else + thisrespawn = map->GetNearRespawn( thisclient ); + if(thisrespawn!=NULL) + { + map->TeleportPlayer( thisclient, thisrespawn->dest, false ); + } + } + else // This is a field, spawn in save town + { + thisrespawn = GetRespawnByMap( thisclient->Position->saved ); + if(thisrespawn != NULL) + { + fPoint coord = thisrespawn->dest; + MapList.Index[thisclient->Position->saved]->TeleportPlayer( thisclient, coord, false ); + } + } + + if(thisrespawn == NULL) + { + fPoint coord; + coord.x = 5200; + coord.y = 5200; + MapList.Index[2]->TeleportPlayer( thisclient, coord, false ); + } + for(unsigned int i=0;i<30;i++) + { // Clean Buffs + thisclient->MagicStatus[i].Duration = 0; + }*/ + + return true; +} +// User Save Town +bool CWorldServer::pakSaveTown ( CPlayer* thisclient, CPacket* P ) +{ + thisclient->Position->saved = thisclient->Position->Map; + GServer->DB->QExecute("UPDATE characters SET townid=%i WHERE id=%i",thisclient->Position->saved,thisclient->CharInfo->charid ); + return true; +} +// Shouting +bool CWorldServer::pakShout ( CPlayer* thisclient, CPacket* P ) +{ + BEGINPACKET(pak, 0x0785); + ADDSTRING ( pak, thisclient->CharInfo->charname ); + ADDBYTE ( pak, 0 ); + ADDSTRING ( pak, &P->Buffer[0] ); + ADDBYTE ( pak, 0 ); + SendToMap ( &pak, thisclient->Position->Map ); + + return true; +} + +// Whispering +bool CWorldServer::pakWhisper ( CPlayer* thisclient, CPacket* P ) +{ + char msgto[17]; + memset( &msgto, '\0', 17 ); + strncpy_s( msgto, (char*)&(*P).Buffer[0],16 ); + CPlayer* otherclient = GetClientByCharName( msgto ); + if(otherclient!=NULL) + { + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, thisclient->CharInfo->charname ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, &P->Buffer[strlen(msgto)+1] ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); + return true; + } + BEGINPACKET( pak, 0x0784 ); + ADDSTRING( pak, msgto ); + ADDBYTE( pak, 0 ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// Return to Char Select +bool CWorldServer::pakCharSelect ( CPlayer* thisclient, CPacket* P ) +{ + if(!thisclient->Session->inGame) return true; + BEGINPACKET( pak, 0x707 ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + thisclient->Session->inGame=false; + RESETPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0xfa ); + ADDWORD ( pak, thisclient->Session->userid );//CharInfo->charid ); + ADDBYTE ( pak, 0x00 ); + SendISCPacket( &pak ); + + for(unsigned j=0; jVisiblePlayers.size(); j++) { + CPlayer* otherclient=thisclient->VisiblePlayers.at(j); + thisclient->ClearObject(otherclient->clientid); + } + for(unsigned j=0; jVisibleMonsters.size(); j++) { + thisclient->ClearObject(thisclient->VisibleMonsters.at(j)); + } + for(unsigned j=0; jVisibleDrops.size(); j++) { + CDrop* thisdrop = thisclient->VisibleDrops.at(j); + thisclient->ClearObject(thisdrop->clientid); + } + for(unsigned j=0; jVisibleNPCs.size(); j++) { + CNPC* thisnpc = thisclient->VisibleNPCs.at(j); + thisclient->ClearObject( thisnpc->clientid ); + } + + thisclient->savedata(); + RESETPACKET( pak, 0x505 ); + ADDWORD ( pak, thisclient->Session->userid ); + SendISCPacket( &pak ); + thisclient->Session->isLoggedIn = false; + thisclient->Saved = true; + pakClearUser( thisclient ); + ClearClientID( thisclient->clientid ); + if(thisclient->client!=NULL) thisclient->client->isActive = false; + return true; +} + +// Buy from NPC +bool CWorldServer::pakNPCBuy ( CPlayer* thisclient, CPacket* P ) +{ + //try + //{ + if(thisclient->Shop->open) + return true; + WORD NPC = GETWORD((*P), 0); + CNPC* thisnpc = GetNPCByID( NPC, thisclient->Position->Map ); + if( thisnpc == NULL ) + return true; + BYTE buycount = GETBYTE((*P), 2); + BYTE sellcount = GETBYTE((*P), 3); + + //Union Shop case... + Log(MSG_INFO,"Buying /selling from NPC %i. Buycount: %i, Sellcount: %i" ,thisnpc->npctype, buycount,sellcount); + bool is_unionshop = false; + + //if (thisnpc->npctype == 1109 || thisnpc->npctype == 1110 || thisnpc->npctype == 1111 || thisnpc->npctype == 1112 || thisnpc->npctype == 1043) + //{ + // is_unionshop = true; + // Log(MSG_INFO,"identified as a union shop"); + //} + + switch(thisnpc->npctype) + { + case 1109: + case 1110: + case 1111: + case 1112: + case 1043: + is_unionshop = true; + Log(MSG_INFO,"identified as a union shop"); + break; + } + + int UpointCost = 0; + int UnionVar = thisclient->Union_s->unionvar[0]; + int TotUpointCost = 0; + BYTE ncount = 0; + BEGINPACKET( pak, 0x717 ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x00 ); + for (int i=0; ithisnpc->tab1; + break; + case 1: + sellid = thisnpc->thisnpc->tab2; + break; + case 2: + sellid = thisnpc->thisnpc->tab3; + break; + case 3: + sellid = thisnpc->thisnpc->specialtab; + break; + default: + sellid = 0; + } + if(SellList.Index[sellid]==NULL) + { // invalid tab + //Log( MSG_WARNING, "Bad tab ID"); + continue; + } + int tmpcount = count; + while (tmpcount > 0) + { + CItem thisitem; + thisitem.itemnum = SellList.Index[sellid]->item[itemid] % 1000; + thisitem.itemtype = SellList.Index[sellid]->item[itemid] / 1000; + thisitem.count = count; + //Log( MSG_WARNING, "Item purchased. type = %i. id = %i. tempcount = %i", thisitem.itemtype,thisitem.itemnum,tmpcount); + + if(thisitem.itemtype <= 9) + { + UpointCost = GServer->EquipList[thisitem.itemtype].Index[thisitem.itemnum]->unionpoint; + thisitem.type = GServer->EquipList[thisitem.itemtype].Index[thisitem.itemnum]->type; + thisitem.durability = EquipList[thisitem.itemtype].Index[thisitem.itemnum]->durability; + } + if(thisitem.itemtype == 10) + { + UpointCost = GServer->UseList.Index[thisitem.itemnum]->unionpoint; + thisitem.type = GServer->UseList.Index[thisitem.itemnum]->type; + thisitem.durability = 0; + } + if(thisitem.itemtype == 11) + { + UpointCost = GServer->JemList.Index[thisitem.itemnum]->unionpoint; + thisitem.type = GServer->JemList.Index[thisitem.itemnum]->type; + thisitem.durability = 0; + } + if(thisitem.itemtype == 12) + { + UpointCost = GServer->NaturalList.Index[thisitem.itemnum]->unionpoint; + thisitem.type = GServer->NaturalList.Index[thisitem.itemnum]->type; + thisitem.durability = 0; + } + if(thisitem.itemtype == 14) + { + UpointCost = GServer->PatList.Index[thisitem.itemnum]->unionpoint; + thisitem.type = GServer->PatList.Index[thisitem.itemnum]->type; + thisitem.durability = GServer->PatList.Index[thisitem.itemnum]->durability; + } + if(is_unionshop && UpointCost == 0) + { + ncount++; + Log( MSG_WARNING, "Item has no price in Server"); + tmpcount = 0; + continue; //this item has no price so skip it + } + if(is_unionshop && thisclient->Union_s->unionvar[UnionVar] < UpointCost) + { + ncount++; + Log( MSG_WARNING, "You cannot afford this item"); + tmpcount = 0; + continue; //can't afford the item so don't bother adding it + } + //Log( MSG_WARNING, "Item purchased. type = %i. id = %i", thisitem.itemtype,thisitem.itemnum); + unsigned newslot = thisclient->GetNewItemSlot( thisitem ); + if ( newslot == 0xffff ) + { + ncount -= 1; + Log( MSG_WARNING, "failed to find free slot"); + break; + } + int thisslotcount = 999 - thisclient->items[newslot].count; + //Log( MSG_WARNING, "this slot count = %i", thisslotcount); + //if ( thisslotcount > tmpcount ) + //{ + // thisslotcount = tmpcount; + //} + //Log( MSG_WARNING, "this slot count = %i", thisslotcount); + tmpcount -= thisslotcount; + thisitem.count += thisclient->items[newslot].count; + thisitem.refine = 0; + thisitem.lifespan = 100; + thisitem.appraised = true; + thisitem.socketed = false; + thisitem.stats = 0; + if(is_unionshop) //make sure all union shop items come with extra stats + { + if(thisitem.itemtype <= 9) + thisitem.stats = GServer->RandNumber(1,299); + } + thisitem.gem = 0; + thisclient->items[newslot] = thisitem; + ADDBYTE ( pak, newslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[newslot] ) ); + + switch(thisitem.itemtype) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 8: + case 9: + case 14: + { + float price = 0; + UINT quality = 0; + UINT bprice = 0; + if(thisitem.itemtype<10 ) + { + quality = EquipList[thisitem.itemtype].Index[thisitem.itemnum]->quality; + bprice = EquipList[thisitem.itemtype].Index[thisitem.itemnum]->price; + } + else + { + quality = PatList.Index[thisitem.itemnum]->quality; + bprice = PatList.Index[thisitem.itemnum]->price; + } + price = quality; + price += 0x32; // town rate + price *= bprice; + price += 0.5; + price /= 100; + price = (float)round(price); + //Log( MSG_WARNING, "Item bought: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + if(is_unionshop) + { + thisclient->Union_s->unionvar[UnionVar] -= UpointCost; + if(UnionVar == 9) + { + Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + thisclient->Union_s->unionvar[UnionVar],thisclient->Session->userid, thisclient->Session->password); + Log(MSG_DEBUG,"KTPoints updated and saved to the database"); + Log( MSG_WARNING, "item Purchased successfully"); + } + + } + else + thisclient->CharInfo->Zulies -= (long int)price; + } + break; + case 10: + case 12: + { + // this values should be the same as packet 753 + //Log( MSG_WARNING, "Type %i item selected", thisitem.itemtype); + BYTE values[11] = {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}; + UINT type = 0; + UINT bprice = 0; + UINT pricerate = 0; + UINT pvalue = 0; + if(thisitem.itemtype == 10) + { + type = UseList.Index[thisitem.itemnum]->type; + bprice = UseList.Index[thisitem.itemnum]->price; + pricerate = UseList.Index[thisitem.itemnum]->pricerate; + pvalue = UseList.Index[thisitem.itemnum]->pricevalue; + } + else + { + type = NaturalList.Index[thisitem.itemnum]->type; + bprice = NaturalList.Index[thisitem.itemnum]->price; + pricerate = NaturalList.Index[thisitem.itemnum]->pricerate; + pvalue = NaturalList.Index[thisitem.itemnum]->pricevalue; + } + unsigned int value = 0; + float price = 0; + bool flag; + if(type<421) + if(type<311) + flag = false; + else + if(type>312) + flag = false; + else + flag = true; + else + if(type<=428) + flag = true; + else + if(type<311) + flag = false; + else + flag = true; + if(flag) + { + value = pvalue; + if(value>=11) + value ^= 0xffffffff; + else + value = values[value]; + value -= 0x32; + value *= pricerate; + price = value * 0.001; + price += 1; + price *= bprice; + price += 0.5; + price = (float)floor(price); + //Log( MSG_WARNING, "Item bought: itemnum %i, itemtype %i, itemcount %i, price %0.0f, flag %i", thisitem.itemnum, thisitem.itemtype, thisitem.count, price, flag); + if(is_unionshop) + { + thisclient->Union_s->unionvar[UnionVar] -= (UpointCost * count); + + TotUpointCost -= (UpointCost * count); + if(UnionVar == 9) + { + Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + thisclient->Union_s->unionvar[UnionVar],thisclient->Session->userid, thisclient->Session->password); + Log(MSG_DEBUG,"KTPoints updated and saved to the database"); + Log( MSG_WARNING, "item Purchased successfully"); + } + //Log( MSG_WARNING, "item Purchased successfully"); + } + else + thisclient->CharInfo->Zulies -= (long int)price * count; + } + else + { + price = pricerate; + value = 0x61 - 0x32; // misc rate - 0x32 + price *= value; + price *= 0.001; + price += 1; + price *= bprice; + price += 0.5; + price = (float)floor(price); + //Log( MSG_WARNING, "Item bought: itemnum %i, itemtype %i, itemcount %i, price %0.0f, flag %i", thisitem.itemnum, thisitem.itemtype, thisitem.count, price, flag); + if(is_unionshop) + { + thisclient->Union_s->unionvar[UnionVar] -= (UpointCost * count); + //Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + TotUpointCost -= (UpointCost * count); + if(UnionVar == 9) + { + Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + thisclient->Union_s->unionvar[UnionVar],thisclient->Session->userid, thisclient->Session->password); + Log(MSG_DEBUG,"KTPoints updated and saved to the database"); + Log( MSG_WARNING, "item Purchased successfully"); + } + //Log( MSG_WARNING, "item Purchased successfully"); + } + else + thisclient->CharInfo->Zulies -= (long int)price * count; + } + } + break; + case 7: + case 11: + case 13: + { + float price = 0; + UINT bprice = 0; + UINT pricerate; + if(thisitem.itemtype==7) + { + pricerate = EquipList[7].Index[thisitem.itemnum]->pricerate; + bprice = EquipList[7].Index[thisitem.itemnum]->price; + } + else + { + switch(thisitem.itemtype) + { + case 11: + pricerate = JemList.Index[thisitem.itemnum]->pricerate; + bprice = JemList.Index[thisitem.itemnum]->price; + break; + case 13:continue; + } + } + price = pricerate; + unsigned int value = 0x61 - 0x32; // misc rate - 0x32 + price *= value; + price *= 0.001; + price += 1; + price *= bprice; + price += 0.5; + price = (float)round(price); + if(thisitem.itemtype == 7) + price = (float)round(bprice * 1.3); + //Log( MSG_WARNING, "Item bought: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + if(is_unionshop) + { + thisclient->Union_s->unionvar[UnionVar] -= UpointCost; + //Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + TotUpointCost -= UpointCost; + if(UnionVar == 9) + { + Log( MSG_WARNING, "Union Shop Item bought: UnionID %i, itemnum %i, itemtype %i, itemcount %i, price %i", UnionVar, thisitem.itemnum, thisitem.itemtype, thisitem.count, UpointCost); + GServer->DB->QExecute("UPDATE accounts SET ktpoints=%i WHERE id=%i AND password='%s'", + thisclient->Union_s->unionvar[UnionVar],thisclient->Session->userid, thisclient->Session->password); + Log(MSG_DEBUG,"KTPoints updated and saved to the database"); + Log( MSG_WARNING, "item Purchased successfully"); + } + //Log( MSG_WARNING, "item Purchased successfully"); + } + else + thisclient->CharInfo->Zulies -= (long int)price*count; + } + break; + default: + Log( MSG_WARNING, "Invalid Item Type: %i", thisitem.itemtype ); + break; + } + ncount++; + Log( MSG_WARNING, "item count: %i", ncount ); + } + BEGINPACKET(pak1, 0x720 ); + ADDWORD(pak1, thisclient->Union_s->unionvar[0] ); + ADDDWORD(pak1, TotUpointCost); + thisclient->client->SendPacket( &pak1 ); + } + for (int i=0; iitems[slotid]; + thisitem.count = count; + switch(thisitem.itemtype) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 8: + case 9: + case 14: + { + float price = 0; + price = 7.142857E-05F * 5000; + if(thisitem.itemtype<10) + { + price *= EquipList[thisitem.itemtype].Index[thisitem.itemnum]->price; + } + else + { + price *= PatList.Index[thisitem.itemnum]->price; + } + price *= thisitem.durability + 0xc8; + price *= 40; + price *= 0xc8 - 0x62; //town rate + price *= 1.000000E-06; + price = (float)floor(price); + //Log( MSG_WARNING, "Item Sold: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + thisclient->CharInfo->Zulies += (long int)price*count; + } + break; + case 10: + case 12: + { + // this values are the same from packet 753 + BYTE values[11] = {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}; + UINT type = 0; + UINT bprice = 0; + UINT pricerate = 0; + UINT pvalue = 0; + if(thisitem.itemtype==10) + { + type = UseList.Index[thisitem.itemnum]->type; + bprice = UseList.Index[thisitem.itemnum]->price; + pricerate = UseList.Index[thisitem.itemnum]->pricerate; + pvalue = UseList.Index[thisitem.itemnum]->pricevalue; + } + else + { + type = NaturalList.Index[thisitem.itemnum]->type; + bprice = NaturalList.Index[thisitem.itemnum]->price; + pricerate = NaturalList.Index[thisitem.itemnum]->pricerate; + pvalue = NaturalList.Index[thisitem.itemnum]->pricevalue; + } + unsigned int value = 0; + float price = 0; + bool flag; + if(type<421) + { + if(type<311) + flag = false; + else + if(type>312) + flag = false; + else + flag = true; + } + else + { + if(type<=428) + flag = true; + else + if(type<311) + flag = false; + else + flag = true; + } + if(flag) + { + value = pvalue; + if(value>=11) + value ^= 0xffffffff; + else + value = values[value]; + value -= 0x32; + value *= pricerate; + value += 1000; + value *= bprice; + value *= (200 - 0x62); //town rate ( 100) + price = value * 5.555555555555556E-06; + price = (float)floor(price); + //Log( MSG_WARNING, "Item Sold: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + thisclient->CharInfo->Zulies += (long int)price*count; + } + else + { + price = pricerate; + value = 0x61 - 0x32; // misc rate -0x32 + price *= value; + price += 1000; + price *= bprice; + price *= (200 - 0x62); //town rate ( 100) + price *= 5.555555555555556E-06; + price = (float)floor(price); + //Log( MSG_WARNING, "Item Sold: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + thisclient->CharInfo->Zulies += (long int)price*count; + } + } + break; + case 7: + case 11: + case 13: + { + float price = 0; + UINT bprice = 0; + UINT pricerate = 0; + switch(thisitem.itemtype) + { + case 7: + pricerate = EquipList[thisitem.itemtype].Index[thisitem.itemnum]->pricerate; + bprice = EquipList[thisitem.itemtype].Index[thisitem.itemnum]->price; + break; + case 11: + pricerate = JemList.Index[thisitem.itemnum]->pricerate; + bprice = JemList.Index[thisitem.itemnum]->price; + break; + case 13:continue;//can we sell quest items? :S + + } + price = pricerate; + price *= 0x61 - 0x32; // misc rate -0x32 + price += 1000; + price *= bprice; + price *= (200 - 0x62); //town rate ( 100) + price *= 5.555555555555556E-06; + price = (float)floor(price); + if(thisitem.itemtype == 7) + price = (float)floor(bprice * 0.5555508); + //Log( MSG_WARNING, "Item Sold: itemnum %i, itemtype %i, itemcount %i, price %0.0f", thisitem.itemnum, thisitem.itemtype, thisitem.count, price); + thisclient->CharInfo->Zulies += (long int)price*count; + } + break; + default: + Log( MSG_WARNING, "Invalid Item Type: %i", thisitem.itemtype ); + break; + } + thisclient->items[slotid].count -= count; + if( thisclient->items[slotid].count <=0 ) + ClearItem( thisclient->items[slotid] ); + ADDBYTE( pak, slotid ); + ADDWORD( pak, BuildItemHead( thisclient->items[slotid] ) ); + ADDDWORD( pak, BuildItemData( thisclient->items[slotid] ) ); + ncount++; + } + SETQWORD( pak, 0, thisclient->CharInfo->Zulies ); + SETBYTE( pak, 8, ncount ); + thisclient->client->SendPacket( &pak ); + thisclient->saveinventory(); + return true; + //} + //__seh_except(info, context) + //{ + // Log( MSG_ERROR, "Error in pakNPCBuy"); + // return false; + //} + //__seh_end +} + + + +// Trade action +bool CWorldServer::pakTradeAction ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + CPacket pak; + BYTE action = GETBYTE( (*P), 0 ); + thisclient->Trade->trade_target = GETWORD( (*P), 1 ); + CPlayer* otherclient = GetClientByID( thisclient->Trade->trade_target, thisclient->Position->Map ); + if (otherclient == NULL) return true; + switch(action) + { + case 0: + // REQUEST TRADE + RESETPACKET( pak, 0x7c0 ); + ADDBYTE( pak, 0 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); // send the request packet to the other player with action zero + thisclient->Trade->trade_status = 2; // requesting trade. + otherclient->Trade->trade_status = 1; // other client needs to respond + break; + case 1: + // ACCEPT TRADE + RESETPACKET( pak, 0x7c0 ); + ADDBYTE( pak, 1 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); // send a trade accept packet + thisclient->Trade->trade_status = 3; // set both trade status bits to 3 (trade window open for both players) + otherclient->Trade->trade_status = 3; + for(int i=0; i<11; i++) thisclient->Trade->trade_count[i] = 0; + for(int i=0; i<10; i++) thisclient->Trade->trade_itemid[i] = 0; + for(int i=0; i<11; i++) otherclient->Trade->trade_count[i] = 0; + for(int i=0; i<10; i++) otherclient->Trade->trade_itemid[i] = 0; + break; + case 2: // REJECT TRADE + case 3: // CANCEL TRADE + // reject and cancel both do the same thing + RESETPACKET( pak, 0x7c0 ); + ADDBYTE( pak, 3 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); // send packet to cancel trade + thisclient->Trade->trade_target = 0; // remove trade targets from both clients. + otherclient->Trade->trade_target = 0; + thisclient->Trade->trade_status = 0; // set trade status to 0 for both clients + thisclient->Trade->trade_status = 0; + break; + case 4: + // CHECK READY + RESETPACKET( pak, 0x7c0 ); + ADDBYTE( pak, 4 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); // send trade locked packet to other client + thisclient->Trade->trade_status = 4; // set MY OWN trade status to locked + break; + case 5: + // UNCHECK READY + // seems we can't do this right now + break; + case 6: + // TRADE DONE + if (thisclient->Trade->trade_status == 6) + thisclient->Trade->trade_status = 4; + else + thisclient->Trade->trade_status = 6; + if (otherclient->Trade->trade_status == 6) + { + RESETPACKET( pak, 0x7c0 ); + // Complete the trade + ADDBYTE( pak, 6 ); + ADDWORD( pak, otherclient->clientid ); + ADDBYTE( pak, 0 ); + thisclient->client->SendPacket( &pak ); + RESETPACKET( pak, 0x7c0 ); + ADDBYTE( pak, 6 ); + ADDWORD( pak, thisclient->clientid ); + ADDBYTE( pak, 0 ); + otherclient->client->SendPacket( &pak ); + + // Update the zuly + thisclient->CharInfo->Zulies -= thisclient->Trade->trade_count[0x0a]; + otherclient->CharInfo->Zulies -= otherclient->Trade->trade_count[0x0a]; + thisclient->CharInfo->Zulies += otherclient->Trade->trade_count[0x0a]; + otherclient->CharInfo->Zulies += thisclient->Trade->trade_count[0x0a]; + + unsigned tucount = 0; + unsigned oucount = 0; + + // Begin this clients inventory update + BEGINPACKET( pakt, 0x717 ); + ADDQWORD( pakt, thisclient->CharInfo->Zulies ); + ADDBYTE( pakt, 0 ); + + // Begin the other clients inventory update + BEGINPACKET( pako, 0x717 ); + ADDQWORD( pako, otherclient->CharInfo->Zulies ); + ADDBYTE( pako, 0 ); + + for (unsigned i=0; i<10; i++) + { + if(thisclient->Trade->trade_count[i] > 0) + { + CItem thisitem = thisclient->items[thisclient->Trade->trade_itemid[i]]; + unsigned newslot = otherclient->GetNewItemSlot( thisitem ); + if(newslot==0xffff) continue; + thisclient->items[thisclient->Trade->trade_itemid[i]].count -= thisclient->Trade->trade_count[i]; + if( thisclient->items[thisclient->Trade->trade_itemid[i]].count<=0) + ClearItem(thisclient->items[thisclient->Trade->trade_itemid[i]]); + otherclient->items[newslot]=thisitem; + otherclient->items[newslot].count = thisclient->Trade->trade_count[i]; + ADDBYTE( pakt, (unsigned char)thisclient->Trade->trade_itemid[i] ); + ADDWORD( pakt, BuildItemHead( thisclient->items[thisclient->Trade->trade_itemid[i]] ) ); + ADDDWORD( pakt, BuildItemData( thisclient->items[thisclient->Trade->trade_itemid[i]] ) ); + ADDBYTE( pako, newslot ); + ADDWORD( pako, BuildItemHead( otherclient->items[newslot] ) ); + ADDDWORD( pako, BuildItemData( otherclient->items[newslot] ) ); + tucount++; + oucount++; + } + if(otherclient->Trade->trade_count[i] > 0) + { + CItem thisitem = otherclient->items[otherclient->Trade->trade_itemid[i]]; + unsigned newslot = thisclient->GetNewItemSlot( thisitem ); + if(newslot==0xffff) continue; + otherclient->items[otherclient->Trade->trade_itemid[i]].count -= otherclient->Trade->trade_count[i]; + if( otherclient->items[otherclient->Trade->trade_itemid[i]].count<=0 ) + ClearItem( otherclient->items[otherclient->Trade->trade_itemid[i]] ); + thisclient->items[newslot]=thisitem; + thisclient->items[newslot].count = otherclient->Trade->trade_count[i]; + ADDBYTE( pako, (unsigned char)otherclient->Trade->trade_itemid[i] ); + ADDWORD( pako, BuildItemHead( otherclient->items[otherclient->Trade->trade_itemid[i]] ) ); + ADDDWORD( pako, BuildItemData( otherclient->items[otherclient->Trade->trade_itemid[i]] ) ); + ADDBYTE( pakt, newslot ); + ADDWORD( pakt, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD( pakt, BuildItemData( thisclient->items[newslot] ) ); + tucount++; + oucount++; + } + } + + // Update itemcount and send + SETBYTE( pakt, 8, tucount ); + SETBYTE( pako, 8, oucount ); + thisclient->client->SendPacket( &pakt ); + otherclient->client->SendPacket( &pako ); + thisclient->Trade->trade_status=0; + otherclient->Trade->trade_status=0; + thisclient->saveinventory(); + otherclient->saveinventory(); + } + break; + case 7: + // TRADE BUSY + break; + case 8: + // TRADE TOO FAR + break; + case 9: + // NOT A VALID TARGET + break; + case 10: + // OUT OF INVENTORY + break; + case 11: + // + default: + break; + } + + return true; +} + +// Trade action +bool CWorldServer::pakTradeAdd ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + BYTE islot = GETBYTE((*P),0); + WORD slotid = GETWORD((*P),1); + DWORD count = GETDWORD((*P),3); + CPlayer* otherclient = GetClientByID( thisclient->Trade->trade_target, thisclient->Position->Map ); + if (otherclient == NULL) return true; + BEGINPACKET( pak, 0x7c1 ); + ADDBYTE( pak, islot ); + if( islot == 0x0a ) + { + ADDWORD( pak, 0xccdf );//0xccccccdf ); + ADDDWORD( pak, count ); + thisclient->Trade->trade_count[islot] = count; + } + else + { + if(islot>0x0a) + return false; + if( count != 0 ) + { + if(count>thisclient->items[slotid].count) + return false; + thisclient->Trade->trade_count[islot] = count; + thisclient->Trade->trade_itemid[islot] = slotid; + CItem tmpitem = thisclient->items[slotid]; tmpitem.count = count; + ADDWORD( pak, BuildItemHead( tmpitem ) ); + ADDDWORD( pak, BuildItemData( tmpitem ) ); + } + else + { + thisclient->Trade->trade_count[islot] = 0; + thisclient->Trade->trade_itemid[islot] = 0; + ADDDWORD( pak, 0 ); + ADDDWORD( pak, 0 ); + } + } + otherclient->client->SendPacket( &pak ); + return true; +} + + +// Attack skill +bool CWorldServer::pakStartSkill ( CPlayer* thisclient, CPacket* P ) +{ + + if( thisclient->Shop->open || thisclient->Status->Muted!=0xff ) + return true; + fPoint thispoint; + unsigned int skillid; + UINT targetid = GETWORD( (*P), 0 ); + BYTE skillnum = GETBYTE( (*P), 2 );//skill inventory number + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill went to pakStartSkill. Skill number = %i", skillnum); + } + if(skillnum >= MAX_SKILL) + { + Log( MSG_HACK, "Invalid Skill id %i for %s ", skillnum, thisclient->CharInfo->charname ); + return false; + } + if (skillnum > 29) + { + skillid = thisclient->askill[skillnum - 30] + thisclient->askilllvl[skillnum - 30]; + } + else + { + skillid = thisclient->bskills[skillnum]; + } + CMap* map = MapList.Index[thisclient->Position->Map]; + CCharacter* character = map->GetCharInMap( targetid ); + if(character == NULL) return true; + + if(character->IsMonster()) + { + BEGINPACKET( pak, 0x79f ); + ADDWORD( pak, character->clientid ); + ADDWORD( pak, character->Stats->HP ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + } + CSkills* thisskill = GetSkillByID( skillid ); + if(thisskill == NULL) + return true; + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill type = %i target = %i",thisskill->skilltype, thisskill->target); + } + if(!thisclient->canUseSkill(thisskill)) + { + SendPM(thisclient, "Sorry. You cannot use that skill"); + return true; + } + if(thisskill->target == 9 && !character->IsDead()) + { + ClearBattle( thisclient->Battle ); + return true; + } + //if( isSkillTargetFriendly( thisskill ) ) + //{ + // if(thisclient->Session->codedebug) //Debug code + // { + // SendPM(thisclient, "Target is friendly. Action = SKILL_BUFF"); + // } + // thisclient->StartAction( character, SKILL_BUFF, skillid ); + //} + //else + //{ + switch(thisskill->skilltype) + { + case 3: case 6: + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Target is UN-friendly. Action = SKILL_ATTACK"); + } + thisclient->StartAction( character, SKILL_ATTACK, skillid ); + break; + case 9: case 11: case 13: + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Target is either friendly or unfriedly. Action = SKILL_BUFF"); + SendPM(thisclient, "My id = %i target id = %i",thisclient->clientid, character->clientid); + } + thisclient->StartAction( character, SKILL_BUFF, skillid ); + break; + case 20: // resurrect + ClearBattle( thisclient->Battle ); + if(thisskill->target == 9) + { + if(character->IsDead()) + { + character->Stats->HP = (int)character->GetMaxHP()/10; + } + return true; + } + break; + default: + SendPM(thisclient, "Unhandled skill request"); + break; + } + //} + return true; +} + +// Self skills +bool CWorldServer::pakSkillSelf( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Skill went to pakSkillSelf" ); + SendPM(thisclient, buffer); + } + if( thisclient->Shop->open || thisclient->Status->Muted!=0xff || !thisclient->Status->CanCastSkill) + return true; + unsigned int skillid; + BYTE num = GETBYTE((*P),0); + if(num >= MAX_SKILL) + { + Log( MSG_HACK, "Invalid Skill id %i for %s ", num, thisclient->CharInfo->charname ); + return false; + } + if (num > 29) + { + skillid = thisclient->askill[num-30]+thisclient->askilllvl[num-30]; + } + else + { + skillid = thisclient->bskills[num]; + } + CSkills* thisskill = GetSkillByID( skillid ); + if(thisskill == NULL) + return true; + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Skill type = %i target = %i",thisskill->skilltype,thisskill->target); + SendPM(thisclient, buffer); + } + if(!thisclient->canUseSkill(thisskill)) + { + SendPM(thisclient, "Sorry. You cannot use that skill"); + return true; + } + unsigned int skilltarget = thisskill->target; + unsigned int skillrange = thisskill->aoeradius; + + if( thisskill->aoe == 0 ) + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 0. Action = BUFF_SELF"); + } + thisclient->StartAction( NULL, BUFF_SELF, skillid ); + } + else + { + if(isSkillTargetFriendly( thisskill )) + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 1 and target is friendly. Action = BUFF_AOE"); + } + thisclient->StartAction( NULL, BUFF_AOE, skillid ); + } + else + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 1 and target is UN-friendly. Action = SKILL_AOE"); + } + thisclient->StartAction( NULL, SKILL_AOE, skillid ); + } + } + return true; +} + +// Aoe Skill +bool CWorldServer::pakSkillAOE( CPlayer* thisclient, CPacket* P) +{ + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Skill went to pakSkillAOE" ); + SendPM(thisclient, buffer); + } + + if( thisclient->Shop->open || thisclient->Status->Muted != 0xff || !thisclient->Status->CanCastSkill) return true; + BYTE num = GETBYTE( (*P), 0 ); + thisclient->Position->skilltargetpos.x = P->GetFloat(1)/100; //these set an absolute aoe center. not a monster. + thisclient->Position->skilltargetpos.y = P->GetFloat(5)/100; + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "target X = %f target Y = %f",thisclient->Position->skilltargetpos.x,thisclient->Position->skilltargetpos.y ); + } + + //arnold + if(num >= MAX_SKILL) + { + Log( MSG_HACK, "Invalid Skill id %i for %s ", num, thisclient->CharInfo->charname ); + return false; + } + unsigned int skillid; + if (num > 29) + { + skillid = thisclient->askill[num-30] + thisclient->askilllvl[num-30]; + } + else + { + skillid = thisclient->bskills[num]; + } + CSkills* thisskill = GetSkillByID( skillid ); + if(thisskill == NULL) return true; + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Skill type = %i target = %i",thisskill->skilltype,thisskill->target); + SendPM(thisclient, buffer); + } + if(!thisclient->canUseSkill(thisskill)) + { + SendPM(thisclient, "Sorry. You cannot use that skill"); + return true; + } + if(thisskill->aoe == 1) + { + if(isSkillTargetFriendly( thisskill )) + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 1 and target is friendly. Action = nothing"); + } + //cout << "Friendly skill: " << thisclient->Battle->skillid << endl; + } + else + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 1 and target is UN-friendly. Action = AOE_TARGET"); + } + thisclient->StartAction( NULL , AOE_TARGET, skillid ); + } + } + else + { + if(thisclient->Session->codedebug) //Debug code + { + SendPM(thisclient, "Skill aoe is 0. Action = nothing"); + } + Log( MSG_INFO, "no aoe, skillid = %i ", thisclient->Battle->skillid); + } + return true; +} + +// Consumible Items +bool CWorldServer::pakUseItem ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + BYTE slot = GETBYTE((*P),0); + if(!CheckInventorySlot( thisclient, slot)) + return false; + if( thisclient->items[slot].count<=0 ) + return true; + CUseInfo* thisuse = GetUseItemInfo( thisclient, slot); + if(thisuse == NULL) + { + Log(MSG_WARNING,"[%i]Invalid Item, Item: %i. - Type: %i", + thisclient->clientid, thisclient->items[slot].itemnum, + thisclient->items[slot].itemtype); + return true; + } + bool flag = false; + switch(thisuse->usescript) + { + case 1: // Food + { + thisclient->UsedItem->usevalue = thisuse->usevalue; + thisclient->UsedItem->usetype = thisuse->usetype; + thisclient->UsedItem->userate = 15; + thisclient->UsedItem->used = 0; + BEGINPACKET( pak,0x7a3 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisuse->itemnum ); + SendToVisible( &pak, thisclient ); + flag = true; + Log(MSG_WARNING, "Food item used. type %i Value %i", thisuse->usetype, thisuse->usevalue ); + } + break; + case 2: // Return Scroll + { + BEGINPACKET( pak,0x7a3 ); + ADDWORD ( pak,thisclient->clientid ); + ADDWORD ( pak, thisuse->itemnum ); + ADDBYTE ( pak,slot ); + thisclient->client->SendPacket( &pak ); + //thisclient->items[slot].count -= 1; + //if( thisclient->items[slot].count == 0 ) + // ClearItem( thisclient->items[slot] ); + fPoint thispoint; + thispoint.x = floor( (double)thisuse->usevalue/10000 ); + thispoint.y = floor( (double)(thisuse->usevalue%10000) ); + TeleportTo ( thisclient, thisuse->usetype, thispoint ); + flag = true; + } + break; + case 3: // Charm Scroll + { + CSkills* thisskill = GetSkillByID( thisuse->usetype ); + if(thisskill == NULL) + { + Log(MSG_WARNING, "Char %s tried to use invalid scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + delete thisuse; + return true; + } + int skilltarget = thisskill->target; + int skillrange = thisskill->aoeradius; + if( thisskill->aoe == 0 ) + { + thisclient->StartAction( NULL,BUFF_SELF,thisuse->usetype ); + } + else + { + if(isSkillTargetFriendly( thisskill )) + { + thisclient->StartAction( NULL,BUFF_AOE,thisuse->usetype ); + } + else + { + thisclient->StartAction( NULL,SKILL_AOE,thisuse->usetype ); + } + } + flag = true; + } + break; + case 4: // Dance Scroll | FireCrackers | Emotion + { + //Start Animation + BEGINPACKET( pak, 0x7b2 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisuse->usevalue ); + SendToVisible( &pak, thisclient ); + //Finish Animation + RESETPACKET( pak, 0x7bb ); + ADDWORD ( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient ); + //???? + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, thisclient->clientid); + ADDWORD ( pak, thisuse->usevalue ); + SendToVisible( &pak, thisclient ); + flag = true; + } + break; + case 12: //Dance scrolls + { + CSkills* thisskill = GetSkillByID( thisuse->usetype ); + if(thisskill == NULL) + { + Log(MSG_WARNING, "Char %s tried to use invalid Dance scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + delete thisuse; + return true; + } + //Log(MSG_INFO, "Char %s used a Dance scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + int skilltarget = thisskill->target; + int skillrange = thisskill->aoeradius; + flag = true; + //Log(MSG_INFO, "Sending skill request. AOE range = %i", skillrange); + if(skillrange > 0 ) //AOE effect + { + thisclient->StartAction( NULL,BUFF_AOE,thisuse->usetype ); + } + else + { + thisclient->StartAction( NULL,BUFF_SELF,thisuse->usetype ); + } + flag = true; + } + break; + case 13: // Fireworks + { + CSkills* thisskill = GetSkillByID( thisuse->usetype ); + if(thisskill == NULL) + { + Log(MSG_WARNING, "Char %s tried to use invalid Dance scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + delete thisuse; + return true; + } + //Log(MSG_INFO, "Char %s used a Dance scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + int skilltarget = thisskill->target; + int skillrange = thisskill->aoeradius; + flag = true; + Log(MSG_INFO, "Sending skill request. AOE range = %i", skillrange); + if(skillrange > 0 ) //AOE effect + { + thisclient->StartAction( NULL,BUFF_AOE,thisuse->usetype ); + } + else + { + Log(MSG_INFO, "Char %s used an invalid firework type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + } + flag = true; + } + break; + case 5: // summon + case 11: // Summon ownerless + { + CNPCData* thisnpc = GetNPCDataByID( thisuse->usevalue ); + if(thisnpc == NULL) + { + Log( MSG_WARNING,"[%i]NPCDATA NOT Founded: %i.", + thisclient->clientid,thisuse->usevalue ); + delete thisuse; + return true; + } + //Start Animation + BEGINPACKET( pak, 0x7b2 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisuse->usetype ); + ADDBYTE ( pak, 6 ); + SendToVisible( &pak, thisclient ); + //Finish Animation + RESETPACKET( pak, 0x7bb ); + ADDWORD ( pak, thisclient->clientid ); + SendToVisible( &pak, thisclient ); + //???? + RESETPACKET( pak, 0x7b9); + ADDWORD ( pak, thisclient->clientid); + ADDWORD ( pak, thisuse->usetype ); + SendToVisible( &pak, thisclient ); + // Add our Mob to the mobs list + fPoint position = RandInCircle( thisclient->Position->current, 5 ); + CMap* map = MapList.Index[thisclient->Position->Map]; + int owner = 0; + if(thisuse->usescript == 5) owner = thisclient->clientid; + map->AddMonster( thisuse->usevalue, position, owner,0,1 ); + flag = true; + } + break; + case 6: // Snowball + { + WORD clientid = GETWORD((*P),2); + fPoint thispoint; + CMap* map = MapList.Index[thisclient->Position->Map]; + CCharacter* character = map->GetCharInMap( clientid ); + if(character == NULL) return true; + thisclient->StartAction( character, SKILL_BUFF, thisuse->usevalue ); + flag = true; + } + break; + case 7: // scrolls [damage,maxhp,...] + { + CSkills* thisskill = GetSkillByID( thisuse->usetype ); + if(thisskill == NULL) + { + Log(MSG_WARNING, "Char %s tried to use invalid scroll type: %i", thisclient->CharInfo->charname, thisuse->usetype ); + delete thisuse; + return true; + } + int skilltarget = thisskill->target; + int skillrange = thisskill->aoeradius; + if( thisskill->aoe == 0 ) + { + thisclient->StartAction( NULL, BUFF_SELF, thisuse->usetype ); + } + else + { + if(isSkillTargetFriendly( thisskill )) + { + thisclient->StartAction( NULL, BUFF_AOE, thisuse->usetype ); + } + else + { + thisclient->StartAction( NULL, SKILL_AOE, thisuse->usetype ); + } + } + flag = true; + } + break; + case 10: // Skill Book + { + flag = LearnSkill( thisclient, thisuse->usevalue ); + } + break; + case 14: // Fairy scroll + { + GServer->Config.ScrollFairy ++; //increase count of scroll fairies by one + int oldFairyMax = GServer->Config.FairyMax; + GServer->Config.FairyMax = (int)ceil((float)GServer->ClientList.size() / 50.0) + GServer->Config.ScrollFairy; //recalculate the maximum number of fairies + if( oldFairyMax < GServer->Config.FairyMax ) // if we increased the fairy max we need to make a dummy fairy to hold the space or we will crash the server + { + CFairy* thisfairy = new (nothrow) CFairy; + thisfairy->LastTime = clock(); + thisfairy->assigned = 0; + thisfairy->ListIndex = 0; + thisfairy->WaitTime = GServer->Config.FairyWait; + GServer->FairyList.push_back( thisfairy ); + } + for (int i=0; iassigned == false) // we found an empty fairy so let's assign it + { + FairyList.at(i)->ListIndex = 0; + FairyList.at(i)->LastTime = clock(); + FairyList.at(i)->assigned = true; + FairyList.at(i)->scroll = true; // Scroll value set to true. this IS a scroll buff fairy + thisclient->Fairy = true; + thisclient->FairyListIndex = i; // FairyList index number of our actual fairy + DoFairyStuff(thisclient, 1); // spawn fairy to target + thisclient->SetStats(); + flag = true; + break; + } + } + + break; + } + } + if(flag == true) + { + BEGINPACKET( pak,0x7a3 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisuse->itemnum ); + ADDBYTE ( pak, slot ); + thisclient->client->SendPacket( &pak ); + thisclient->items[slot].count -= 1; + if( thisclient->items[slot].count <= 0 ) + ClearItem( thisclient->items[slot] ); + thisclient->saveinventory(); + } + delete thisuse; + return true; +} + +// Level UP Skill +bool CWorldServer::pakLevelUpSkill( CPlayer *thisclient, CPacket* P ) +{ + CSkills* thisskill; + CSkills* nextskill; + char rs = 0; + char rl = 0; + int pos = GETBYTE ((*P),0); + // Check that skill is valid + if (pos > 59) + { // Passive Skill - ID + 60 + thisskill = GetSkillByID((thisclient->pskill[pos-60])+(thisclient->pskilllvl[pos-60])); + nextskill = GetSkillByID(((thisclient->pskill[pos-60])+(thisclient->pskilllvl[pos-60]))+1); + } + else if (pos > 29) + { // Active Skill - ID + 30 + thisskill = GetSkillByID((thisclient->askill[pos-30])+(thisclient->askilllvl[pos-30])); + nextskill = GetSkillByID(((thisclient->askill[pos-30])+(thisclient->askilllvl[pos-30]))+1); + } + else return true; + // Make sure skill is found + if (thisskill == NULL || nextskill == NULL) return true; + if(thisskill->skillid != nextskill->skillid) return true; //if skillid is different then we have gone off the end of the skill levels + + // Check SP requirement + if (thisclient->CharInfo->SkillPoints < nextskill->sp) { + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x02 ); + thisclient->client->SendPacket( &pak ); + return true; + } + + // Check zuly requirement + if (thisclient->CharInfo->Zulies < nextskill->zuly) { + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x01 ); + thisclient->client->SendPacket( &pak ); + return true; + } + + // Check both stat requirements. + bool fail = false; + for (char i = 0; i < 2; i++) + { + switch (nextskill->req[i]) + { + case sLevel: + if (nextskill->reqam[i] > thisclient->Stats->Level) fail = true; + break; + case sStrength: + if (nextskill->reqam[i] > thisclient->Attr->Str) fail = true; + break; + case sDexterity: + if (nextskill->reqam[i] > thisclient->Attr->Dex) fail = true; + break; + case sIntelligence: + if (nextskill->reqam[i] > thisclient->Attr->Int) fail = true; + break; + case sConcentration: + if (nextskill->reqam[i] > thisclient->Attr->Con) fail = true; + break; + case sCharm: + if (nextskill->reqam[i] > thisclient->Attr->Cha) fail = true; + break; + case sSensibility: + if (nextskill->reqam[i] > thisclient->Attr->Sen) fail = true; + break; + default: + break; + } + } + + if (fail) + { + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x03 ); + thisclient->client->SendPacket( &pak ); + return true; + } + + // Check class requirement + bool passClass = false; + int nocheck = 4; // Used to check if no lvl check + for (char i = 0; i < 4; i++) + { + if (nextskill->c_class[i]>0) + { + switch(nextskill->c_class[i]) + { + case cSoldierJob: + if((thisclient->CharInfo->Job)/100==1)passClass = true; + break; + case cMuseJob: + if((thisclient->CharInfo->Job)/100==2)passClass = true; + break; + case cHawkerJob: + if((thisclient->CharInfo->Job)/100==3)passClass = true; + break; + case cDealerJob: + if((thisclient->CharInfo->Job)/100==4)passClass = true; + break; + case cKnightJob: + if(thisclient->CharInfo->Job==121)passClass = true; + break; + case cChampionJob: + if(thisclient->CharInfo->Job==122)passClass = true; + break; + case cMageJob: + if(thisclient->CharInfo->Job==221)passClass = true; + break; + case cClericJob: + if(thisclient->CharInfo->Job==222)passClass = true; + break; + case cRaiderJob: + if(thisclient->CharInfo->Job==321)passClass = true; + break; + case cScoutJob: + if(thisclient->CharInfo->Job==322)passClass = true; + break; + case cBourgeoisJob: + if(thisclient->CharInfo->Job==421)passClass = true; + break; + case cArtisanJob: + if(thisclient->CharInfo->Job==422)passClass = true; + break; + default: + break; + } + if (passClass) break; + } else + nocheck--; + } + // Player isn't the required class/job + if (!passClass && nocheck > 0) + { + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x04 ); + thisclient->client->SendPacket( &pak ); + return true; + } + if(thisclient->Session->codedebug) //Debug code + { + char buffer[200]; + sprintf ( buffer, "Passed check for class" ); + SendPM(thisclient, buffer); + } + // Check required skills/skill levels + for (char j = 0; j < 3 ; j++) + { + if(nextskill->rskill[j]>0) + { + rs++; + for (char i = 0; i<30;i++) + { + // Subtract 1 because our characters levels are actually off by 1 + // Check passive skill + if ( ( thisclient->pskill[i] == nextskill->rskill[j] ) && + ( thisclient->pskilllvl[i] >= nextskill->lskill[j] - 1 ) ) + { + rl++; + break; // No use continueing for the rest of the for loop, right? + } + // Check Active Skill + if ( ( thisclient->askill[i] == nextskill->rskill[j] ) && + ( thisclient->askilllvl[i] >= nextskill->lskill[j] - 1 ) ) + { + rl++; + break; // No need to continue inner loop + } + } + } + } + + if(rl != rs) + { + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x05 ); + thisclient->client->SendPacket( &pak ); + return true; + } + + if (pos > 59) thisclient->pskilllvl[pos-60] += 1; + else if (pos > 29) thisclient->askilllvl[pos-30] += 1; + thisclient->CharInfo->SkillPoints -= nextskill->sp; + thisclient->CharInfo->Zulies -= nextskill->zuly; + + BEGINPACKET( pak, 0x7b1 ); + ADDBYTE( pak, 0x00 ); + ADDBYTE( pak, GETBYTE ((*P),0) ); + ADDWORD( pak, GETWORD ((*P),1) ); + ADDWORD( pak, thisclient->CharInfo->SkillPoints ); + thisclient->client->SendPacket( &pak ); + thisclient->SetStats(); + return true; +} + +// Equip bullets arrows and cannons +bool CWorldServer::pakEquipABC ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + BYTE itemslot = GETBYTE((*P),0); + int slot = ((itemslot-32)/4)+72; + int dest = 0; + int type = (itemslot-32)%4; + if(!CheckInventorySlot( thisclient, slot)) + return false; + if(itemslot>3) + { + dest = type+132; + } + else + { + slot = itemslot + 132; + dest = thisclient->GetNewItemSlot( thisclient->items[slot] ); + if(dest==0xffff) + return true; + type=300; + } + CItem tmpitm = thisclient->items[slot]; + thisclient->items[slot] =thisclient->items[dest]; + thisclient->items[dest] = tmpitm; + + thisclient->UpdateInventory( dest, slot ); + BEGINPACKET( pak, 0x7ab ); + ADDWORD ( pak, thisclient->clientid ); + unsigned int effect = ( thisclient->items[dest].itemnum * 32 ) + type; + ADDWORD ( pak, effect ); + SendToVisible( &pak, thisclient ); + thisclient->Stats->Attack_Power = thisclient->GetAttackPower( ); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Craft item (Template by Arnold) (Crafting modifications by: Killerfly) Fixed for irose by: Arnold // +// Recoded for osProse by PurpleYouko // +////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool CWorldServer::pakCraft( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open==true) + return true; + CItem item; + UINT quality = 0; + int m = 0; + + // determine the item number and type + item.itemnum = (GETWORD((*P), 2)); + item.itemtype = (GETBYTE((*P), 0x1)); + + //get material used lookup number + int materialnumber = 0; + if(item.itemtype < 10) + { + materialnumber = EquipList[item.itemtype].Index[item.itemnum]->material; + } + else + { + switch(item.itemtype) + { + case 11:materialnumber = JemList.Index[item.itemnum]->material;break; + case 14:materialnumber = PatList.Index[item.itemnum]->material;break; + default: + break; + } + } + if(thisclient->Session->codedebug) + { + SendPM(thisclient, "Attempting to craft an item. type = %i. id = %i",item.itemtype, item.itemnum); + SendPM(thisclient, "Material number = %i",materialnumber); + } + //check material slots before we start making the new item + if(ProductList.Index[materialnumber]->matType != 0) // recipe has an item type listed rather than a specific item + { + WORD material= GETWORD((*P), 4); + UINT matid = thisclient->items[material].itemnum; + quality = NaturalList.Index[matid]->quality; + if(thisclient->Session->codedebug) + { + SendPM(thisclient, "Recipe recquires an item with type = %i.",ProductList.Index[materialnumber]->matType); + SendPM(thisclient, "Type of material in slot = %i",thisclient->items[material].type); + } + if(ProductList.Index[materialnumber]->matType != thisclient->items[material].type) + { + if(thisclient->Session->codedebug) + { + SendPM(thisclient, "Rejected as mat types do not match"); + } + return true; //item in the slot is the wrong type + } + } + m=0; + for(char used=4; used != 12; used +=2) + { + WORD material= GETWORD((*P), used);//gets inventory location + if(thisclient->Session->codedebug) + { + SendPM(thisclient, "Slot number = %i", material); + SendPM(thisclient, "Recipe recquires an item with type = %i. id = %i",ProductList.Index[materialnumber]->itemtype[m],ProductList.Index[materialnumber]->itemid[m]); + SendPM(thisclient, "Type of material in slot = %i. id = %i",thisclient->items[material].itemtype,thisclient->items[material].itemnum); + SendPM(thisclient, "Material count required = %i", ProductList.Index[materialnumber]->amount[m]); + SendPM(thisclient, "Material count in slot = %i", thisclient->items[material].count); + } + if(!CheckInventorySlot( thisclient, material)) + { + SendPM(thisclient, "Crafting Error 0: Missing or incorrect material in inventory slot."); + return true; + } + if (material != 0) + { + if(ProductList.Index[materialnumber]->matType != 0 && m == 0) //only checked for the first item in the recipe + { + if(thisclient->items[material].type != ProductList.Index[materialnumber]->matType) + { + SendPM(thisclient, "Crafting Error %i 1: Server and Client crafting recipes do not match",m); + return true; // item type does not match recipe + } + } + else + { + if(thisclient->items[material].itemtype != ProductList.Index[materialnumber]->itemtype[m]) + { + SendPM(thisclient, "Crafting Error %i 2: Server and Client crafting recipes do not match",m); + return true; // item type does not match recipe + } + if(thisclient->items[material].itemnum != ProductList.Index[materialnumber]->itemid[m]) + { + SendPM(thisclient, "Crafting Error %i 3: Server and Client crafting recipes do not match",m); + return true; // item id does not match recipe + } + } + if(thisclient->items[material].count < ProductList.Index[materialnumber]->amount[m]) + { + SendPM(thisclient, "Crafting Error %i 4: Server and Client crafting recipes do not match",m); + return true; //not enough of the required item + } + } + m += 1; + } + if(thisclient->Session->codedebug) + { + SendPM(thisclient, "Item cleared the checks and will be created"); + } + + // item durability randomizer + int lowest = thisclient->Attr->Con/ 10 + 17; + int highest = thisclient->Attr->Con / 10 + 70; + int range = (highest - lowest) + 1; + item.durability = lowest+int(range*rand()/(RAND_MAX + 1.0)); + + // stats randomizer + int changeofstatslow = thisclient->Attr->Sen / 13 + 10; + int changeofstatshigh = thisclient->Attr->Sen / 13 + 50; + int changeofstatsrange = (changeofstatshigh-changeofstatslow)+1; + if (changeofstatslow+int(changeofstatsrange*rand()/(RAND_MAX + 1.0)) > 50) + { + int statslowget = 1; + int statshighget = 256; + int setstatrange=(statshighget-statslowget)+1; + item.stats = statslowget + int(setstatrange*rand()/(RAND_MAX + 1.0)); + item.appraised = 1; + } + else + { + item.stats = 0; + item.appraised = 0; + } + item.socketed = 0; + item.gem = 0; + item.lifespan = 100; //Its new so 100% + item.refine = 0; + item.count = 1; + + + unsigned newslot= thisclient->GetNewItemSlot( item ); + if (newslot !=0xffff) + { + //removes material items from the slots + item.count +=(thisclient->items[newslot].count); + thisclient->items[newslot] = item; + m=0; + for(char used=4; used != 12; used +=2) + { + WORD material= GETWORD((*P), used);//gets inventory location + if (material != 0) + { + thisclient->items[material].count -= ProductList.Index[materialnumber]->amount[m]; + m += 1; + if (thisclient->items[material].count <=0) + { + ClearItem(thisclient->items[material]); + } + } + } + + BEGINPACKET( pak, 0x07d8); + ADDWORD( pak, thisclient->clientid ); + ADDWORD( pak, 0x0100); + ADDWORD( pak, item.itemnum);// item id not shifted + thisclient->client->SendPacket(&pak); + + RESETPACKET( pak, 0x07af); + ADDBYTE( pak, 0x00); + ADDWORD( pak, newslot); + + // Make craft bars + int bar1 = item.durability * 9; + int bar2 = changeofstatsrange * 9; + int bar3 = item.durability + changeofstatsrange * 6; + int bar4 = item.durability + changeofstatsrange + bar3 / 3; + + ADDWORD( pak, bar1);//progress bar1 0 is empty 0x0400 is full bar + ADDWORD( pak, bar2);//progress bar2 0 is empty 0x0400 is full bar + if ((GETWORD((*P), 8))==0){ ADDWORD( pak, 0x99a0);}else{ ADDWORD( pak, bar3);}//progress bar3 0 is empty 0x0400 is full bar + if ((GETWORD((*P), 10))==0){ ADDWORD( pak, 0x99a0);}else{ ADDWORD( pak, bar4);}//progress bar4 0 is empty 0x0400 is full bar + ADDWORD(pak, BuildItemHead(item)); + + if (item.itemtype == 11) + { + ADDWORD( pak, item.count);// amount + ADDWORD( pak, 0x0000); + } + else + { + ADDDWORD(pak, BuildItemData(item)); + } + int crafting_exp = item.durability + changeofstatsrange * (thisclient->Stats->Level/ 5); + thisclient->CharInfo->Exp += crafting_exp;// add exp + thisclient->client->SendPacket(&pak); + if(!thisclient->CheckPlayerLevelUP( )){ + RESETPACKET( pak, 0x79b ); + ADDDWORD ( pak, thisclient->CharInfo->Exp ); + ADDWORD ( pak, thisclient->CharInfo->stamina ); + ADDWORD ( pak, 0 ); + thisclient->client->SendPacket( &pak );} + } + else + { + BEGINPACKET (pak, 0x702); + ADDSTRING(pak, "No free slot !"); + ADDBYTE(pak, 0); + thisclient->client->SendPacket(&pak); + } + thisclient->saveinventory(); + return true; +} + + +bool CWorldServer::pakModifiedItemDone( CPlayer* thisclient, CPacket* P ) +{ + DWORD result = GETDWORD((*P), 0x00 ); + BEGINPACKET( pak, 0x07d8); + ADDWORD( pak, thisclient->clientid ); + ADDDWORD( pak, result); + SendToVisible( &pak , thisclient); + return true; + +} + + + +// Identify Item +bool CWorldServer::pakidentify( CPlayer* thisclient, CPacket* P) +{ + WORD itemslot = GETWORD ((*P),0); + if(!CheckInventorySlot( thisclient, itemslot)) + return false; + thisclient->items[itemslot].appraised = true; + BEGINPACKET( pak, 0x7ba ); + ADDWORD ( pak, itemslot ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// Show Storge Items +bool CWorldServer::pakStorage( CPlayer* thisclient, CPacket* P) +{ + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 0x00: + { + UINT nstorageitems = thisclient->Getnstorageitems(thisclient); + Log( MSG_INFO, "Number of items in storage: %i ", nstorageitems); + BEGINPACKET( pak, 0x7ad ); + ADDBYTE ( pak, 0x00 ); + ADDBYTE ( pak, nstorageitems ); //numero de items + UINT counter = 0; + for(int i=0;i<160;i++) + { + if( thisclient->storageitems[i].itemtype !=0 && counter < 144) //packet can only handle 144 items + { + ADDBYTE ( pak, i ); + ADDWORD ( pak, BuildItemHead( thisclient->storageitems[i] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->storageitems[i] ) ); + counter ++; + } + } + ADDQWORD( pak, thisclient->CharInfo->Storage_Zulies ); + thisclient->client->SendPacket( &pak ); + } + break; + default: + Log( MSG_INFO, "Storage unknow action: %i ", action); + } + return true; +} + +// Change Storage (Deposit/Withdraw items) +bool CWorldServer::pakChangeStorage( CPlayer* thisclient, CPacket* P) +{ + /* Packet for this function + =========================== + [BYTE] Action + [BYTE] Item Slot + [WORD] Item Head + [DWORD] Item Body + [BYTE] Unk (0x00) + + */ + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 0x00: //Deposit + { + int itemslot = (int)GETBYTE((*P),1); + CItem clientitem = GetItemByHeadAndData( GETWORD((*P), 2), GETDWORD((*P), 4) ); + + if(!CheckInventorySlot( thisclient, itemslot )) + return false; + CItem newitem = thisclient->items[itemslot]; + //if(newitem == NULL) + // return true; + // Set the newitem's count to the count from the client for slot search + newitem.count = clientitem.count; + + Log(MSG_INFO, "New Item: %i %i", newitem.itemtype, newitem.itemnum); + // find an empty or matching slot depending on new item type + int newslot = thisclient->GetNewStorageItemSlot ( newitem ); + if(newslot == 0xffff) + return true; + if(newitem.itemtype >= 10 && newitem.itemtype <= 12) //is it a stackable type + { + int count = clientitem.count; + if(count > thisclient->items[itemslot].count) + count = thisclient->items[itemslot].count; //stops players transferring more than they actually have + newitem.count = count; + + if (thisclient->storageitems[newslot].count > 0) + thisclient->storageitems[newslot].count += newitem.count; //add the new items to the storage slot + else + thisclient->storageitems[newslot] = newitem; + + thisclient->items[itemslot].count -= count; //delete the items from inventory slot + if(thisclient->items[itemslot].count <= 0) + ClearItem(thisclient->items[itemslot]); + } + else + { + // non-stackable + ClearItem(thisclient->items[itemslot]); + thisclient->storageitems[newslot] = newitem; + } + BEGINPACKET( pak, 0x7ae ); + ADDWORD ( pak, itemslot ); + ADDWORD ( pak, newslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[itemslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[itemslot] ) ); + ADDWORD ( pak, BuildItemHead( thisclient->storageitems[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->storageitems[newslot] ) ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + //thisclient->nstorageitems++; //this is pointless and serves no purpose + } + break;//thanks to anon for post that this break was missing + case 0x01: //Withdraw + { + BYTE storageslot = GETBYTE((*P),1); + CItem clientitem = GetItemByHeadAndData( GETWORD((*P), 2), GETDWORD((*P), 4) ); + + if(storageslot >= 160) + { + Log( MSG_HACK, "Invalid storage slot %i from %s", storageslot, thisclient->Session->username ); + return false; + } + CItem newitem = thisclient->storageitems[storageslot]; + newitem.count = clientitem.count; + + int newslot= thisclient->GetNewItemSlot ( newitem ); + if (newslot == 0xffff) + return true; + + if(newitem.itemtype > 9 && newitem.itemtype < 14) + { + WORD count = clientitem.count; + if( count > thisclient->storageitems[storageslot].count ) + count = thisclient->storageitems[storageslot].count; + newitem.count = count; + + if (thisclient->items[newslot].count > 0) + thisclient->items[newslot].count += newitem.count; + else + thisclient->items[newslot] = newitem; + + thisclient->storageitems[storageslot].count -= count; + if(thisclient->storageitems[storageslot].count<=0) + ClearItem(thisclient->storageitems[storageslot]); + } + else + { + ClearItem(thisclient->storageitems[storageslot]); + thisclient->items[newslot] = newitem; + } + + BEGINPACKET( pak, 0x7ae ); + ADDWORD ( pak, newslot ); + ADDWORD ( pak, storageslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[newslot] ) ); + ADDWORD ( pak, BuildItemHead( thisclient->storageitems[storageslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->storageitems[storageslot] ) ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + //thisclient->nstorageitems--; //this is pointless and serves no purpose + } + break; + default: + Log( MSG_INFO, "Storage unknow action: %i ", action); + } + thisclient->saveinventory(); + thisclient->savestorage(); + return true; +} + +// Deposit/Widthdraw zulies +bool CWorldServer::pakStoreZuly( CPlayer* thisclient, CPacket* P) +{ + BYTE action = GETBYTE ((*P), 0 ); + QWORD zuly = GETQWORD ((*P), 1 ); + switch(action) + { + case 0x10://deposit' + { + if(zuly > thisclient->CharInfo->Zulies) + return true; + thisclient->CharInfo->Zulies -= zuly; + thisclient->CharInfo->Storage_Zulies += zuly; + BEGINPACKET( pak, 0x7da ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDQWORD ( pak, thisclient->CharInfo->Storage_Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + } + break; + case 0x11://withdraw + { + if(zuly > thisclient->CharInfo->Storage_Zulies ) + return true; + thisclient->CharInfo->Zulies += zuly; + thisclient->CharInfo->Storage_Zulies -= zuly; + BEGINPACKET( pak, 0x7da ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDQWORD ( pak, thisclient->CharInfo->Storage_Zulies ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + } + break; + default: + { + Log( MSG_INFO, "Storage unknow action: %i ", action); + break; + } + } + thisclient->saveinventory(); + thisclient->savestorage(); + return true; +} + +// Open Shop +bool CWorldServer::pakOpenShop( CPlayer* thisclient, CPacket* P ) +{ +//Log( MSG_INFO, "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x ", GETBYTE((*P),0), GETBYTE((*P),1), GETBYTE((*P),2), GETBYTE((*P),3), GETBYTE((*P),4), GETBYTE((*P),5), GETBYTE((*P),6), GETBYTE((*P),7), GETBYTE((*P),8), GETBYTE((*P),9), GETBYTE((*P),10), GETBYTE((*P),11), GETBYTE((*P),12), GETBYTE((*P),13), GETBYTE((*P),14), GETBYTE((*P),15), GETBYTE((*P),16), GETBYTE((*P),17), GETBYTE((*P),18), GETBYTE((*P),19), GETBYTE((*P),20), GETBYTE((*P),21), GETBYTE((*P),22), GETBYTE((*P),23), GETBYTE((*P),24) ); + if( thisclient->Shop->open ) + return true; + BYTE nselling = GETBYTE((*P),0); + BYTE nbuying = GETBYTE((*P),1); + if(nselling>30 || nbuying>30) + return true; + int nchar = ((nselling + nbuying) * 11 )+2; + strncpy_s(thisclient->Shop->name ,(char*)&(*P).Buffer[nchar] , P->Size-nchar ); + thisclient->Shop->Selling = nselling; + thisclient->Shop->Buying = nbuying; + for(int i=0;iShop->SellingList[i].slot = slot; + if( thisclient->items[slot].itemtype>9 && thisclient->items[slot].itemtype<14 ) + thisclient->Shop->SellingList[i].count = GETWORD((*P),n+3); + else + thisclient->Shop->SellingList[i].count = 1; + thisclient->Shop->SellingList[i].price = GETDWORD((*P),n+7); + } + for(int i=0;iShop->BuyingList[slot].slot = slot; + DWORD head = GETWORD((*P),n+1); + DWORD data = GETDWORD((*P),n+3); + CItem thisitem = GetItemByHeadAndData( head, data ); + thisclient->Shop->BuyingList[slot].item = thisitem; + if( thisitem.itemtype > 9 && thisitem.itemtype < 14 ) + thisclient->Shop->BuyingList[slot].count = GETWORD((*P),n+3); + else + thisclient->Shop->BuyingList[slot].count = 1; + thisclient->Shop->BuyingList[slot].price = GETDWORD((*P),n+7); + } + BEGINPACKET( pak, 0x796 ); + ADDWORD ( pak, thisclient->clientid ); + ADDFLOAT ( pak, thisclient->Position->current.x ); + ADDFLOAT ( pak, thisclient->Position->current.y ); + ADDWORD ( pak, 0x9057 );//>??? + SendToVisible( &pak, thisclient ); + + RESETPACKET( pak, 0x7c2 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, thisclient->Shop->ShopType ); + ADDSTRING ( pak, thisclient->Shop->name ); + ADDBYTE ( pak, 0x00 ); + SendToVisible( &pak, thisclient ); + thisclient->Shop->open = true; + return true; +} + +// Show Shop to other players +bool CWorldServer::pakShowShop( CPlayer* thisclient, CPacket* P ) +{ + WORD otherclientid = GETWORD((*P),0); + CPlayer* otherclient = GetClientByID ( otherclientid, thisclient->Position->Map ); + if(otherclient == NULL) + return true; + BEGINPACKET( pak, 0x7c4 ); + ADDBYTE ( pak, otherclient->Shop->Selling ); + ADDBYTE ( pak, otherclient->Shop->Buying ); + for(unsigned int i = 0; iShop->Selling;i++) + { + CItem thisitem = otherclient->items[otherclient->Shop->SellingList[i].slot]; + thisitem.count = otherclient->Shop->SellingList[i].count; + ADDBYTE ( pak, i ); + ADDWORD ( pak, BuildItemHead( thisitem ) ); + ADDDWORD ( pak, BuildItemData( thisitem ) ); + ADDDWORD ( pak, otherclient->Shop->SellingList[i].price ); + } + for(unsigned int i = 0; i<30;i++) + { + if(otherclient->Shop->BuyingList[i].count>0) + { + CItem thisitem = otherclient->Shop->BuyingList[i].item; + ADDBYTE ( pak, i ); + ADDWORD ( pak, BuildItemHead( thisitem ) ); + ADDDWORD ( pak, BuildItemData( thisitem ) ); + ADDDWORD ( pak, otherclient->Shop->BuyingList[i].price ); + } + } + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// Buy From Shop +bool CWorldServer::pakBuyShop( CPlayer* thisclient, CPacket* P ) +{ + // Log( MSG_INFO, "Size:%04x", P->Size ); + // Log( MSG_INFO, "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x ", GETBYTE((*P),0), GETBYTE((*P),1), GETBYTE((*P),2), GETBYTE((*P),3), GETBYTE((*P),4), GETBYTE((*P),5), GETBYTE((*P),6), GETBYTE((*P),7), GETBYTE((*P),8), GETBYTE((*P),9), GETBYTE((*P),10), GETBYTE((*P),11), GETBYTE((*P),12), GETBYTE((*P),13), GETBYTE((*P),14), GETBYTE((*P),15), GETBYTE((*P),16), GETBYTE((*P),17), GETBYTE((*P),18), GETBYTE((*P),19), GETBYTE((*P),20), GETBYTE((*P),21), GETBYTE((*P),22), GETBYTE((*P),23), GETBYTE((*P),24) ); + + WORD otherclientid = GETWORD((*P),0); + BYTE action = GETBYTE((*P),2); + switch(action) + { + case 0x01://check this + { + CPlayer* otherclient = GetClientByID( otherclientid, thisclient->Position->Map ); + if( otherclient == NULL ) + return true; + BYTE slot = GETBYTE((*P),3); + unsigned int count = 0; + unsigned int invslot = otherclient->Shop->SellingList[slot].slot; + CItem newitem = otherclient->items[invslot]; + if(otherclient->items[invslot].itemtype>9 && + otherclient->items[invslot].itemtype<14) + count = GETWORD((*P),6); + else + count = 1; + if( count > otherclient->Shop->SellingList[slot].count ) + return true; + newitem.count = count; + if( thisclient->CharInfo->Zulies<(otherclient->Shop->SellingList[slot].price*count) ) + return true; + unsigned int newslot = thisclient->GetNewItemSlot ( newitem ); + if(newslot==0xffff) + return true; + thisclient->CharInfo->Zulies -= (otherclient->Shop->SellingList[slot].price*count); + otherclient->CharInfo->Zulies += (otherclient->Shop->SellingList[slot].price*count); + if(otherclient->items[invslot].itemtype>9 && + otherclient->items[invslot].itemtype<14) + { + if(otherclient->items[invslot].count<=count) + { + ClearItem(otherclient->items[invslot]) + otherclient->Shop->SellingList[slot].slot = 0; + otherclient->Shop->SellingList[slot].count = 0; + otherclient->Shop->SellingList[slot].price = 0; + } + else + { + if(otherclient->Shop->SellingList[slot].count<=count) + { + otherclient->Shop->SellingList[slot].slot = 0; + otherclient->Shop->SellingList[slot].count = 0; + otherclient->Shop->SellingList[slot].price = 0; + } + else + { + otherclient->Shop->SellingList[slot].count -= count; + } + otherclient->items[invslot].count-=count; + } + } + else + { + ClearItem( otherclient->items[invslot] ) + otherclient->Shop->SellingList[slot].slot = 0; + otherclient->Shop->SellingList[slot].count = 0; + otherclient->Shop->SellingList[slot].price = 0; + } + if(thisclient->items[newslot].count!=0) + thisclient->items[newslot].count += newitem.count; + else + thisclient->items[newslot] = newitem; + //update inventory (buyer) + BEGINPACKET( pak, 0x7c7 ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x01 ); //buy action + ADDBYTE ( pak, newslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[newslot] ) ); + thisclient->client->SendPacket( &pak ); + + //update inventory (seller) + RESETPACKET( pak, 0x7c7 ); + ADDQWORD ( pak, otherclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x01 ); //buy action + ADDBYTE ( pak, invslot ); + ADDWORD ( pak, BuildItemHead( otherclient->items[invslot] ) ); + ADDDWORD ( pak, BuildItemData( otherclient->items[invslot] ) ); + otherclient->client->SendPacket( &pak ); + + //update shop + RESETPACKET( pak, 0x7c6 ); + ADDWORD ( pak, otherclient->clientid ); + ADDBYTE ( pak, 0x05 ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, slot ); + if(otherclient->Shop->SellingList[slot].count<=0) + { + ADDDWORD ( pak, 0x00000000 ); + ADDDWORD ( pak, 0x00000000 ); + } + else + { + ADDWORD ( pak, BuildItemHead( otherclient->items[invslot] )); + ADDDWORD ( pak, BuildItemData( otherclient->items[invslot] )); + } + SendToVisible( &pak, otherclient ); + } + break; + default: + Log( MSG_WARNING, "Buy unknow action: %i", action ); + } + return true; +} + +// Sell in Shop +bool CWorldServer::pakSellShop( CPlayer* thisclient, CPacket* P ) +{ + //Log( MSG_INFO, "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x ", GETBYTE((*P),0), GETBYTE((*P),1), GETBYTE((*P),2), GETBYTE((*P),3), GETBYTE((*P),4), GETBYTE((*P),5), GETBYTE((*P),6), GETBYTE((*P),7), GETBYTE((*P),8), GETBYTE((*P),9), GETBYTE((*P),10), GETBYTE((*P),11), GETBYTE((*P),12), GETBYTE((*P),13), GETBYTE((*P),14), GETBYTE((*P),15), GETBYTE((*P),16), GETBYTE((*P),17), GETBYTE((*P),18), GETBYTE((*P),19), GETBYTE((*P),20), GETBYTE((*P),21), GETBYTE((*P),22), GETBYTE((*P),23), GETBYTE((*P),24) ); + + WORD otherclientid = GETWORD((*P),0); + BYTE action = GETBYTE((*P),2); + switch(action) + { + case 0x01: + { + CPlayer* otherclient = GetClientByID( otherclientid, thisclient->Position->Map ); + if( otherclient == NULL ) + return true; + unsigned int count = 0; + BYTE invslot = GETBYTE((*P),3); + BYTE slot = GETBYTE((*P),4); + if(!CheckInventorySlot( thisclient, slot)) + return false; + CItem newitem = thisclient->items[invslot]; + if(thisclient->items[invslot].itemtype>9 && thisclient->items[invslot].itemtype<14) + count = GETWORD((*P),7); + else + count = 1; + newitem.count = count; + if( otherclient->CharInfo->Zulies<(otherclient->Shop->BuyingList[slot].price*count) ) + return true; + unsigned int newslot = otherclient->GetNewItemSlot ( newitem ); + if( newslot==0xffff ) + return true; + if( thisclient->items[invslot].countCharInfo->Zulies += (otherclient->Shop->BuyingList[slot].price*count); + otherclient->CharInfo->Zulies -= (otherclient->Shop->BuyingList[slot].price*count); + if(thisclient->items[invslot].itemtype>9 && + thisclient->items[invslot].itemtype<14) + { + if(thisclient->items[invslot].count<=count) + { + ClearItem( thisclient->items[invslot] ); + if(count>=otherclient->Shop->BuyingList[slot].count) + { + ClearItem( otherclient->Shop->BuyingList[slot].item ); + otherclient->Shop->BuyingList[slot].slot = 0; + otherclient->Shop->BuyingList[slot].count = 0; + otherclient->Shop->BuyingList[slot].price = 0; + } + else + { + otherclient->Shop->BuyingList[slot].count -= count; + } + } + else + { + if(otherclient->Shop->BuyingList[slot].count<=count) + { + ClearItem( otherclient->Shop->BuyingList[slot].item ); + otherclient->Shop->BuyingList[slot].slot = 0; + otherclient->Shop->BuyingList[slot].count = 0; + otherclient->Shop->BuyingList[slot].price = 0; + } + else + { + otherclient->Shop->BuyingList[slot].count -= count; + } + thisclient->items[invslot].count-=count; + } + } + else + { + ClearItem( thisclient->items[invslot] ) + ClearItem( otherclient->Shop->BuyingList[slot].item ); + otherclient->Shop->BuyingList[slot].slot = 0; + otherclient->Shop->BuyingList[slot].count = 0; + otherclient->Shop->BuyingList[slot].price = 0; + } + if(otherclient->items[newslot].count!=0) + otherclient->items[newslot].count += newitem.count; + else + otherclient->items[newslot] = newitem; + + //update inventory (seller) + BEGINPACKET( pak, 0x7c7 ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, invslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[invslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[invslot] ) ); + thisclient->client->SendPacket( &pak ); + + //update inventory (buyer) + RESETPACKET( pak, 0x7c7 ); + ADDQWORD ( pak, otherclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, newslot ); + ADDWORD ( pak, BuildItemHead( otherclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( otherclient->items[newslot] ) ); + otherclient->client->SendPacket( &pak ); + + //update shop + CItem thisitem = otherclient->Shop->BuyingList[slot].item; + RESETPACKET( pak, 0x7c6 ); + ADDWORD ( pak, otherclient->clientid ); + ADDBYTE ( pak, 0x07 ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, slot ); + ADDWORD ( pak, BuildItemHead( thisitem ) ); + ADDDWORD ( pak, BuildItemData( thisitem ) ); + SendToVisible( &pak, otherclient ); + } + break; + default: + Log( MSG_WARNING, "Sell unknow action: %i", action ); + } + return true; +} + +// Close Shop +bool CWorldServer::pakCloseShop( CPlayer* thisclient, CPacket* P ) +{ + BEGINPACKET( pak, 0x7c3 ); + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, 0x00 ); + SendToVisible( &pak, thisclient ); + thisclient->Shop->open = false; + for(unsigned int j=0;j<30;j++) + { + thisclient->Shop->SellingList[j].slot = 0; + thisclient->Shop->SellingList[j].count = 0; + thisclient->Shop->SellingList[j].price = 0; + thisclient->Shop->BuyingList[j].slot = 0; + thisclient->Shop->BuyingList[j].count = 0; + thisclient->Shop->BuyingList[j].price = 0; + ClearItem( thisclient->Shop->BuyingList[j].item ); + + } + return true; +} + +// Modified item (put gem, refine, Drill ) +bool CWorldServer::pakModifiedItem( CPlayer* thisclient, CPacket* P ) +{ + printf("[DEBUG]: "); + for (int i = 0;i<(P->Size-6);i++) + { + printf("%i ",GETBYTE((*P),i)); + } + printf("\n"); + if(thisclient->Shop->open==true) + return true; + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 0x01://Gemming + { + BYTE destslot = GETBYTE((*P),1); + BYTE srcslot = GETBYTE((*P),2); + if(!CheckInventorySlot( thisclient, destslot)) + return false; + if(!CheckInventorySlot( thisclient, srcslot)) + return false; + if( thisclient->items[srcslot].count<=0 ) + { + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x12 ); // dont have requerid material + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; + } + if( thisclient->items[destslot].gem!=0 ) + { + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x02 ); //no socket available + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; + } + thisclient->items[destslot].gem = thisclient->items[srcslot].itemnum; + thisclient->items[destslot].stats = thisclient->items[srcslot].itemnum; + thisclient->items[srcslot].count--; + if(thisclient->items[srcslot].count<1) + ClearItem( thisclient->items[srcslot] ); + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x01 );//gemming success + ADDBYTE ( pak, 0x02 ); + ADDBYTE ( pak, destslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[destslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[destslot] ) ); + ADDBYTE ( pak, srcslot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[srcslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[srcslot] ) ); + thisclient->client->SendPacket( &pak ); + thisclient->SetStats( ); + thisclient->saveinventory(); + } + break; //case 0x02 ( Surprise gift box ) + case 0x02: //itemdivide by user + case 0x03: //itemdivide by npc + { + BYTE slot = GETBYTE((*P),3); + if(!CheckInventorySlot( thisclient, slot)) + { + SendPM(thisclient, "Sorry! Checkinventory returned false for slot %i", slot); + return true; + } + BEGINPACKET( pak, 0x7bc ) + ADDBYTE ( pak, 0x07 );//successful + ADDBYTE ( pak, 0x01 ); + CItem splititem; + char updateteller = 0; + UINT material_lookup_number = STB_ITEM[(thisclient->items[slot].itemtype)-1].rows[thisclient->items[slot].itemnum][14]; + UINT CraftLevel = STB_ITEM[(thisclient->items[slot].itemtype)-1].rows[thisclient->items[slot].itemnum][13]; + SendPM(thisclient, "Material lookup number = %i Craft Level = %i", material_lookup_number,CraftLevel); + + //first item + int matType = ProductList.Index[material_lookup_number]->matType; + int item1 = ProductList.Index[material_lookup_number]->itemid[0]; + int itemtype1 = ProductList.Index[material_lookup_number]->itemtype[0]; + int itemcount1 = ProductList.Index[material_lookup_number]->amount[0]; + int itemcount2 = ProductList.Index[material_lookup_number]->amount[1]; + int itemcount3 = ProductList.Index[material_lookup_number]->amount[2]; + int itemcount4 = ProductList.Index[material_lookup_number]->amount[3]; + unsigned slot1 = 0; + unsigned slot2 = 0; + unsigned slot3 = 0; + unsigned slot4 = 0; + if(matType != 0) + { + // we have to find one of the correct item types rather than just a specific item. Seems to be related to craft skill level + SendPM(thisclient, "item matType = %i", matType); + splititem.itemtype = 12; // always seems to be a natural item here + // Search the natural list and return the nth item in the list where n is the craft level of the item + splititem.itemnum = GetMatIDfromRecipe (CraftLevel, matType); + if(splititem.itemnum == 0) + { + SendPM(thisclient, "Item number returned as zero"); + return true; + } + SendPM(thisclient, "Item number = %i type = %i", splititem.itemnum, splititem.itemtype); + } + else + { + splititem.itemtype = itemtype1; + splititem.itemnum = item1; + //need to adjust the count according to some rules + } + splititem.count = itemcount1; + SendPM(thisclient, "Item count = %i", splititem.count); + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + slot1 = thisclient->GetNewItemSlot( splititem ); + if (slot1 != 0xffff) + { + splititem.count += (thisclient->items[slot1].count); + thisclient->items[slot1] = splititem; + ADDBYTE ( pak, slot1); + ADDWORD ( pak, BuildItemHead( thisclient->items[slot1] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot1] ) ); + updateteller += 1; + } + else + { + thisclient->items[slot1].count -= itemcount1; + if(thisclient->items[slot1].count < 1) + ClearItem( thisclient->items[slot1] ); + SendPM(thisclient, "Not enough free slots"); + return true; + } + + //second item + SendPM(thisclient, "Second item now"); + splititem.itemnum = ProductList.Index[material_lookup_number]->itemid[1]; + splititem.itemtype = ProductList.Index[material_lookup_number]->itemtype[1]; + splititem.count = ProductList.Index[material_lookup_number]->amount[1]; + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + SendPM(thisclient, "Item number = %i type = %i", splititem.itemnum, splititem.itemtype); + slot2 = thisclient->GetNewItemSlot( splititem ); + if (slot2 != 0xffff) + { + splititem.count +=(thisclient->items[slot2].count); + thisclient->items[slot2] = splititem; + ADDBYTE ( pak, slot2); + ADDWORD ( pak, BuildItemHead( thisclient->items[slot2] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot2] ) ); + updateteller += 1; + } + else + { + thisclient->items[slot1].count -= itemcount1; + if(thisclient->items[slot1].count < 1) + ClearItem( thisclient->items[slot1] ); + thisclient->items[slot2].count -= itemcount2; + if(thisclient->items[slot2].count < 1) + ClearItem( thisclient->items[slot2] ); + SendPM(thisclient, "Not enough free slots"); + return true; + } + + //third item (if it exists) + if(ProductList.Index[material_lookup_number]->amount[2] !=0 ) + { + SendPM(thisclient, "Third item now"); + splititem.itemnum = ProductList.Index[material_lookup_number]->itemid[2]; + splititem.itemtype = ProductList.Index[material_lookup_number]->itemtype[2]; + splititem.count = ProductList.Index[material_lookup_number]->amount[2]; + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + SendPM(thisclient, "Item number = %i type = %i", splititem.itemnum, splititem.itemtype); + slot3 = thisclient->GetNewItemSlot( splititem ); + if (slot3 != 0xffff) + { + splititem.count +=(thisclient->items[slot3].count); + thisclient->items[slot3] = splititem; + ADDBYTE ( pak, slot3); + ADDWORD ( pak, BuildItemHead( thisclient->items[slot3] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot3] ) ); + updateteller += 1; + } + else + { + thisclient->items[slot1].count -= itemcount1; + if(thisclient->items[slot1].count < 1) + ClearItem( thisclient->items[slot1] ); + thisclient->items[slot2].count -= itemcount2; + if(thisclient->items[slot2].count < 1) + ClearItem( thisclient->items[slot2] ); + thisclient->items[slot3].count -= itemcount3; + if(thisclient->items[slot3].count < 1) + ClearItem( thisclient->items[slot3] ); + SendPM(thisclient, "Not enough free slots"); + return true; + } + } + + //fourth item + if(ProductList.Index[material_lookup_number]->amount[3] !=0 ) + { + splititem.itemnum = ProductList.Index[material_lookup_number]->itemid[3]; + splititem.itemtype = ProductList.Index[material_lookup_number]->itemtype[3]; + splititem.count = ProductList.Index[material_lookup_number]->amount[3]; + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + slot4 = thisclient->GetNewItemSlot( splititem ); + if (slot4 !=0xffff) + { + splititem.count +=(thisclient->items[slot4].count); + thisclient->items[slot4] = splititem; + ADDBYTE ( pak, slot4); + ADDWORD ( pak, BuildItemHead( thisclient->items[slot4] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot4] ) ); + updateteller += 1; + } + else + { + thisclient->items[slot1].count -= itemcount1; + if(thisclient->items[slot1].count < 1) + ClearItem( thisclient->items[slot1] ); + thisclient->items[slot2].count -= itemcount2; + if(thisclient->items[slot2].count < 1) + ClearItem( thisclient->items[slot2] ); + thisclient->items[slot3].count -= itemcount3; + if(thisclient->items[slot3].count < 1) + ClearItem( thisclient->items[slot3] ); + thisclient->items[slot4].count -= itemcount4; + if(thisclient->items[slot4].count < 1) + ClearItem( thisclient->items[slot4] ); + SendPM(thisclient, "Not enough free slots"); + return true; + } + } + + /* + for(unsigned int i=0;iitem[0]) + { + splititem.itemtype = 12; + splititem.itemnum = i; + splititem.count = ProductList.Index[material_lookup_number]->amount[0]; + if(splititem.count > 1) + { + splititem.count/=2; + } + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + unsigned newslot = thisclient->GetNewItemSlot( splititem ); + if (newslot !=0xffff) + { + splititem.count +=(thisclient->items[newslot].count); + thisclient->items[newslot] = splititem; + ADDBYTE ( pak,newslot); + ADDWORD ( pak, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[newslot] ) ); + updateteller+=1; + } + break; + } + } + for (char teller = 1; teller<4;teller++) + { + if (ProductList.Index[material_lookup_number]->amount[teller]>0) + { + splititem.count = ProductList.Index[material_lookup_number]->amount[teller]; + if(splititem.count > 1) + { + splititem.count /= 2; + } + splititem.itemtype = (ProductList.Index[material_lookup_number]->item[teller])/1000; + splititem.itemnum = (ProductList.Index[material_lookup_number]->item[teller])-(1000*splititem.itemtype); + splititem.appraised = false; + splititem.durability = 0; + splititem.gem = 0; + splititem.lifespan = 0; + splititem.refine = 0; + splititem.socketed = 0; + splititem.stats = 0; + unsigned newslot = thisclient->GetNewItemSlot( splititem ); + if (newslot !=0xffff) + { + splititem.count += (thisclient->items[newslot].count); + thisclient->items[newslot] = splititem; + ADDBYTE ( pak,newslot); + ADDWORD ( pak, BuildItemHead( thisclient->items[newslot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[newslot] ) ); + updateteller += 1; + } + } + } + */ + thisclient->items[slot].count -= 1; + ADDBYTE ( pak,slot); + if (thisclient->items[slot].count <=0) + { + ClearItem(thisclient->items[slot]); + ADDWORD ( pak,0); + ADDDWORD ( pak,0); + } + else + { + ADDWORD ( pak, BuildItemHead( thisclient->items[slot] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot] ) ); + } + updateteller += 1; + SETBYTE(pak,1,updateteller); + thisclient->client->SendPacket( &pak ); + thisclient->saveinventory(); + } + break; + case 0x04://NPC Refine + case 0x05://Player Refine + { + BYTE item = GETBYTE((*P),3); //slot number + BYTE material1 = GETBYTE((*P),4); //slot number + int itemtype = thisclient->items[item].itemtype; + int itemnum = thisclient->items[item].itemnum; + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Item: %i Material: %i",item, material1 ); + } + if(!CheckInventorySlot( thisclient, item)) + return false; + if(!CheckInventorySlot( thisclient, material1)) + return false; + //compare material1 with refinetype from equiplist + int refinetype = GServer->EquipList[itemtype].Index[itemnum]->refinetype; + int refineitem = GServer->ProductList.Index[refinetype]->item[0]; + int refinecount = GServer->ProductList.Index[refinetype]->amount[0]; + if( thisclient->Session->codedebug ) + { + SendPM(thisclient, "Refineitem needed for this item: %i",refineitem ); + } + //if(refineitem != material1) + // return false; + if(thisclient->items[item].count<1 || thisclient->items[material1].count < refinecount) + { + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x12 ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; + } + unsigned int nextlevel = ( thisclient->items[item].refine / 16 ) + 1; + if( nextlevel > 9 ) + return true;//if a client were able to try and upgrade an lvl 9 grade item this would probebly lockup this client + + thisclient->items[material1].count -= refinecount; + if ((thisclient->items[material1].count) < 1) + ClearItem( thisclient->items[material1] ); + + + bool success = true; + srand( time(NULL)); + unsigned int prefine = rand()%100; + + //PY ToDo. This whole function needs to be recoded so that we know if it's a bindrune or a talisman + //right now the success chance is Bindrune chance (which is the same as talisman chance in the STB anyway for now) + int refinechance = upgrade[nextlevel].BindRune_Success; + + if( GServer->EquipList[itemtype].Index[itemnum]->raretype != 0 ) + refinechance += GServer->Config.Rare_Refine; + if( prefine > refinechance) + success = false; + + BEGINPACKET( pak, 0x7bc ) + if( success ) + { + thisclient->items[item].refine = nextlevel*16; + thisclient->items[item].durability += nextlevel; + ADDBYTE ( pak, 0x10 );//successful + } + else + { + if(GServer->Config.KillOnFail || nextlevel > 5) + { + ClearItem( thisclient->items[item] ); + } + else + { + thisclient->items[item].refine = (nextlevel-2)*16; + thisclient->items[item].durability -= 2; + } + ADDBYTE ( pak, 0x11 );//Fail + } + {ADDBYTE ( pak, 0x04 );} + ADDBYTE ( pak, material1 ); + ADDWORD ( pak, BuildItemHead( thisclient->items[material1] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[material1] ) ); + ADDBYTE ( pak, item ); + ADDWORD ( pak, BuildItemHead( thisclient->items[item] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[item] ) ); + ADDBYTE ( pak, 0x00 ); + ADDDWORD ( pak, 0x002f0000 ); + ADDDWORD ( pak, 0x00000017 ); + thisclient->client->SendPacket( &pak ); + thisclient->saveinventory(); + } + break; + case 0x06: // Drill + { + BYTE material = GETBYTE((*P), 1); + BYTE item = GETBYTE((*P), 3); + int itemnum = 0; + if(!CheckInventorySlot( thisclient, item)) + return false; + if(!CheckInventorySlot( thisclient, material)) + return false; + //Log(MSG_INFO,"client has %i drills in slot %i",thisclient->items[material].count,material); + if( thisclient->items[material].count <= 0 ) + { + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x12 ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; + } + else + { + //find the item number + itemnum = thisclient->items[material].itemnum; + //Log(MSG_INFO,"Item number = %i",itemnum); + } + //already drilled + if(thisclient->items[item].socketed) + { + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x23 ); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + return true; + } + //if( thisclient->items[item].socketed ) return true;//Show message + thisclient->items[material].count -= 1; + //thisclient->items[item].socketed = true; + if( thisclient->items[material].count <= 0 ) + ClearItem(thisclient->items[material]); + + //LMA: Adding some probability there :) + int success = RandNumber(0,100); + //Log(MSG_INFO,"Drill succes? %i / %i",success,UseList.Index[itemnum]->useeffect[1]); + if(success > UseList.Index[itemnum]->useeffect[1]) + { + //fail + thisclient->items[item].socketed = false; + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x21 ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, material); + ADDDWORD ( pak, BuildItemHead( thisclient->items[material] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[material] ) ); + thisclient->client->SendPacket(&pak); + } + else + { + // success + thisclient->items[item].socketed = true; + BEGINPACKET( pak, 0x7bc ); + ADDBYTE ( pak, 0x20 ); + ADDBYTE ( pak, 0x02 ); + ADDBYTE ( pak, material); + ADDWORD ( pak, BuildItemHead( thisclient->items[material] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[material] ) ); + ADDBYTE ( pak, item ); + ADDWORD ( pak, BuildItemHead( thisclient->items[item] ) ); + ADDDWORD ( pak, BuildItemData( thisclient->items[item] ) ); + thisclient->client->SendPacket(&pak); + } + //Log(MSG_INFO,"client now has %i drills in slot %i",thisclient->items[material].count,material); + } + thisclient->saveinventory(); + break; + default: + Log( MSG_WARNING,"Modified Item unknown action: %i", action); + } + return true; +} + +// Repair hammer +bool CWorldServer::pakRepairHammer( CPlayer* thisclient, CPacket* P ) +{ + Log( MSG_INFO, "Repairhammer. %02x %02x %02x %02x", GETBYTE((*P),0), GETBYTE((*P),1), GETBYTE((*P),2),GETBYTE((*P),3) ); + UINT repairhammer = GETBYTE((*P),0); + UINT repairitem = GETBYTE((*P),2); + if (thisclient->items[repairhammer].count < 1) + return true; + thisclient->items[repairhammer].count -= 1; + if (thisclient->items[repairhammer].count < 1) + ClearItem( thisclient->items[repairhammer] ); + thisclient->items[repairitem].durability -= 2; + thisclient->items[repairitem].lifespan=100; + BEGINPACKET ( pak, 0x7cb ); + ADDBYTE ( pak, 0x02 ); + ADDBYTE ( pak, repairhammer); + ADDWORD ( pak, BuildItemHead( thisclient->items[repairhammer] )); + ADDDWORD ( pak, BuildItemData( thisclient->items[repairhammer] )); + ADDBYTE ( pak, repairitem); + ADDWORD ( pak, BuildItemHead( thisclient->items[repairitem] )); + ADDDWORD ( pak, BuildItemData( thisclient->items[repairitem] )); + thisclient->client->SendPacket( &pak ); + thisclient->saveinventory(); + return true; +} +// Repair npc +bool CWorldServer::pakRepairItem( CPlayer* thisclient, CPacket* P ) +{ + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 0x61: //Ronk + case 0x3d: + case 0x39: + case 0x1c: + case 0x08: + case 0x74: + case 0x89: + case 0x12: // Raffle + case 0x43: //NPC Repair Item / Town + { + BYTE slot = GETBYTE((*P),2); + if(!CheckInventorySlot( thisclient, slot)) return false; + if(thisclient->items[slot].count<1) return true; + //Log( MSG_INFO,"Item socketed?: %i", thisclient->items[slot].socketed); + thisclient->items[slot].lifespan = 100; + //Log( MSG_INFO,"Item socketed?: %i", thisclient->items[slot].socketed); + //Still TODO: find where prices of storage and repair are and add it to the code. + BEGINPACKET( pak, 0x7cd ); + ADDQWORD ( pak, thisclient->CharInfo->Zulies ); + ADDBYTE ( pak, 0x01 ); + ADDBYTE ( pak, slot ); + ADDWORD ( pak, BuildItemHead( thisclient->items[slot] )); + ADDDWORD ( pak, BuildItemData( thisclient->items[slot] )); + ADDBYTE ( pak, 0x00 ); + thisclient->client->SendPacket( &pak ); + thisclient->SetStats( ); + thisclient->saveinventory(); + } + break; + default: + Log( MSG_WARNING,"Repair Item unknown action: %i", action); + break; + } + return true; +} + +// Ride request +bool CWorldServer::pakRideRequest( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Shop->open) + return true; + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 0x01://Ride Request + { + WORD tclientid = GETWORD((*P),1); + WORD oclientid = GETWORD((*P),3); + if( tclientid != thisclient->clientid ) + return true; + CPlayer* otherclient = GetClientByID( oclientid, thisclient->Position->Map ); + if(otherclient == NULL) + return true; + BEGINPACKET( pak, 0x7dd ); + ADDBYTE ( pak, 0x01 ); + ADDWORD ( pak, thisclient->clientid ); + ADDWORD ( pak, oclientid ); + ADDFLOAT ( pak, thisclient->Position->current.x*100 ); + ADDFLOAT ( pak, thisclient->Position->current.y*100 ); + otherclient->client->SendPacket( &pak ); + } + break; + case 0x02://Accept + { + WORD oclientid = GETWORD((*P),1); + WORD tclientid = GETWORD((*P),3); + if( tclientid != thisclient->clientid ) + return true; + CPlayer* otherclient = GetClientByID( oclientid, thisclient->Position->Map ); + if(otherclient == NULL) + return true; + BEGINPACKET( pak, 0x796 ); + ADDWORD ( pak, tclientid ); + ADDFLOAT ( pak, thisclient->Position->current.x*100 ); + ADDFLOAT ( pak, thisclient->Position->current.y*100 ); + ADDWORD ( pak, 0x0000 ); + SendToVisible( &pak, otherclient ); + + RESETPACKET( pak, 0x7dd ); + ADDBYTE ( pak, 0x02 ); + ADDWORD ( pak, oclientid ); + ADDWORD ( pak, tclientid ); + SendToVisible( &pak, otherclient ); + thisclient->Ride->Ride = true; + thisclient->Ride->Drive = false; + thisclient->Ride->charid = otherclient->CharInfo->charid; + otherclient->Ride->Ride = true; + otherclient->Ride->Drive = true; + otherclient->Ride->charid = thisclient->CharInfo->charid; + } + break; + case 0x03://Denied + { + WORD oclientid = GETWORD((*P),1); + WORD tclientid = GETWORD((*P),3); + if( tclientid != thisclient->clientid ) + return true; + CPlayer* otherclient = GetClientByID( oclientid, thisclient->Position->Map ); + if(otherclient == NULL) + return true; + BEGINPACKET( pak, 0x7dd ); + ADDBYTE ( pak, 0x03 ); + ADDWORD ( pak, oclientid ); + ADDWORD ( pak, tclientid ); + otherclient->client->SendPacket( &pak ); + } + break; + } + return true; +} + +bool CWorldServer::pakWeight( CPlayer* thisclient, CPacket* P ) +{ + BYTE weight = GETBYTE((*P),0) & 0xff; + if(weight>110) + thisclient->Status->CanAttack = false; + else + thisclient->Status->CanAttack = true; + if(weight>100) + thisclient->Status->CanRun = false; + else + thisclient->Status->CanRun = true; + thisclient->Stats->Move_Speed = thisclient->GetMoveSpeed( ); + BEGINPACKET( pak, 0x762 ); + ADDWORD ( pak, thisclient->clientid ); + ADDBYTE ( pak, weight ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// Show Monster HP or Player Regeneration +bool CWorldServer::pakShowHeal( CPlayer* thisclient, CPacket* P ) +{ + WORD clientid = GETWORD((*P),0); + CCharacter* character = MapList.Index[thisclient->Position->Map]->GetCharInMap( clientid ); + if(character == NULL) return true; + if(character->IsMonster()) + { + // SET MONSTER HEALTH + BEGINPACKET( pak, 0x79f ); + ADDWORD( pak, character->clientid ); + ADDWORD( pak, character->Stats->HP ); + ADDWORD( pak, 0 ); + thisclient->client->SendPacket( &pak ); + //thisclient->Battle->contatk = false; + } + //thisclient->Battle->target = clientid; + return true; +} + +// Disconnect char +bool CWorldServer::pakCharDSClient( CPlayer* thisclient, CPacket* P ) +{ + if(!thisclient->Session->isLoggedIn) return true; + BYTE action = GETBYTE((*P),0); + switch(action) + { + case 1: + { + unsigned int userid = GETDWORD((*P), 1 ); + CPlayer* otherclient = GetClientByUserID( userid ); + if(otherclient==NULL) + { + Log(MSG_WARNING, "userid '%s' not found online", userid ); + return true; + } + otherclient->client->isActive = false; + + } + break; + default: + break; + } + return true; +} + +// Pack Printscreen +bool CWorldServer::pakPrintscreen( CPlayer* thisclient, CPacket* P ) +{ + BEGINPACKET( pak, 0x7eb ); + ADDWORD ( pak, thisclient->Session->userid ); + ADDWORD ( pak, 0x0302 ); + ADDWORD ( pak, 0x2d17 ); + thisclient->client->SendPacket( &pak ); + return true; +} + +// CharServer Connected +bool CWorldServer::pakCSReady ( CPlayer* thisclient, CPacket* P ) +{ + if(thisclient->Session->isLoggedIn) return false; + if(GETDWORD((*P),0) != Config.WorldPass ) + { + Log( MSG_HACK, "CharServer Tried to connect WorldServer with wrong password "); + return true; + } + + thisclient->Session->isLoggedIn=true; + return true; +} + +// CharServer - Disconnect User +bool CWorldServer::pakCSCharSelect ( CPlayer* thisclient, CPacket* P ) +{ + if(!thisclient->Session->isLoggedIn) return false; + Log( MSG_INFO, "Char server requested client kill" ); + DWORD userid = GETDWORD( (*P), 0 ); + CPlayer* otherclient = GetClientByUserID( userid ); + if(otherclient != NULL) + { + if(otherclient->client==NULL) return true; + otherclient->client->isActive = false; + } + Log( MSG_INFO, "Client killed" ); + return true; +} + +bool CWorldServer::pakItemMall( CPlayer* thisclient, CPacket* P ) +{ + return true; +} diff --git a/Src/World Server/worldserver.cpp b/Src/World Server/worldserver.cpp new file mode 100644 index 0000000..670edd7 --- /dev/null +++ b/Src/World Server/worldserver.cpp @@ -0,0 +1,1075 @@ +/* + Rose Online Server Emulator + Copyright (C) 2006,2007 OSRose Team http://www.osrose.net + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + developed with Main erose/hrose source server + some change from the original eich source +*/ +#include "worldserver.h" + +// Constructor +CWorldServer::CWorldServer( string fn ) +{ + filename = fn; + LoadConfigurations( (const char*)fn.c_str() ); + for(unsigned i=0; i<0xffff; i++) + ClientIDList[i] = 0; + ClientIDIndex = 1; + //Clear or Vector list + PartyList.clear(); + MDropList.clear(); + SkillList.clear(); + FairyList.clear(); + TeleGateList.clear(); + // + CEquip* nullequip = new CEquip; + nullequip->id = 0; + nullequip->itemtype = 0; + nullequip->equiptype = 0; + nullequip->type = 0; + nullequip->price = 0; + nullequip->pricerate = 0; + nullequip->weight = 0; + nullequip->quality = 0; + nullequip->level = 0; + nullequip->material = 0; + nullequip->defense = 0; + nullequip->magicresistence = 0; + nullequip->attackdistance = 0; + nullequip->movespeed = 0; + nullequip->attackpower = 0; + nullequip->attackspeed =0; + nullequip->itemgrade = 0; + for(int i=0;i<3;i++) + { + nullequip->occupation[i] = 0; + nullequip->condition1[i] = 0; + nullequip->condition2[i] = 0; + } + for(int i=0;i<2;i++) + { + nullequip->stat1[i] = 0; + nullequip->stat2[i] = 0; + } + for(int i=0;i<10;i++) + { + EquipList[i].nullequip = nullequip; + for(UINT j=0;j<5000;j++) + EquipList[i].Index[j] = nullequip; + } + CNaturalData* nullnatural = new CNaturalData; + nullnatural->id = 0; + nullnatural->itemtype = 0; + nullnatural->type = 0; + nullnatural->price = 0; + nullnatural->pricerate = 0; + nullnatural->weight = 0; + nullnatural->quality = 0; + nullnatural->pricevalue = 0; + NaturalList.nullnatural = nullnatural; + + CPatData* nullpat = new CPatData; + nullpat->id = 0; + nullpat->itemtype = 0; + nullpat->type = 0; + nullpat->price = 0; + nullpat->pricerate = 0; + nullpat->weight = 0; + nullpat->quality = 0; + nullpat->material = 0; + nullpat->partversion = 0; + nullpat->maxfuel = 0; + nullpat->fuelcons = 0; + nullpat->speed = 0; + nullpat->attackdistance = 0; + nullpat->attackpower = 0; + nullpat->attackspeed = 0; + PatList.nullpat = nullpat; + CCSellData* nullsell = new CCSellData; + nullsell->id = 0; + for(UINT i=0;i<50;i++) + nullsell->item[i] = 0; + SellList.nullsell = nullsell ; + for(UINT i=0;i<1000;i++) + { + NaturalList.Index[i] = nullnatural; + PatList.Index[i] = nullpat; + SellList.Index[i] = nullsell; + } + CMap* nullzone = new CMap( ); + nullzone->id =0; + nullzone->dayperiod = 1; + nullzone->morningtime = 0; + nullzone->daytime = 0; + nullzone->eveningtime = 0; + nullzone->nighttime = 0; + nullzone->allowpvp = 0; + nullzone->allowpat = 0; + nullzone->MapTime = 0; + nullzone->LastUpdate = 0; + nullzone->CurrentTime = 0; + for(UINT i=0;i<300;i++) + MapList.Index[i] = nullzone; + MapList.nullzone = nullzone; + CUseData* nulluse = new CUseData; + nulluse->id = 0; + nulluse->itemtype = 0; + nulluse->restriction = 0; + nulluse->type = 0; + nulluse->price = 0; + nulluse->pricerate = 0; + nulluse->weight = 0; + nulluse->quality = 0; + nulluse->pricevalue = 0; + for(int i=0;i<2;i++) + { + nulluse->usecondition[i] = 0; + nulluse->useeffect[i] = 0; + } + UseList.nulluse = nulluse; + CProductData* nullproduct = new CProductData; + nullproduct->id = 0; + for(UINT i=0;i<50;i++) + { + nullproduct->item[i]; + nullproduct->amount[i]; + } + ProductList.nullproduct = nullproduct; + for(UINT i=0;i<2000;i++) + { + UseList.Index[i] = nulluse; + ProductList.Index[i] = nullproduct; + } + CJemData* nulljem = new CJemData; + nulljem->id = 0; + nulljem->itemtype = 0; + nulljem->type = 0; + nulljem->price = 0; + nulljem->pricerate = 0; + nulljem->weight = 0; + nulljem->quality = 0; + nulljem->material = 0; + for(int i=0;i<2;i++) + { + nulljem->stat1[i] = 0; + nulljem->stat2[i] = 0; + } + JemList.nulljem = nulljem ; + for(UINT i=0;i<4000;i++) + JemList.Index[i] = nulljem; + for(UINT i=0;i<500;i++) + { + StatsList[i].stat[0] = 0; + StatsList[i].stat[1] = 0; + StatsList[i].value[0] = 0; + StatsList[i].value[1] = 0; + } + MapMutex = PTHREAD_MUTEX_INITIALIZER; //fast mutex + SQLMutex = PTHREAD_MUTEX_INITIALIZER; + PlayerMutex = PTHREAD_MUTEX_INITIALIZER; +} + +// Destructor +CWorldServer::~CWorldServer( ) +{ + // free memory + for(UINT i=0;iplayer; + delete ClientList.at(i); + } + delete EquipList[0].nullequip; + delete JemList.nulljem; + delete NaturalList.nullnatural; + delete PatList.nullpat; + delete ProductList.nullproduct; + delete SellList.nullsell; + delete UseList.nulluse; + delete MapList.nullzone; + delete [] GradeList; + delete DB; +} + +// This cleans up our clients mess :P +void CWorldServer::OnClientDisconnect( CClientSocket* thisclient ) +{ + if(thisclient->player == NULL) return; + CPlayer* player = (CPlayer*)thisclient->player; + if(player == NULL) return; + if(!player->Session->isLoggedIn) return; + if(!player->Saved) + { + player->savedata(); + player->SaveBackupStorage( 2 ); // make a backup of the character's storage on exit + player->Session->isLoggedIn = false; + //send packet to change messenger status (offline) + BEGINPACKET( pak, 0x7e1 ); + ADDBYTE ( pak, 0xfa ); + ADDWORD ( pak, player->Session->userid );//CharInfo->charid );CharInfo->charid ); + ADDBYTE ( pak, 0x00 ); + // cryptPacket( (char*)&pak, NULL ); + // send( csock, (char*)&pak, pak.Size, 0 ); + SendISCPacket (&pak); + } + if ( player->Fairy ) + { + GServer->FairyList.at(player->FairyListIndex)->assigned = false; + GServer->FairyList.at(player->FairyListIndex)->LastTime = clock(); + GServer->FairyList.at(player->FairyListIndex)->ListIndex = 0; + GServer->FairyList.at(player->FairyListIndex)->WaitTime = GServer->Config.FairyWait * (rand()% GServer->GetFairyRange(1)+ GServer->GetFairyRange(0)); + player->Fairy = false; + player->FairyListIndex = 0; + GServer->DoFairyStuff(player, 0); + // recalculate FairyMax + int oldFairyMax = GServer->Config.FairyMax; + GServer->Config.FairyMax = (int)ceil((float)GServer->ClientList.size() / 50.0); //(1 fairy more every 50 player) + if( oldFairyMax > GServer->Config.FairyMax ){ + GServer->FairyList.erase( GServer->FairyList.begin() + GServer->FairyList.size() ); + } + } + if(player->Party->party!=NULL) + { + CParty* party = player->Party->party; + BEGINPACKET( pak, 0x7d2 ); + ADDWORD ( pak, 0xff00 ); + ADDDWORD ( pak, player->CharInfo->charid ); + bool pflag = false; + party->RemovePlayer( player ); + if(party->Members.size()>1) + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* othermember = party->Members.at(i); + if(!pflag) + { + ADDDWORD( pak, othermember->CharInfo->charid ); + if(player->Party->IsMaster) + othermember->Party->IsMaster = true; + pflag = true; + } + othermember->client->SendPacket( &pak ); + } + } + else + { + for(UINT i=0;iMembers.size();i++) + { + CPlayer* othermember = party->Members.at(i); + RESETPACKET( pak, 0x7d1 ); + ADDBYTE ( pak, 0x05 ); + ADDWORD ( pak, 0x0000 ); + ADDWORD ( pak, 0x0000 ); + othermember->client->SendPacket( &pak ); + othermember->Party->party = NULL; + othermember->Party->IsMaster = true; + } + RemoveParty( party ); + delete party; + } + } + DB->QExecute("UPDATE accounts SET online=false where id=%u", player->Session->userid ); +} + +// Server is started, lets run our loop :D +void CWorldServer::ServerLoop( ) +{ + fd_set fds; + int activity; + maxfd = 0; + sockaddr_in ClientInfo; + SOCKET NewSocket; + timeval timeout; + maxfd = sock; + OnServerStep(); + + //MySQL Ping (every 10 minutes) + UINT time_last_ping = clock(); + UINT delay_ping = 600000; //10 minutes + UINT time_last_update = clock(); + UINT ServerTimeUpdateDelay = 3600000; //1 hour + do + { + //MySQL Ping + UINT etime = (UINT)round((clock( ) - time_last_ping)); + if(etime >= delay_ping) + { + time_last_ping = clock(); + Ping(); + } + UINT stime = (UINT)round((clock( ) - time_last_update)); + if(stime >= ServerTimeUpdateDelay) + { + ServerRunTime ++; + time_last_update = clock(); + DB->QExecute("UPDATE runtime SET hours = %i WHERE id = %i", ServerRunTime, ThisSession); + } + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + NewSocket = INVALID_SOCKET; + FD_ZERO( &fds ); + pthread_mutex_lock( &PlayerMutex ); + if(!Config.usethreads) + FillFDS( &fds ); + FD_SET( sock, &fds ); + activity = select( maxfd+1, &fds, NULL, NULL, &timeout ); + if ( activity == 0 ) + { + FD_SET( sckISCII, &fds ); + activity = select( maxfd+1, &fds, NULL, NULL, &timeout ); + if ( activity == 0 ) + { + pthread_mutex_unlock( &PlayerMutex ); + #ifdef _WIN32 + Sleep(1); + #else + usleep(1); + #endif + continue; + } + if ( activity < 0 && errno != EINTR ) + { + #ifdef _WIN32 + Log( MSG_ERROR, "Select command failed. Error #%i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Select command failed. Error #%i", errno ); + #endif + isActive = false; + } + if ( FD_ISSET( sckISCII, &fds ) ) + { + int clientinfolen = sizeof( sockaddr_in ); + #ifdef _WIN32 + NewSocket = accept( sckISCII, (sockaddr*)&ClientInfo, (int*)&clientinfolen ); + #else + NewSocket = accept( sckISCII, (sockaddr*)&ClientInfo, (socklen_t*)&clientinfolen ); + #endif + // TODO: check if server is full + if (NewSocket != INVALID_SOCKET) + AddUser( NewSocket, &ClientInfo, true); + else + { + #ifdef _WIN32 + Log( MSG_ERROR, "Error accepting socket: %i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Error accepting socket: %i", errno ); + #endif + } + } + } + if ( activity < 0 && errno != EINTR ) + { + #ifdef _WIN32 + Log( MSG_ERROR, "Select command failed. Error #%i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Select command failed. Error #%i", errno ); + #endif + isActive = false; + } + if ( FD_ISSET( sock, &fds ) ) + { + int clientinfolen = sizeof( sockaddr_in ); + #ifdef _WIN32 + NewSocket = accept( sock, (sockaddr*)&ClientInfo, (int*)&clientinfolen ); + #else + NewSocket = accept( sock, (sockaddr*)&ClientInfo, (socklen_t*)&clientinfolen ); + #endif + // TODO: check if server is full + if (NewSocket != INVALID_SOCKET) + AddUser( NewSocket, &ClientInfo, false); + else + { + #ifdef _WIN32 + Log( MSG_ERROR, "Error accepting socket: %i", WSAGetLastError() ); + #else + Log( MSG_ERROR, "Error accepting socket: %i", errno ); + #endif + } + } + if(!Config.usethreads) + HandleClients( &fds ); + pthread_mutex_unlock( &PlayerMutex ); + #ifdef _WIN32 + Sleep(1); + #else + usleep(1); + #endif + } while( isActive ); +} + +// load the servers encryption +void CWorldServer::LoadEncryption() +{ + GenerateCryptTables( CryptTable, 0x87654321 ); + srand ( (unsigned)time(NULL) ); +} + +// Return a new clientsocket structure +CClientSocket* CWorldServer::CreateClientSocket( ) +{ + //DB->QExecute( "UPDATE channels SET connected=%i where id=%i and type=2", ConnectedClients, Config.ServerID ); + CClientSocket* client = new CClientSocket; + CPlayer* player = new CPlayer( client ); + client->player = (void*) player; + return client; +} + +// Deletes an old clientsocket structure +void CWorldServer::DeleteClientSocket( CClientSocket* thisclient ) +{ + //DB->QExecute( "UPDATE channels SET connected=%i where id=%i and type=2", ConnectedClients, Config.ServerID ); + if(thisclient->player!=NULL) + { + CPlayer* player = (CPlayer*) thisclient->player; + CMap* map = MapList.Index[player->Position->Map]; + pthread_mutex_lock( &MapMutex ); + map->RemovePlayer( player ); + pthread_mutex_unlock( &MapMutex ); + delete player; + } + else delete thisclient; + Log( MSG_INFO, "User Disconnected" ); +} + +void CWorldServer::SendISCServerInfo( ) +{ + BEGINPACKET( pak, 0x400 ); + ADDBYTE( pak,2 ); + ADDDWORD( pak, Config.ServerID ); + ADDDWORD( pak, Config.ParentID ); + ADDDWORD( pak, Config.WorldPort ); + ADDSTRING( pak, Config.WorldIP ); + ADDBYTE( pak, 0 ); + ADDSTRING( pak, Config.ServerName ); + ADDBYTE( pak,0 ); + SendISCPacket( &pak ); +} + +// This function is called just before the server starts +bool CWorldServer::OnServerReady( ) +{ + ServerOnline = true; + GServer = this; + clock_t timer = clock(); + LastUpdateTime = clock(); + ATTK_SPEED_MOD = 120; + HIT_DELAY_MOD = 0; + MOVE_SPEED_MOD = 100000; + + //SendISCServerInfo( ); + + //Load our Server Info + LoadConfig( ); + LoadLTB( ); + LoadSTBData( ); + LoadZoneData( ); + LoadConsItem( ); + LoadSellData( ); + LoadProductItem( ); + LoadPatItem( ); + LoadNaturalItem( ); + LoadJemItem( ); + LoadEquip( ); + LoadItemStats( ); + LoadSkillData( ); + LoadStatusData( ); + LoadBreak( ); + LoadNewDrops( ); + //LoadPYDropsData( ); + LoadSkillBookDropsData( ); + + + LoadNPCData( ); + LoadTeleGateData( ); + LoadRespawnData( ); + if(Config.SpawnType == 1)LoadMonsterSpawn( ); + if(Config.SpawnType == 0)LoadMobGroups( ); + LoadAipData( ); + LoadNPCs( ); + LoadMonsters( ); + LoadUpgrade( ); + LoadQuestData( ); + LoadGrades(); //LMA: loading refine (grades) bonuses. + LoadWayPoints( ); + + //LoadZones(); + CleanConnectedList( ); + Log(MSG_INFO, "Database Loaded " ); + pthread_create( &WorldThread[WORLD_THREAD], &at, WorldProcess, NULL); + pthread_create( &WorldThread[VISUALITY_THREAD], &at, VisibilityProcess, NULL); + pthread_create( &MapThread[0], &at, MapProcess, NULL); + Log( MSG_INFO, "Process Loaded. WorldDelay %i | MapDelay %i | VisualDelay %i",Config.WorldDelay,Config.MapDelay,Config.VisualDelay); + //DB->QExecute( "DELETE FROM channels WHERE id=%u and type=%i", Config.ServerID, Config.ServerType ); +// if(!DB->QExecute("INSERT INTO channels (id,type,name,host,port,lanip,lansubmask,connected,maxconnections,owner) VALUES (%i,%i,'%s','%s',%u,'%s','%s',0,%i,%i)", +// Config.ServerID, Config.ServerType, Config.ServerName, Config.WorldIP, Config.WorldPort, Config.LanIP, Config.LanSubnet, Config.MaxConnections, Config.ParentID)) +// { +// Log(MSG_WARNING, "Error accessing to database, the other server will not connect to WorldServer" ); +// } + DB->QExecute( "DELETE FROM channels WHERE id=%u", Config.ServerID); + if(!DB->QExecute("INSERT INTO channels (id,name,host,port,connected,maxconnections,owner) VALUES (%i,'%s','%s',%u,0,%i,%i)", + Config.ServerID, Config.ServerName, Config.WorldIP, Config.WorldPort, Config.MaxConnections, Config.ParentID)) + { + Log(MSG_WARNING, "Error accessing to database, the other server will not connect to WorldServer" ); + } + //DB->QFree( ); + + // set timestamp for startup + time_t rtime; + time(&rtime); + char *timestamp = ctime(&rtime); + timestamp[ strlen(timestamp)-1 ] = ' '; + int a1 = 0; + DB->QExecute("INSERT INTO runtime (starttime,hours) VALUES ('%s',%i)",timestamp, a1 ); + MYSQL_ROW row; + MYSQL_RES *result = DB->QStore("SELECT id FROM runtime"); + while(row = mysql_fetch_row(result)) + { + ThisSession = atoi(row[0]); + } + DB->QFree( ); + ServerStartTime = clock(); + ServerRunTime = 0; + + /* + MYSQL_ROW row; + bool pflag = false; + //Get IP and Port from Charserver + + MYSQL_RES *result = DB->QStore( "SELECT host,port,lanip FROM channels WHERE id=%u", Config.ParentID ); + if(result==NULL) return false; + if (mysql_num_rows( result ) == 1) + { + row = mysql_fetch_row( result ); + switch(Config.Connection) + { + case 0://wanip + Config.CharIP = row[0]; + break; + case 1://lanip + Config.CharIP = row[2]; + break; + default://localhost + Config.CharIP = "127.0.0.1"; + break; + } + Config.CharPort = atoi(row[1]); + pflag = true; + } + DB->QFree( ); + if(pflag) + {*/ + Log( MSG_INFO, "Initializing Charserver connection" ); + // Connect To CharServer + csock = socket( AF_INET, SOCK_STREAM, 0 ); + if (csock == INVALID_SOCKET) + { + Log( MSG_WARNING, "Could not access login server" ); + } + struct sockaddr_in ain; + ain.sin_family = AF_INET; + ain.sin_addr.s_addr = inet_addr( Config.CharIP ); + ain.sin_port = htons( Config.CharPort ); + if ( connect( csock, (SOCKADDR*) &ain, sizeof(ain) ) == SOCKET_ERROR ) + Log( MSG_WARNING, "Could not access charserver" ); + else + Log( MSG_INFO, "Connected to charserver" ); + + //say hello to char server + BEGINPACKET( pak, 0x500 ); + ADDDWORD ( pak, Config.CharPass ); + ADDDWORD ( pak, Config.ServerID ); + ADDWORD ( pak, Config.WorldsPort ); + SendISCPacket( &pak ); +/* CPacket *P=&pak; + unsigned char* Buffer = (unsigned char*)P; + unsigned Size = P->Size; + EncryptBuffer( CryptTable, Buffer ); + send( csock, (char*)Buffer, Size, 0 ); + //EncryptBuffer(CryptTable,pak); + //cryptPacket( (char*)&pak, NULL ); + //send( csock, (char*)&pak, pak.Size, 0 ); + } + */ +/////////////////////////////////////////////////////////////////////////////////// + float loadtime = (float)( clock() - timer ) / CLOCKS_PER_SEC; + Log( MSG_INFO, "Server took %.4f seconds to load", loadtime ); + + return true; +} + +// Send Levelup To charserver +bool CWorldServer::SendLevelUPtoChar(CPlayer *thisclient) +{ + + BEGINPACKET( pak, 0x79e ); + ADDWORD( pak, thisclient->CharInfo->charid );//Session->userid );//CharInfo->charid ); + ADDWORD( pak, thisclient->Stats->Level ); + //cryptPacket( (char*)&pak, NULL ); + //send( csock, (char*)&pak, pak.Size, 0 ); + SendISCPacket( &pak ); + + RESETPACKET( pak, 0x79e ); + ADDWORD( pak, thisclient->CharInfo->charid ); + ADDWORD( pak, thisclient->Stats->Level ); + thisclient->client->SendPacket(&pak); + return true; +} + +bool CWorldServer::pakGiveItem( CPlayer* thisclient, unsigned char itemtype, unsigned short itemid, unsigned short count ) +{ + char itemtypes[15] = {4,0,0,0,0,0,0,0,0,0,1,2,2,4,3}; + CPacket pakout; + CItem thisitem; + + thisitem.Clear( ); + thisitem.itemtype = itemtype; + thisitem.itemnum = itemid; + thisitem.count = count; + if( itemtypes[thisitem.itemtype] == 0 ) + { + thisitem.appraised = true; + thisitem.lifespan = 100; + thisitem.gem = 0; + thisitem.durability = STB_ITEM[thisitem.itemtype-1].rows[thisitem.itemnum][29]; + } + + pakout.StartPacket( 0x71f ); + pakout.AddByte( 0 ); + while( thisitem.count > 0 ) + { + unsigned char thisslot = thisclient->AddItem( thisitem ); + if( thisslot == 0xff ) break; + pakout.AddByte( thisslot ); + pakout.AddWord( thisclient->items[thisslot].GetPakHeader( ) ); + pakout.AddDWord( thisclient->items[thisslot].GetPakData( ) ); + pakout.Buffer[0]++; + } + thisclient->client->SendPacket( &pakout ); + + return true; +} + +bool CWorldServer::pakGiveExp( CPlayer* thisclient, unsigned exp ) +{ + CPacket pakout; + thisclient->CharInfo->Exp+= exp; + if(!thisclient->CheckPlayerLevelUP( )) + { + pakout.StartPacket( 0x79b ); + pakout.AddDWord( thisclient->CharInfo->Exp ); + pakout.AddWord( thisclient->CharInfo->stamina ); + pakout.AddWord( 0 ); + thisclient->client->SendPacket( &pakout ); + } + return true; +} + +bool CWorldServer::pakGiveMoney( CPlayer* thisclient, unsigned amount ) +{ + CPacket pakout; + thisclient->CharInfo->Zulies += amount; + pakout.StartPacket( 0x71e ); + pakout.AddQWord( thisclient->CharInfo->Zulies ); + thisclient->client->SendPacket( &pakout ); + return true; +} + +void CWorldServer::DePopulateZone( CPlayer* thisclient, int zonex, int zoney )//compile warning fix by Fluorescent abs -> fabs +{ + CPacket pakout, pakout2; + if( !thisclient->Session->inGame || fabs( thisclient->Position->current.x - zonex ) > 1 || fabs( thisclient->Position->current.y - zoney ) > 1 ) + { + pakout.StartPacket( 0x794 ); + + for( unsigned i=0; iSession->inGame || thisclient==otherclient ) continue; + if( otherclient->Position->current.x == zonex && otherclient->Position->current.y == zoney && otherclient->Position->Map == thisclient->Position->Map ) + { + pakout2.StartPacket( 0x794 ); + pakout2.AddWord( thisclient->clientid ); + otherclient->client->SendPacket( &pakout2 ); + + pakout.AddWord( otherclient->clientid ); + } + } + + for( unsigned i=0; iMonsterSpawnList.size();j++) + { + CMonster* thismonster = thismap->MonsterList.at(j); + if( thismonster->Position->current.x == zonex && thismonster->Position->current.y == zoney && thismonster->Position->Map == thisclient->Position->Map ) + pakout.AddWord( thismonster->clientid ); + + for( unsigned k=0; kNPCList.size( ); k++ ) + { + CNPC* thisnpc = thismap->NPCList[k]; + if( thisnpc->pos.x == zonex && thisnpc->pos.y == zoney && thisnpc->posMap == thisclient->Position->Map ) + pakout.AddWord( thisnpc->clientid ); + } + + for( unsigned k=0; kDropsList.size( ); k++ ) + { + CDrop* thisdrop = thismap->DropsList[k]; + if( thisdrop->pos.x == zonex && thisdrop->pos.y == zoney && thisdrop->posMap == thisclient->Position->Map ) + pakout.AddWord( thisdrop->clientid ); + } + } + } + + if( pakout.Size > 6 ) thisclient->client->SendPacket( &pakout ); + } +} + +bool CWorldServer::pakTeleport( CPlayer* thisclient, unsigned short map, float x, float y ) +{ + + fPoint coord; + coord.x = x/100; + coord.y = y/100; + MapList.Index[map]->TeleportPlayer( thisclient, coord, false ); + /* + CPacket pakout; + + thisclient->Session->inGame = false; + + GServer->MapList.Index[thisclient->Position->Map]->RemovePlayer( thisclient, false ); + + ClearClientID( thisclient->clientid ); + thisclient->Position->Map = map; + thisclient->Position->destiny.x = x; + thisclient->Position->destiny.y = y; + thisclient->Position->current = thisclient->Position->destiny; + thisclient->Status->Stance= 0; + + pakout.StartPacket( 0x7a8 ); + pakout.AddWord( thisclient->clientid ); + pakout.AddWord( thisclient->Position->Map ); + pakout.AddFloat( thisclient->Position->current.x ); + pakout.AddFloat( thisclient->Position->current.y ); + pakout.AddWord( 1 ); + thisclient->client->SendPacket( &pakout );*/ + return true; +} + +// disconect all the clients +void CWorldServer::DisconnectAll() +{ + for(UINT i=0;iplayer; + if(otherclient->Session->isLoggedIn) + { + otherclient->savedata( ); + otherclient->Saved = true; + otherclient->client->isActive = false; + } + } +} + +// Load Server configuration +void CWorldServer::LoadConfigurations( const char* file ) +{ + //Database + Config.SQLServer.pcServer = ConfigGetString ( file, "mysql_host", "localhost" ); + Config.SQLServer.pcDatabase = ConfigGetString ( file, "mysql_database", "roseon_beta" ); + Config.SQLServer.pcUserName = ConfigGetString ( file, "mysql_user", "root" ); + Config.SQLServer.pcPassword = ConfigGetString ( file, "mysql_pass", "" ); + Config.SQLServer.pcPort = ConfigGetInt ( file, "mysql_port", 3306 ); + //Server + Config.ServerID = ConfigGetInt ( file, "serverid", 1 ); + Config.ServerType = 2; // World always = 2 + Config.WorldPort = ConfigGetInt ( file, "serverport", 29200 ); + Config.WorldIP = ConfigGetString ( file, "serverip", "127.0.0.1" ); + Config.ParentID = ConfigGetInt ( file, "parentid", 1 ); + Config.ServerName = ConfigGetString ( file, "servername", "Channel" ); + Config.MaxConnections = ConfigGetInt ( file, "maxconnections", 100 ); + //Config.Connection = ConfigGetInt ( file, "connection", 0 ); + //Config.LanIP = ConfigGetString ( file, "lanip", "192.168.0.1" ); + //Config.LanSubnet = ConfigGetString ( file, "lansubmask", "192.168.0" ); + Config.CharIP = ConfigGetString ( file, "charip", "127.0.0.1" ); + Config.CharPort = ConfigGetInt ( file, "charport", 29110 ); + Config.WorldsPort = ConfigGetInt ( file, "WorldsPort", 29210 ); + //World + Config.MinimumAccessLevel = ConfigGetInt ( file, "minimal_access_level", 100 ); + Config.usethreads = ConfigGetInt ( file, "usethreads", 0 )==0?false:true; + //Config.EXP_RATE = ConfigGetInt ( file, "exp_rate", 10 ); + //Config.DROP_RATE = ConfigGetInt ( file, "drop_rate", 1 ); + Config.DROP_TYPE = ConfigGetInt ( file, "drop_type", 2 ); + //Config.ZULY_RATE = ConfigGetInt ( file, "zuly_rate", 1 ); + Config.WELCOME_MSG = ConfigGetString ( file, "welcome_msg", "Welcome to Rose Online" ); + Config.AUTOSAVE = ConfigGetInt ( file, "autosave", 0 ); + Config.SAVETIME = ConfigGetInt ( file, "savetime", 3600 ); + Config.MapDelay = ConfigGetInt ( file, "mapdelay", 10 ); + Config.WorldDelay = ConfigGetInt ( file, "worlddelay", 200 ); + Config.VisualDelay = ConfigGetInt ( file, "visualdelay", 500 ); + Config.Partygap = ConfigGetInt ( file, "partygap", 10 ); + Config.MaxStat = ConfigGetInt ( file, "maxstat", 254 ); + Config.FairyMode = ConfigGetInt ( file, "fairy", 1 ); + Config.FairyStay = ConfigGetInt ( file, "fairystay", 20 ); + Config.FairyWait = ConfigGetInt ( file, "fairywait", 15 ); + Config.FairyMax = ConfigGetInt ( file, "fairymax", 0); + Config.FairyTestMode = ConfigGetInt ( file, "fairytestmode", 1); + //Config.PlayerDmg = ConfigGetInt ( file, "playerdmg", 120); + //Config.MonsterDmg = ConfigGetInt ( file, "monsterdmg", 100); + Config.Cfmode = ConfigGetInt ( file, "cfmode", 0); + + //Password + Config.LoginPass = ConfigGetInt ( file, "loginpass", 123456 ); + Config.CharPass = ConfigGetInt ( file, "charpass", 123456 ); + Config.WorldPass = ConfigGetInt ( file, "worldpass", 123456 ); + if(Config.AUTOSAVE==1) + Log( MSG_INFO, "Autosaving Every %i minutes", Config.SAVETIME/60 ); + Config.DeathDelay = 1000; //delay time in miliseconds. + + LoadCommandLevels(); +} + +// Load commands from commands.ini [by Paul_T] +void CWorldServer::LoadCommandLevels( void ) +{ + Config.Command_Ani = ConfigGetInt ( "commands.ini", "ani", 299 ); + Config.Command_Ann = ConfigGetInt ( "commands.ini", "ann", 299 ); + Config.Command_Ban = ConfigGetInt ( "commands.ini", "ban", 299 ); + Config.Command_Cha = ConfigGetInt ( "commands.ini", "cha", 299 ); + Config.Command_ChangeFairyWait = ConfigGetInt ( "commands.ini", "changefairywait", 299 ); + Config.Command_ChangeFairyStay = ConfigGetInt ( "commands.ini", "changefairystay", 299 ); + Config.Command_ChangeFairyTestMode = ConfigGetInt ( "commands.ini", "changefairytestmode", 299 ); + Config.Command_Class = ConfigGetInt ( "commands.ini", "class", 299 ); + Config.Command_Convert = ConfigGetInt ( "commands.ini", "convert", 299 ); + Config.Command_Cfmode = ConfigGetInt ( "commands.ini", "cfmode", 299 ); + Config.Command_DelSpawn = ConfigGetInt ( "commands.ini", "delspawn", 299 ); + Config.Command_DQuest = ConfigGetInt ( "commands.ini", "dquest", 299 ); + Config.Command_Drop = ConfigGetInt ( "commands.ini", "drop", 299 ); + Config.Command_DSpawn = ConfigGetInt ( "commands.ini", "dspawn", 299 ); + Config.Command_ESpawn = ConfigGetInt ( "commands.ini", "espawn", 299 ); + Config.Command_Exp = ConfigGetInt ( "commands.ini", "exp", 299 ); + Config.Command_Face = ConfigGetInt ( "commands.ini", "face", 299 ); + Config.Command_Give2 = ConfigGetInt ( "commands.ini", "give2", 299 ); + Config.Command_GiveFairy = ConfigGetInt ( "commands.ini", "givefairy", 299 ); + Config.Command_GiveZuly = ConfigGetInt ( "commands.ini", "givezuly", 299 ); + Config.Command_Go = ConfigGetInt ( "commands.ini", "go", 299 ); + Config.Command_Goto = ConfigGetInt ( "commands.ini", "goto", 299 ); + Config.Command_GoToMap = ConfigGetInt ( "commands.ini", "gotomap", 299 ); + Config.Command_Hair = ConfigGetInt ( "commands.ini", "hair", 299 ); + Config.Command_Heal = ConfigGetInt ( "commands.ini", "heal", 299 ); + Config.Command_Here = ConfigGetInt ( "commands.ini", "here", 299 ); + Config.Command_Hide = ConfigGetInt ( "commands.ini", "hide", 299 ); + Config.Command_Info = ConfigGetInt ( "commands.ini", "info", 299 ); + Config.Command_IQuest = ConfigGetInt ( "commands.ini", "iquest", 299 ); + Config.Command_Item = ConfigGetInt ( "commands.ini", "item", 299 ); + Config.Command_Job = ConfigGetInt ( "commands.ini", "job", 299 ); + Config.Command_Kick = ConfigGetInt ( "commands.ini", "kick", 299 ); + Config.Command_KillInRange = ConfigGetInt ( "commands.ini", "killinrange", 299 ); + Config.Command_Level = ConfigGetInt ( "commands.ini", "level", 299 ); + Config.Command_LevelUp = ConfigGetInt ( "commands.ini", "levelup", 299 ); + Config.Command_ManageFairy = ConfigGetInt ( "commands.ini", "managefairy", 299 ); + Config.Command_Mdmg = ConfigGetInt ( "commands.ini", "mdmg", 299 ); + Config.Command_Mon = ConfigGetInt ( "commands.ini", "mon", 299 ); + Config.Command_Monster = ConfigGetInt ( "commands.ini", "monster", 299 ); + Config.Command_Move = ConfigGetInt ( "commands.ini", "move", 299 ); + Config.Command_Moveto = ConfigGetInt ( "commands.ini", "moveto", 299 ); + Config.Command_Mute = ConfigGetInt ( "commands.ini", "mute", 299 ); + Config.Command_Npc = ConfigGetInt ( "commands.ini", "npc", 299 ); + Config.Command_Pak = ConfigGetInt ( "commands.ini", "pak", 299 ); + Config.Command_Pak2 = ConfigGetInt ( "commands.ini", "pak2", 299 ); + Config.Command_Pakm = ConfigGetInt ( "commands.ini", "pakm", 299 ); + Config.Command_Partylvl = ConfigGetInt ( "commands.ini", "partylvl", 299); + Config.Command_PlayerInfo = ConfigGetInt ( "commands.ini", "playerinfo", 299 ); + Config.Command_Pdmg = ConfigGetInt ( "commands.ini", "pdmg", 299 ); + Config.Command_Pvp = ConfigGetInt ( "commands.ini", "pvp", 299 ); + Config.Command_Rate = ConfigGetInt ( "commands.ini", "rate", 299 ); + Config.Command_Reload = ConfigGetInt ( "commands.ini", "reload", 299 ); + Config.Command_ReloadQuest = ConfigGetInt ( "commands.ini", "reloadquest", 299 ); + Config.Command_Rules = ConfigGetInt ( "commands.ini", "rules", 99 ); + Config.Command_Save = ConfigGetInt ( "commands.ini", "save", 299 ); + Config.Command_ServerInfo = ConfigGetInt ( "commands.ini", "serverinfo", 299 ); + Config.Command_Set = ConfigGetInt ( "commands.ini", "set", 299 ); + Config.Command_Settime = ConfigGetInt ( "commands.ini", "settime", 299 ); + Config.Command_ShopType = ConfigGetInt ( "commands.ini", "shoptype", 299 ); + Config.Command_Shutdown = ConfigGetInt ( "commands.ini", "shutdown", 299 ); + Config.Command_SSpawn = ConfigGetInt ( "commands.ini", "sspawn", 299 ); + Config.Command_Stat = ConfigGetInt ( "commands.ini", "stat", 299 ); + Config.Command_Summon = ConfigGetInt ( "commands.ini", "summon", 299 ); + Config.Command_TargetInfo = ConfigGetInt ( "commands.ini", "targetinfo", 299 ); + Config.Command_Tele = ConfigGetInt ( "commands.ini", "tele", 299 ); + Config.Command_TeleToMe = ConfigGetInt ( "commands.ini", "teletome", 299 ); + Config.Command_Transx = ConfigGetInt ( "commands.ini", "transx", 299 ); + Config.Command_Who = ConfigGetInt ( "commands.ini", "who", 299 ); + Config.Command_Who2 = ConfigGetInt ( "commands.ini", "who2", 299 ); + Config.Command_Broadcast = ConfigGetInt ( "commands.ini", "broadcast", 299 ); + Config.Command_GlobalTime = ConfigGetInt ( "commands.ini", "globaldelay", 30 ); + Config.Command_GlobalPrefix = ConfigGetString ( "commands.ini", "globalprefix", "[Broadcast]" ); + +} +bool CWorldServer::Ping( ) +{ + return DB->Ping( ); +} + +void CWorldServer::ReceivedISCPacket( CPacket* pak ) +{ + switch( pak->Command ) + { + case 0x100: + break; + case 0x101: + SendISCServerInfo( ); + break; + case 0x300: + break; + case 0x301: + break; + case 0x400: + break; + case 0x401: + break; + case 0x500: + break; + case 0x501: + break; + case 0x502: + { + for( unsigned i=0; iSession->inGame ) + { + BEGINPACKET( pakout, 0x702 ); + ADDSTRING( pakout, (char*)&pak->Buffer[0] ); + ADDBYTE( pakout, 0 ); + otherclient->client->SendPacket( &pakout ); + } + } + break; + } + case 0x503: + exit( 0 ); + break; + case 0x580: + break; + case 0x581: + break; + default: + Log( MSG_DEBUG, "UNKNOWN ISC PACKET - 0x%x %04x %04x", pak->Command, pak->Size, pak->Unused ); + } +} + +// Incoming packet +bool CWorldServer::OnReceivePacket( CClientSocket* thisclient, CPacket *P ) +{//Log( MSG_INFO, "(SID:%i) Received packet. Command:%04x Size:%04x", thisclient->sock, P->Command, P->Size ); + switch( P->Command ) + { + case 0x0500: return pakCSReady ( (CPlayer*)thisclient->player, P ); + case 0x0502: return pakCharDSClient ( (CPlayer*)thisclient->player, P ); + case 0x0505: return pakCSCharSelect ( (CPlayer*)thisclient->player, P ); + case 0x0700: return pakPing ( (CPlayer*)thisclient->player, P ); + case 0x0707: return pakExit ( (CPlayer*)thisclient->player, P ); + case 0x070b: return pakDoIdentify ( (CPlayer*)thisclient->player, P ); + case 0x071c: return pakCharSelect ( (CPlayer*)thisclient->player, P ); + case 0x0730: return pakQuestTrigger ( (CPlayer*)thisclient->player, P ); + //case 0x0753: return pakRespawned ( (CPlayer*)thisclient->player, P ); + case 0x0753: return pakDoID ( (CPlayer*)thisclient->player, P ); + case 0x0755: return pakUserDied ( (CPlayer*)thisclient->player, P ); + case 0x0756: return pakSaveTown ( (CPlayer*)thisclient->player, P ); + case 0x0762: return pakWeight ( (CPlayer*)thisclient->player, P ); + case 0x0771: return pakStopChar ( (CPlayer*)thisclient->player, P ); + case 0x0781: return pakDoEmote ( (CPlayer*)thisclient->player, P ); + case 0x0782: return pakChangeStance ( (CPlayer*)thisclient->player, P ); + case 0x0783: return pakNormalChat ( (CPlayer*)thisclient->player, P ); + case 0x0784: return pakWhisper ( (CPlayer*)thisclient->player, P ); + case 0x0785: return pakShout ( (CPlayer*)thisclient->player, P ); + case 0x0786: return pakPartyChat ( (CPlayer*)thisclient->player, P ); + case 0x0798: return pakStartAttack ( (CPlayer*)thisclient->player, P ); + case 0x079f: return pakShowHeal ( (CPlayer*)thisclient->player, P ); + case 0x079a: return pakMoveChar ( (CPlayer*)thisclient->player, P ); + case 0x07a1: return pakNPCBuy ( (CPlayer*)thisclient->player, P ); + case 0x07a3: return pakUseItem ( (CPlayer*)thisclient->player, P ); + case 0x07a4: return pakDoDrop ( (CPlayer*)thisclient->player, P ); + case 0x07a5: return pakChangeEquip ( (CPlayer*)thisclient->player, P ); + case 0x07a7: return pakPickDrop ( (CPlayer*)thisclient->player, P ); + case 0x07a8: return pakGate ( (CPlayer*)thisclient->player, P ); + case 0x07a9: return pakAddStats ( (CPlayer*)thisclient->player, P ); + case 0x07aa: return pakMoveSkill ( (CPlayer*)thisclient->player, P ); + case 0x07ab: return pakEquipABC ( (CPlayer*)thisclient->player, P ); + case 0x07af: return pakCraft ( (CPlayer*)thisclient->player, P ); + case 0x07b1: return pakLevelUpSkill ( (CPlayer*)thisclient->player, P ); + case 0x07b2: return pakSkillSelf ( (CPlayer*)thisclient->player, P ); + case 0x07b3: return pakStartSkill ( (CPlayer*)thisclient->player, P ); + case 0x07b4: return pakSkillAOE ( (CPlayer*)thisclient->player, P ); + case 0x07c0: return pakTradeAction ( (CPlayer*)thisclient->player, P ); + case 0x07c1: return pakTradeAdd ( (CPlayer*)thisclient->player, P ); + case 0x07ad: return pakStorage ( (CPlayer*)thisclient->player, P ); + case 0x07ae: return pakChangeStorage ( (CPlayer*)thisclient->player, P ); + case 0x07ba: return pakidentify ( (CPlayer*)thisclient->player, P ); + case 0x07bc: return pakModifiedItem ( (CPlayer*)thisclient->player, P ); + case 0x07bf: return true;//add to wishlist + case 0x07c2: return pakOpenShop ( (CPlayer*)thisclient->player, P ); + case 0x07c3: return pakCloseShop ( (CPlayer*)thisclient->player, P ); + case 0x07c4: return pakShowShop ( (CPlayer*)thisclient->player, P ); + case 0x07c5: return pakBuyShop ( (CPlayer*)thisclient->player, P ); + case 0x07c6: return pakSellShop ( (CPlayer*)thisclient->player, P ); + case 0x07ca: return pakChangeCart ( (CPlayer*)thisclient->player, P ); + case 0x07cb: return pakRepairHammer ( (CPlayer*)thisclient->player, P ); + case 0x07cd: return pakRepairItem ( (CPlayer*)thisclient->player, P ); + case 0x07d0: return pakPartyActions ( (CPlayer*)thisclient->player, P ); + case 0x07d1: return pakPartyManager ( (CPlayer*)thisclient->player, P ); + case 0x07d7: return pakPartyOption ( (CPlayer*)thisclient->player, P ); + case 0x07d8: return pakModifiedItemDone ( (CPlayer*)thisclient->player, P ); + case 0x07d9: return pakItemMall ( (CPlayer*)thisclient->player, P ); + case 0x07da: return pakStoreZuly ( (CPlayer*)thisclient->player, P ); + case 0x07dd: return pakRideRequest ( (CPlayer*)thisclient->player, P ); + case 0x07e0: return pakCreateClan ( (CPlayer*)thisclient->player, P ); + case 0x07e1: return pakClanManager ( (CPlayer*)thisclient->player, P ); + case 0x07eb: return pakPrintscreen ( (CPlayer*)thisclient->player, P ); + case 0x0808: return pakGameGuard ( (CPlayer*)thisclient->player, P ); + default: + Log( MSG_WARNING, "(SID:%i) Received unknown packet. Command:%04x Size:%04x", thisclient->sock, P->Command, P->Size ); + break; + } + return true; +} +// -----------------------------------------------------------------------------------------