summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpanikon <panikon@zoho.com>2014-05-06 21:14:46 -0300
committerpanikon <panikon@zoho.com>2014-05-06 21:14:46 -0300
commit9a425c11b61fb6f4e299013c7d8d9841129b8f45 (patch)
treeb8e8bc56c6e51679c23fe2de0e268f49b29f7288 /src
parent9cf6b362a0d5e2f52c017d747c0fa8f69a1a273f (diff)
downloadhercules-9a425c11b61fb6f4e299013c7d8d9841129b8f45.tar.gz
hercules-9a425c11b61fb6f4e299013c7d8d9841129b8f45.tar.bz2
hercules-9a425c11b61fb6f4e299013c7d8d9841129b8f45.tar.xz
hercules-9a425c11b61fb6f4e299013c7d8d9841129b8f45.zip
Bug fixes and other changes
#Fixed issue where a corrupted map cache would lead to a crash *Moved Big-endian compatibility functions to common/utils.h #Fixed issue 8162 *http://hercules.ws/board/tracker/issue-8162-loadnpc-doesnt-trigger-oninit-of-duplicate-npcs/ *Added options to npc_parse_duplicate #Fixed issue 8169 *http://hercules.ws/board/tracker/issue-8169-script-command-guildskill-skill-idlevel-not-working-as-intended/ *Changed *guildskill behavior, now it behaves exactly as depicted in the documentation *Updated *guildskill documentation #Added missing GBI types to mapif_parse_GuildBasicInfoChange now it's possible to change guild exp, lv, skill point and skill information #GeoIP revamp *GeoIP module was partially rewritten *Added several data checks to prevent corruption and crashes *Updated GeoIP database *See https://github.com/maxmind/geoip-api-c/blob/master/libGeoIP/GeoIP.c for more information #Added packetver checks regarding NST_MARKET *Now *tradertype warns if user is trying to use this feature with older clients
Diffstat (limited to 'src')
-rw-r--r--src/char/int_guild.c50
-rw-r--r--src/char/inter.c119
-rw-r--r--src/common/mmo.h14
-rw-r--r--src/common/utils.c52
-rw-r--r--src/common/utils.h10
-rw-r--r--src/map/clif.c6
-rw-r--r--src/map/intif.c20
-rw-r--r--src/map/map.c14
-rw-r--r--src/map/npc.c21
-rw-r--r--src/map/npc.h2
-rw-r--r--src/map/script.c35
-rw-r--r--src/tool/mapcache.c59
12 files changed, 310 insertions, 92 deletions
diff --git a/src/char/int_guild.c b/src/char/int_guild.c
index 28b803027..d8556f023 100644
--- a/src/char/int_guild.c
+++ b/src/char/int_guild.c
@@ -1421,29 +1421,61 @@ int mapif_parse_GuildMessage(int fd,int guild_id,int account_id,char *mes,int le
return mapif_guild_message(guild_id,account_id,mes,len, fd);
}
-// Modification of the guild
+/**
+ * Changes basic guild information
+ * The types are available in mmo.h::guild_basic_info
+ **/
int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const void *data, int len) {
struct guild *g;
- short value = *((const int16 *)data);
+ struct guild_skill gd_skill;
+ short value;
g = inter_guild_fromsql(guild_id);
- if(g==NULL)
+
+ if( g == NULL )
return 0;
switch(type) {
+ case GBI_EXP:
+ value = *((const int16 *)data);
+ if( g->exp+value < 0 )
+ return 0;
+ g->exp += value;
+ guild_calcinfo(g);
+ break;
+
case GBI_GUILDLV:
+ value = *((const int16 *)data);
if (value > 0 && g->guild_lv + value <= MAX_GUILDLEVEL) {
g->guild_lv += value;
g->skill_point += value;
} else if (value < 0 && g->guild_lv + value >= 1)
g->guild_lv += value;
- mapif_guild_info(-1,g);
- g->save_flag |= GS_LEVEL;
- return 0;
- default:
- ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n",type);
break;
+
+ case GBI_SKILLPOINT:
+ value = *((const int16 *)data);
+ if( g->skill_point+value < 0 )
+ return 0;
+ g->skill_point += value;
+ break;
+
+ case GBI_SKILLLV:
+ gd_skill = *((const struct guild_skill*)data);
+ memcpy(&(g->skill[(gd_skill.id - GD_SKILLBASE)]), &gd_skill, sizeof(gd_skill));
+ if( !guild_calcinfo(g) )
+ mapif_guild_info(-1,g);
+ g->save_flag |= GS_SKILL;
+ mapif_guild_skillupack(g->guild_id, gd_skill.id, 0);
+ break;
+
+ default:
+ ShowError("int_guild: GuildBasicInfoChange: Unknown type %d, see mmo.h::guild_basic_info for more information\n",type);
+ return 0;
}
- mapif_guild_basicinfochanged(guild_id,type,data,len);
+ mapif_guild_info(-1,g);
+ g->save_flag |= GS_LEVEL;
+ // Information is already sent in mapif_guild_info
+ //mapif_guild_basicinfochanged(guild_id,type,data,len);
return 0;
}
diff --git a/src/char/inter.c b/src/char/inter.c
index 6a8412b25..a2ff556d9 100644
--- a/src/char/inter.c
+++ b/src/char/inter.c
@@ -370,7 +370,11 @@ const char* job_name(int class_) {
}
/* [Dekamaster/Nightroad] */
-const char * geoip_countryname[253] = {"Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
+#define GEOIP_MAX_COUNTRIES 255
+#define GEOIP_STRUCTURE_INFO_MAX_SIZE 20
+#define GEOIP_COUNTRY_BEGIN 16776960
+
+const char * geoip_countryname[GEOIP_MAX_COUNTRIES] = {"Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
"Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
"Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
@@ -395,17 +399,15 @@ const char * geoip_countryname[253] = {"Unknown","Asia/Pacific Region","Europe",
"Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela",
"Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa",
"Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey",
- "Saint Barthelemy","Saint Martin"};
-unsigned char *geoip_cache;
-void geoip_readdb(void){
- struct stat bufa;
- FILE *db=fopen("./db/GeoIP.dat","rb");
- fstat(fileno(db), &bufa);
- geoip_cache = (unsigned char *) malloc(sizeof(unsigned char) * bufa.st_size);
- if(fread(geoip_cache, sizeof(unsigned char), bufa.st_size, db) != bufa.st_size) { ShowError("geoip_cache reading didn't read all elements \n"); }
- fclose(db);
- ShowStatus("Finished Reading "CL_GREEN"GeoIP"CL_RESET" Database.\n");
-}
+ "Saint Barthelemy", "Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan"};
+/**
+ * GeoIP information
+ **/
+struct s_geoip {
+ unsigned char *cache; // GeoIP.dat information see geoip_init()
+ bool active;
+} geoip;
+
/* [Dekamaster/Nightroad] */
/* WHY NOT A DBMAP: There are millions of entries in GeoIP and it has its own algorithm to go quickly through them, a DBMap wouldn't be efficient */
const char* geoip_getcountry(uint32 ipnum){
@@ -414,8 +416,11 @@ const char* geoip_getcountry(uint32 ipnum){
const unsigned char *buf;
unsigned int offset = 0;
+ if( geoip.active == false )
+ return geoip_countryname[0];
+
for (depth = 31; depth >= 0; depth--) {
- buf = geoip_cache + (long)6 *offset;
+ buf = geoip.cache + (long)6 *offset;
if (ipnum & (1 << depth)) {
/* Take the right-hand branch */
x = (buf[3*1 + 0] << (0*8))
@@ -427,16 +432,97 @@ const char* geoip_getcountry(uint32 ipnum){
+ (buf[3*0 + 1] << (1*8))
+ (buf[3*0 + 2] << (2*8));
}
- if (x >= 16776960) {
- x=x-16776960;
+ if (x >= GEOIP_COUNTRY_BEGIN) {
+ x = x-GEOIP_COUNTRY_BEGIN;
+
+ if( x > GEOIP_MAX_COUNTRIES )
+ return geoip_countryname[0];
+
return geoip_countryname[x];
}
offset = x;
}
+ ShowError("geoip_getcountry(): Error traversing database for ipnum %d\n", ipnum);
+ ShowWarning("geoip_getcountry(): Possible database corruption!\n");
+
return geoip_countryname[0];
}
/**
+ * Disables GeoIP
+ * frees geoip.cache
+ **/
+void geoip_final( void ) {
+ if( geoip.cache )
+ aFree(geoip.cache);
+
+ if( geoip.active ) {
+ ShowStatus("GeoIP "CL_RED"disabled"CL_RESET".\n");
+ geoip.active = false;
+ }
+}
+
+/**
+ * Reads GeoIP database and stores it into memory
+ * geoip.cache should be freed after use!
+ * http://dev.maxmind.com/geoip/legacy/geolite/
+ **/
+void geoip_init(void) {
+ int i, fno;
+ char db_type = 1;
+ unsigned char delim[3];
+ struct stat bufa;
+ FILE *db;
+
+ geoip.active = true;
+
+ db = fopen("./db/GeoIP.dat","rb");
+ if( db == NULL ) {
+ ShowError("geoip_readdb: Error reading GeoIP.dat!\n");
+ geoip_final();
+ return;
+ }
+ fno = fileno(db);
+ if( fstat(fno, &bufa) < 0 ) {
+ ShowError("geoip_readdb: Error stating GeoIP.dat! Error %d\n", errno);
+ geoip_final();
+ return;
+ }
+ geoip.cache = aMalloc( (sizeof(geoip.cache) * bufa.st_size) );
+ if( fread(geoip.cache, sizeof(unsigned char), bufa.st_size, db) != bufa.st_size ) {
+ ShowError("geoip_cache: Couldn't read all elements!\n");
+ fclose(db);
+ geoip_final();
+ return;
+ }
+
+ // Search database type
+ lseek(fno, -3l, SEEK_END);
+ for( i = 0; i < GEOIP_STRUCTURE_INFO_MAX_SIZE; i++ ) {
+ read(fno, delim, 3);
+ if( delim[0] == 255 && delim[1] == 255 && delim[2] == 255 ) {
+ read(fno, &db_type, 1);
+ break;
+ } else {
+ lseek(fno, -4l, SEEK_CUR);
+ }
+ }
+
+ fclose(db);
+
+ if( db_type != 1 ) {
+ if( db_type )
+ ShowError("geoip_init(): Database type is not supported %d!\n", db_type);
+ else
+ ShowError("geoip_init(): GeoIP is corrupted!\n");
+
+ geoip_final();
+ return;
+ }
+ ShowStatus("Finished Reading "CL_GREEN"GeoIP"CL_RESET" Database.\n");
+}
+
+/**
* Argument-list version of inter_msg_to_fd
* @see inter_msg_to_fd
*/
@@ -934,7 +1020,7 @@ int inter_init_sql(const char *file)
inter_mail_sql_init();
inter_auction_sql_init();
- geoip_readdb();
+ geoip_init();
msg_config_read("conf/messages.conf", false);
return 0;
}
@@ -954,6 +1040,7 @@ void inter_final(void)
inter_mail_sql_final();
inter_auction_sql_final();
+ geoip_final();
do_final_msg();
return;
}
diff --git a/src/common/mmo.h b/src/common/mmo.h
index 07a05677f..d535d2874 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -632,11 +632,21 @@ enum fame_list_type {
RANKTYPE_PK = 3, //Not supported yet
};
-enum { //Change Guild Infos
+/**
+ * Guild Basic Information
+ * It is used to request changes via intif_guild_change_basicinfo in map-server and to
+ * signalize changes made in char-server via mapif_parse_GuildMemberInfoChange
+ **/
+enum guild_basic_info {
GBI_EXP = 1, ///< Guild Experience (EXP)
GBI_GUILDLV, ///< Guild level
GBI_SKILLPOINT, ///< Guild skillpoints
- GBI_SKILLLV, ///< Guild skill_lv ?? seem unused
+
+ /**
+ * Changes a skill level, struct guild_skill should be sent.
+ * All checks regarding max skill level should be done in _map-server_
+ **/
+ GBI_SKILLLV, ///< Guild skill_lv
};
enum { //Change Member Infos
diff --git a/src/common/utils.c b/src/common/utils.c
index 9a7d4971b..47747dd32 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -263,7 +263,59 @@ uint32 MakeDWord(uint16 word0, uint16 word1)
( (uint32)(word0 ) )|
( (uint32)(word1 << 0x10) );
}
+/*************************************
+* Big-endian compatibility functions *
+* From mapcache.c *
+*************************************/
+// Converts an int16 from current machine order to little-endian
+int16 MakeShortLE(int16 val)
+{
+ unsigned char buf[2];
+ buf[0] = (unsigned char)( (val & 0x00FF) );
+ buf[1] = (unsigned char)( (val & 0xFF00) >> 0x08 );
+ return *((int16*)buf);
+}
+
+// Converts an int32 from current machine order to little-endian
+int32 MakeLongLE(int32 val)
+{
+ unsigned char buf[4];
+ buf[0] = (unsigned char)( (val & 0x000000FF) );
+ buf[1] = (unsigned char)( (val & 0x0000FF00) >> 0x08 );
+ buf[2] = (unsigned char)( (val & 0x00FF0000) >> 0x10 );
+ buf[3] = (unsigned char)( (val & 0xFF000000) >> 0x18 );
+ return *((int32*)buf);
+}
+
+// Reads an uint16 in little-endian from the buffer
+uint16 GetUShort(const unsigned char* buf)
+{
+ return ( ((uint16)(buf[0])) )
+ |( ((uint16)(buf[1])) << 0x08 );
+}
+
+// Reads an uint32 in little-endian from the buffer
+uint32 GetULong(const unsigned char* buf)
+{
+ return ( ((uint32)(buf[0])) )
+ |( ((uint32)(buf[1])) << 0x08 )
+ |( ((uint32)(buf[2])) << 0x10 )
+ |( ((uint32)(buf[3])) << 0x18 );
+}
+
+// Reads an int32 in little-endian from the buffer
+int32 GetLong(const unsigned char* buf)
+{
+ return (int32)GetULong(buf);
+}
+
+// Reads a float (32 bits) from the buffer
+float GetFloat(const unsigned char* buf)
+{
+ uint32 val = GetULong(buf);
+ return *((float*)(void*)&val);
+}
/// calculates the value of A / B, in percent (rounded down)
unsigned int get_percentage(const unsigned int A, const unsigned int B)
diff --git a/src/common/utils.h b/src/common/utils.h
index 68dd01ac4..f89546b8a 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -36,6 +36,16 @@ extern uint16 GetWord(uint32 val, int idx);
extern uint16 MakeWord(uint8 byte0, uint8 byte1);
extern uint32 MakeDWord(uint16 word0, uint16 word1);
+//////////////////////////////////////////////////////////////////////////
+// Big-endian compatibility functions
+//////////////////////////////////////////////////////////////////////////
+extern int16 MakeShortLE(int16 val);
+extern int32 MakeLongLE(int32 val);
+extern uint16 GetUShort(const unsigned char* buf);
+extern uint32 GetULong(const unsigned char* buf);
+extern int32 GetLong(const unsigned char* buf);
+extern float GetFloat(const unsigned char* buf);
+
size_t hread(void * ptr, size_t size, size_t count, FILE * stream);
size_t hwrite(const void * ptr, size_t size, size_t count, FILE * stream);
diff --git a/src/map/clif.c b/src/map/clif.c
index 2fbf64e09..24dcfebf4 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -18179,6 +18179,7 @@ void clif_parse_NPCShopClosed(int fd, struct map_session_data *sd) {
}
/* NPC Market (by Ind after an extensive debugging of the packet, only possible thanks to Yommy <3) */
void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) {
+#if PACKETVER >= 20131223
struct npc_item_list *shop = nd->u.scr.shop->item;
unsigned short shop_size = nd->u.scr.shop->items, i, c;
struct item_data *id = NULL;
@@ -18199,12 +18200,14 @@ void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) {
npcmarket_open.PacketLength = 4 + ( sizeof(npcmarket_open.list[0]) * c );
clif->send(&npcmarket_open,npcmarket_open.PacketLength,&sd->bl,SELF);
+#endif
}
void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) {
/* TODO track the state <3~ */
sd->npc_shopid = 0;
}
void clif_npc_market_purchase_ack(struct map_session_data *sd, struct packet_npc_market_purchase *req, unsigned char response) {
+#if PACKETVER >= 20131223
unsigned short c = 0;
npcmarket_result.PacketType = npcmarketresultackType;
@@ -18237,11 +18240,14 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, struct packet_npc
npcmarket_result.PacketLength = 5 + ( sizeof(npcmarket_result.list[0]) * c );;
clif->send(&npcmarket_result,npcmarket_result.PacketLength,&sd->bl,SELF);
+#endif
}
void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) {
+#if PACKETVER >= 20131223
struct packet_npc_market_purchase *p = P2PTR(fd);
clif->npc_market_purchase_ack(sd,p,npc->market_buylist(sd,(p->PacketLength - 4) / sizeof(p->list[0]),p));
+#endif
}
/* */
unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) {
diff --git a/src/map/intif.c b/src/map/intif.c
index 1aa5a46d4..6bd24368a 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -692,7 +692,10 @@ int intif_guild_message(int guild_id,int account_id,const char *mes,int len)
return 0;
}
-// Request a change of Guild basic information
+/**
+ * Requests to change a basic guild information, it is parsed via mapif_parse_GuildBasicInfoChange
+ * To see the information types that can be changed see mmo.h::guild_basic_info
+ **/
int intif_guild_change_basicinfo(int guild_id,int type,const void *data,int len)
{
if (intif->CheckForCharServer())
@@ -1237,6 +1240,21 @@ void intif_parse_GuildBasicInfoChanged(int fd) {
case GBI_EXP: g->exp = RFIFOQ(fd,10); break;
case GBI_GUILDLV: g->guild_lv = RFIFOW(fd,10); break;
case GBI_SKILLPOINT: g->skill_point = RFIFOL(fd,10); break;
+ case GBI_SKILLLV: {
+ int idx, max;
+ struct guild_skill *gs = (struct guild_skill *)RFIFOP(fd,10);
+
+ if( gs == NULL )
+ return;
+
+ idx = gs->id - GD_SKILLBASE;
+ max = guild->skill_get_max(gs->id);
+ if( gs->lv > max )
+ gs->lv = max;
+
+ memcpy(&(g->skill[idx]), gs, sizeof(g->skill[idx]));
+ break;
+ }
}
}
diff --git a/src/map/map.c b/src/map/map.c
index 04ac8a239..11ef56cb3 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -2846,6 +2846,7 @@ int map_eraseipport(unsigned short map_index, uint32 ip, uint16 port) {
* [Shinryo]: Init the mapcache
*------------------------------------------*/
char *map_init_mapcache(FILE *fp) {
+ struct map_cache_main_header header;
size_t size = 0;
char *buffer;
@@ -2869,6 +2870,19 @@ char *map_init_mapcache(FILE *fp) {
return NULL;
}
+ rewind(fp);
+
+ // Get main header to verify if data is corrupted
+ if( fread(&header, sizeof(header), 1, fp) != 1 ) {
+ ShowError("map_init_mapcache: Error obtaining main header!\n");
+ return NULL;
+ }
+ ShowError("Map cache is corrupted!\r"); // If the file is totally corrupted this will allow us to warn the user
+ if( GetULong((unsigned char *)&(header.file_size)) != size ) {
+ ShowError("map_init_mapcache: Map cache is corrupted!\n");
+ return NULL;
+ }
+
return buffer;
}
diff --git a/src/map/npc.c b/src/map/npc.c
index ae374e961..43e4bcc88 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -2935,7 +2935,8 @@ const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char*
/// shop/cashshop/npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id>
/// npc: -%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY>
/// npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY>
-const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
+/// !!Only NPO_ONINIT is available trough options!!
+const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath, int options)
{
int x, y, dir, m, xs = -1, ys = -1;
char mapname[32];
@@ -3074,6 +3075,20 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch
nd->u.scr.timerid = INVALID_TIMER;
+ if( type == SCRIPT && options&NPO_ONINIT ) {
+ // From npc_parse_script
+ char evname[EVENT_NAME_LENGTH];
+ struct event_data *ev;
+
+ snprintf(evname, ARRAYLENGTH(evname), "%s::OnInit", nd->exname);
+
+ if( ( ev = (struct event_data*)strdb_get(npc->ev_db, evname) ) ) {
+
+ //Execute OnInit
+ script->run(nd->u.scr.script,ev->pos,0,nd->bl.id);
+
+ }
+ }
return end;
}
@@ -3137,7 +3152,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) {
else
snprintf(w4, sizeof(w4), "%d", snd->class_);
- npc->parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "INSTANCING");
+ npc->parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "INSTANCING", NPO_NONE);
}
return 0;
@@ -4094,7 +4109,7 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) {
}
else if( (i=0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3 )
{
- p = npc->parse_duplicate(w1,w2,w3,w4, p, buffer, filepath);
+ p = npc->parse_duplicate(w1,w2,w3,w4, p, buffer, filepath, (runOnInit?NPO_ONINIT:NPO_NONE));
}
else if( (strcmp(w2,"monster") == 0 || strcmp(w2,"boss_monster") == 0) && count > 3 )
{
diff --git a/src/map/npc.h b/src/map/npc.h
index 346a9f8c0..d1e060a39 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -237,7 +237,7 @@ struct npc_interface {
void (*convertlabel_db) (struct npc_label_list *label_list, const char *filepath);
const char* (*skip_script) (const char *start, const char *buffer, const char *filepath);
const char* (*parse_script) (char *w1, char *w2, char *w3, char *w4, const char *start, const char *buffer, const char *filepath, int options);
- const char* (*parse_duplicate) (char *w1, char *w2, char *w3, char *w4, const char *start, const char *buffer, const char *filepath);
+ const char* (*parse_duplicate) (char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath, int options);
int (*duplicate4instance) (struct npc_data *snd, int16 m);
void (*setcells) (struct npc_data *nd);
int (*unsetcells_sub) (struct block_list *bl, va_list ap);
diff --git a/src/map/script.c b/src/map/script.c
index 3e11a510e..c57b086f4 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -8268,20 +8268,36 @@ BUILDIN(addtoskill) {
/// guildskill <skill id>,<amount>;
/// guildskill "<skill name>",<amount>;
BUILDIN(guildskill) {
- int id;
+ int skill_id, id, max_points;
int level;
+
TBL_PC* sd;
- int i;
+ struct guild *gd;
+ struct guild_skill gd_skill;
sd = script->rid2sd(st);
if( sd == NULL )
- return true;// no player attached, report source
+ return false; // no player attached, report source
- id = ( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ if( (gd = sd->guild) == NULL )
+ return true;
+
+ skill_id = ( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) );
level = script_getnum(st,3);
- for( i=0; i < level; i++ )
- guild->skillup(sd, id);
+ id = skill_id - GD_SKILLBASE;
+ max_points = guild->skill_get_max(skill_id);
+
+ if( (gd->skill[id].lv + level) > max_points )
+ level = max_points - gd->skill[id].lv;
+
+ if( level == 0 )
+ return true;
+
+ memcpy(&gd_skill, &(gd->skill[id]), sizeof(gd->skill[id]));
+ gd_skill.lv += level;
+
+ intif->guild_change_basicinfo( gd->guild_id, GBI_SKILLLV, &(gd_skill), sizeof(gd_skill) );
return true;
}
@@ -18538,6 +18554,13 @@ BUILDIN(tradertype) {
npc->market_delfromsql(nd,USHRT_MAX);
}
+#if PACKETVER < 20131223
+ if( type == NST_MARKET ) {
+ ShowWarning("buildin_tradertype: NST_MARKET is only available with PACKETVER 20131223 or newer!\n");
+ script->reportsrc(st);
+ }
+#endif
+
nd->u.scr.shop->type = type;
return true;
diff --git a/src/tool/mapcache.c b/src/tool/mapcache.c
index caa55c87a..85d5fb548 100644
--- a/src/tool/mapcache.c
+++ b/src/tool/mapcache.c
@@ -6,6 +6,7 @@
#include "../common/malloc.h"
#include "../common/mmo.h"
#include "../common/showmsg.h"
+#include "../common/utils.h"
#include "../config/renewal.h"
@@ -49,60 +50,10 @@ struct map_info {
int32 len;
};
-
-/*************************************
-* Big-endian compatibility functions *
-*************************************/
-
-// Converts an int16 from current machine order to little-endian
-int16 MakeShortLE(int16 val)
-{
- unsigned char buf[2];
- buf[0] = (unsigned char)( (val & 0x00FF) );
- buf[1] = (unsigned char)( (val & 0xFF00) >> 0x08 );
- return *((int16*)buf);
-}
-
-// Converts an int32 from current machine order to little-endian
-int32 MakeLongLE(int32 val)
-{
- unsigned char buf[4];
- buf[0] = (unsigned char)( (val & 0x000000FF) );
- buf[1] = (unsigned char)( (val & 0x0000FF00) >> 0x08 );
- buf[2] = (unsigned char)( (val & 0x00FF0000) >> 0x10 );
- buf[3] = (unsigned char)( (val & 0xFF000000) >> 0x18 );
- return *((int32*)buf);
-}
-
-// Reads an uint16 in little-endian from the buffer
-uint16 GetUShort(const unsigned char* buf)
-{
- return ( ((uint16)(buf[0])) )
- |( ((uint16)(buf[1])) << 0x08 );
-}
-
-// Reads an uint32 in little-endian from the buffer
-uint32 GetULong(const unsigned char* buf)
-{
- return ( ((uint32)(buf[0])) )
- |( ((uint32)(buf[1])) << 0x08 )
- |( ((uint32)(buf[2])) << 0x10 )
- |( ((uint32)(buf[3])) << 0x18 );
-}
-
-// Reads an int32 in little-endian from the buffer
-int32 GetLong(const unsigned char* buf)
-{
- return (int32)GetULong(buf);
-}
-
-// Reads a float (32 bits) from the buffer
-float GetFloat(const unsigned char* buf)
-{
- uint32 val = GetULong(buf);
- return *((float*)(void*)&val);
-}
-
+ /*************************************
+ * Big-endian compatibility functions *
+ * Moved to utils.h *
+ *************************************/
// Reads a map from GRF's GAT and RSW files
int read_map(char *name, struct map_data *m)