added source folders

This commit is contained in:
purpleyouko committed 2025-08-11 11:09:09 -05:00
1 parent f23521f720
commit cfdc19df45
145 files changed
+54574

No files matched your search

+55
View File
@@ -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;
}
}
};
+120
View File
@@ -0,0 +1,120 @@
//Made by Caali
#define __CARRAY_HPP__
/* Includes */
#include <memory.h>
/* 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 <typename T>
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<size(); i++ ){
arr[i] = otherarr.arr[i];
}
}
inline bool operator == ( const CRoseArray& otherarr )
{
if( size() == otherarr.size() )
{
if( arr == NULL && otherarr.arr == NULL )
{
return true;
}
else
{
for( CARRAY_SIZE i = 0; i<size(); i++ )
{
if( arr[i] != otherarr.arr[i] ) return false;
}
return true;
}
}
return false;
}
inline bool operator != ( const CRoseArray& otherarr )
{
return !(this == otherarr);
}
};
+941
View File
@@ -0,0 +1,941 @@
//Made by Caali
#define __CROSEFILE_HPP__
/* Includes */
#include <stdio.h>
#include <stdlib.h>
#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<CFILE_CHAR>* 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<CFILE_CHAR>* result = new CRoseArray<CFILE_CHAR>( 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;i<result->size(); i++ ) res[i] = 0x00;
memcpy( res, result->arr, (size_t)(result->size()*sizeof(CFILE_CHAR)) );
for( CFILE_UINT i = 0;i<CFILE_MAXLINELENGHT; i++ )
{
if( (CFILE_UCHAR)res[i] == 0x0D || (CFILE_UCHAR)res[i] == 0x0A )
{
if( (CFILE_ULONG)Tell( ) >= 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
@<param>
typename
@result
typename: the value being read
*/
template<typename T> T Get( )
{
T result;
Read( &result, sizeof( T ), 1 );
return result;
}
/*template<typename T> 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<CFILE_CHAR>* 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
@<param>
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<typename T> 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);
}
};
+82
View File
@@ -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<dword>();
_Rows = fh->Get<dword>();
_Columns = fh->Get<dword>();
_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<word>();
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;
};
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+242
View File
@@ -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
{
};
+771
View File
@@ -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<word>(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<int>(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<int>(curAbil->btOp, (int*)&tempValue, curAbil->iValue))
if(!OperateValues<int>(curAbil->btOp, &tempValue, curAbil->iValue))
return QUEST_FAILURE;
break;
case sUnion: // Unions aren't implemented? - Drakia
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue))
return QUEST_FAILURE;
break;
case sStrength:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue))
return QUEST_FAILURE;
break;
case sDexterity:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue))
return QUEST_FAILURE;
break;
case sIntelligence:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue))
return QUEST_FAILURE;
break;
case sConcentration:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue))
return QUEST_FAILURE;
break;
case sCharm:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue))
return QUEST_FAILURE;
break;
case sSensibility:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue))
return QUEST_FAILURE;
break;
case sLevel:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue))
return QUEST_FAILURE;
break;
case sStatPoints:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue))
return QUEST_FAILURE;
break;
case sMoney:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue))
return QUEST_FAILURE;
break;
case sEXPRate:
if(!OperateValues<int>(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<int>(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<int>(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<int>(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<dword>(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<CNpc*>(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<CMonster*>(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<CNpc*>(entity);
std::map<dword, CNpc*>::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;
}
@@ -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<word>(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<int>(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<int>(curAbil->btOp, (int*)&tempValue, curAbil->iValue))
if(!OperateValues<int>(curAbil->btOp, &tempValue, curAbil->iValue))
return QUEST_FAILURE;
break;
case sUnion: // Unions aren't implemented? - Drakia
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue))
return QUEST_FAILURE;
break;
case sStrength:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue))
return QUEST_FAILURE;
break;
case sDexterity:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue))
return QUEST_FAILURE;
break;
case sIntelligence:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue))
return QUEST_FAILURE;
break;
case sConcentration:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue))
return QUEST_FAILURE;
break;
case sCharm:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue))
return QUEST_FAILURE;
break;
case sSensibility:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue))
return QUEST_FAILURE;
break;
case sLevel:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue))
return QUEST_FAILURE;
break;
case sStatPoints:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue))
return QUEST_FAILURE;
break;
case sMoney:
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue))
return QUEST_FAILURE;
break;
case sEXPRate:
if(!OperateValues<int>(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<int>(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<int>(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<int>(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<dword>(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<CNpc*>(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<CMonster*>(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<CNpc*>(entity);
std::map<dword, CNpc*>::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;
}
+239
View File
@@ -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;
};
+98
View File
@@ -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 <class T> 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"
+157
View File
@@ -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<dword>();
fh->Seek(fh->Get<word>(), SEEK_CUR);
for(dword i = 0; i < BlockCount; i++)
{
dword RecordCount = fh->Get<dword>();
fh->Seek(fh->Get<word>(), SEEK_CUR);
for(dword j = 0; j < RecordCount; j++)
{
CQuestTrigger* trigger = new CQuestTrigger();
trigger->id=((index*0x10000)+(i*0x100)+j);
trigger->CheckNext = fh->Get<byte>();
trigger->ConditionCount = fh->Get<dword>();
trigger->ActionCount = fh->Get<dword>();
dword len = fh->Get<word>();
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<int>();
data->opcode = fh->Get<int>();
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<int>();
data->opcode = fh->Get<int>() - 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;
}
+17
View File
@@ -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;
}
+20
View File
@@ -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);