소스 검색

Corrected the max. allowed skill name length, and optimized code that works with skill names according to latest changes.
Changed skill db loading code to work similarly to how itemdb/mobdb is loaded (generic file loader + specialized function to process rows).
* all skill db files are now checked for inconsistencies the same way.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@11659 54d463be-8e91-2dee-dedb-b68131a5f0ec

ultramage 17 년 전
부모
커밋
9d59dba910
6개의 변경된 파일337개의 추가작업 그리고 392개의 파일을 삭제
  1. 5 0
      Changelog-Trunk.txt
  2. 3 7
      src/map/atcommand.c
  3. 3 3
      src/map/battle.c
  4. 5 5
      src/map/clif.c
  5. 320 376
      src/map/skill.c
  6. 1 1
      src/map/skill.h

+ 5 - 0
Changelog-Trunk.txt

@@ -4,6 +4,11 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
 2007/11/05
+	* Changed skill db loading code to work similarly to how itemdb/mobdb
+	  is loaded (generic file loader + specialized function to process rows)
+	- all skill db files are now checked for inconsistencies the same way
+	* Corrected the max. allowed skill name length, and optimized code that
+	  works with skill names according to latest changes [ultramage]
 	* Reverted official drop rate estimation [Playtester]
 	- although it really exists we don't have enough information about it
 	- if server owners really want it they can implement it themselves

+ 3 - 7
src/map/atcommand.c

@@ -5620,8 +5620,7 @@ int atcommand_skillid(const int fd, struct map_session_data* sd, const char* com
 	skillen = strlen(message);
 
 	for (idx = 0; idx < MAX_SKILL_DB; idx++) {
-		if ((skill_db[idx].name != NULL && strnicmp(skill_db[idx].name, message, skillen) == 0) ||
-			(skill_db[idx].desc != NULL && strnicmp(skill_db[idx].desc, message, skillen) == 0))
+		if (strnicmp(skill_db[idx].name, message, skillen) == 0 || strnicmp(skill_db[idx].desc, message, skillen) == 0)
 		{
 			sprintf(atcmd_output, "skill %d: %s", idx, skill_db[idx].desc);
 			clif_displaymessage(fd, atcmd_output);
@@ -5750,12 +5749,9 @@ int atcommand_skilltree(const int fd, struct map_session_data* sd, const char* c
 	ent = &skill_tree[c][skillidx];
 
 	for(j=0;j<5;j++)
-		if( ent->need[j].id &&
-			pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv)
+		if( ent->need[j].id && pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv)
 		{
-			char *desc = (skill_db[ ent->need[j].id ].desc) ? skill_db[ ent->need[j].id ].desc : "Unknown skill";
-			sprintf(atcmd_output, "player requires level %d of skill %s",
-				ent->need[j].lv, desc);
+			sprintf(atcmd_output, "player requires level %d of skill %s", ent->need[j].lv, skill_db[ent->need[j].id].desc);
 			clif_displaymessage(fd, atcmd_output);
 			meets = 0;
 		}

+ 3 - 3
src/map/battle.c

@@ -1244,7 +1244,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					if(wflag>0)
 						wd.damage/= wflag;
 					else if(battle_config.error_log)
-						ShowError("0 enemies targeted by %s, divide per 0 avoided!\n", skill_get_name(skill_num));
+						ShowError("0 enemies targeted by %d:%s, divide per 0 avoided!\n", skill_num, skill_get_name(skill_num));
 				}
 
 				//Add any bonuses that modify the base baseatk+watk (pre-skills)
@@ -2221,7 +2221,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 					if(mflag>0)
 						ad.damage/= mflag;
 					else if(battle_config.error_log)
-						ShowError("0 enemies targeted by %s, divide per 0 avoided!\n", skill_get_name(skill_num));
+						ShowError("0 enemies targeted by %d:%s, divide per 0 avoided!\n", skill_num, skill_get_name(skill_num));
 				}
 
 				switch(skill_num){
@@ -2564,7 +2564,7 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
 		if(mflag>0)
 			md.damage/= mflag;
 		else if(battle_config.error_log)
-			ShowError("0 enemies targeted by %s, divide per 0 avoided!\n", skill_get_name(skill_num));
+			ShowError("0 enemies targeted by %d:%s, divide per 0 avoided!\n", skill_num, skill_get_name(skill_num));
 	}
 
 	damage_div_fix(md.damage, md.div_);

+ 5 - 5
src/map/clif.c

@@ -1433,7 +1433,7 @@ int clif_homskillinfoblock(struct map_session_data *sd)
 			WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv ;
 			WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv) ;
 			WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv) ;
-			strncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH) ;
+			safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH) ;
 			WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < merc_skill_tree_get_max(id, hd->homunculus.class_))?1:0;
 			len+=37;
 		}
@@ -4135,7 +4135,7 @@ int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range)
 		range = skill_get_range2(&sd->bl, id,sd->status.skill[skillid].lv);
 
 	WFIFOW(fd,12)= range;
-	strncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
+	safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
 	if(sd->status.skill[skillid].flag ==0)
 		WFIFOB(fd,38)= (sd->status.skill[skillid].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
 	else
@@ -4170,7 +4170,7 @@ int clif_skillinfoblock(struct map_session_data *sd)
 			WFIFOW(fd,len+6) = sd->status.skill[i].lv;
 			WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
 			WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
-			strncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+			safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
 			if(sd->status.skill[i].flag == 0)
 				WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
 			else
@@ -5261,7 +5261,7 @@ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv)
 	WFIFOW(fd, 8)=skilllv;
 	WFIFOW(fd,10)=skill_get_sp(skillid,skilllv);
 	WFIFOW(fd,12)=skill_get_range2(&sd->bl, skillid,skilllv);
-	strncpy((char*)WFIFOP(fd,14),skill_get_name(skillid),NAME_LENGTH);
+	safestrncpy((char*)WFIFOP(fd,14),skill_get_name(skillid),NAME_LENGTH);
 	WFIFOB(fd,38)=0;
 	WFIFOSET(fd,packet_len(0x147));
 	return 0;
@@ -6623,7 +6623,7 @@ int clif_guild_skillinfo(struct map_session_data* sd)
 			WFIFOW(fd,c*37+12) = g->skill[i].lv;
 			WFIFOW(fd,c*37+14) = skill_get_sp(id, g->skill[i].lv);
 			WFIFOW(fd,c*37+16) = skill_get_range(id, g->skill[i].lv);
-			strncpy((char*)WFIFOP(fd,c*37+18), skill_get_name(id), NAME_LENGTH);
+			safestrncpy((char*)WFIFOP(fd,c*37+18), skill_get_name(id), NAME_LENGTH);
 			WFIFOB(fd,c*37+42)= (g->skill[i].lv < guild_skill_get_max(id) && sd == g->member[0].sd) ? 1 : 0;
 			c++;
 		}

+ 320 - 376
src/map/skill.c

@@ -83,14 +83,12 @@ static int skill_get_index( int id )
 
 const char* skill_get_name( int id )
 {
-	int index = skill_get_index(id);
-	return ( index > 0 ) ? skill_db[id].name : "UNKNOWN_SKILL";
+	return skill_db[skill_get_index(id)].name;
 }
 
 const char* skill_get_desc( int id )
 {
-	int index = skill_get_index(id);
-	return ( index > 0 ) ? skill_db[id].desc : "Unknown Skill";
+	return skill_db[skill_get_index(id)].desc;
 }
 
 // macros to check for out of bounds errors [celest]
@@ -7394,8 +7392,7 @@ static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list
 	skill=va_arg(ap,int);
 	c=va_arg(ap,int *);
 
-	if(md->master_id != src_id ||
-		md->special_state.ai != (skill == AM_SPHEREMINE?2:3))
+	if( md->master_id != src_id || md->special_state.ai != (unsigned)(skill == AM_SPHEREMINE?2:3) )
 		return 0; //Non alchemist summoned mobs have nothing to do here.
 
 	if(md->class_==mob_class)
@@ -8549,7 +8546,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx)
 				clif_misceffect(&sd->bl,3);
 				if(item->refine == MAX_REFINE &&
 					item->card[0] == CARD0_FORGE &&
-					MakeDWord(item->card[2],item->card[3]) == sd->status.char_id)
+					(int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id)
 				{ // Fame point system [DracoRPG]
 					switch(ditem->wlv){
 						case 1:
@@ -10710,422 +10707,369 @@ void skill_init_unit_layout (void)
 /*==========================================
  * DB reading.
  * skill_db.txt
+ * skill_require_db.txt
  * skill_cast_db.txt
+ * skill_unit_db.txt
  * produce_db.txt 
  * create_arrow_db.txt
  * abra_db.txt
+ * skill_castnodex_db.txt
+ * skill_nocast_db.txt
  *------------------------------------------*/
-int skill_readdb (void)
+/// Opens and parses a CSV file into columns, feeding them to the specified callback function row by row.
+/// Tracks the progress of the operation (file position, number of successfully processed rows).
+/// Returns 'true' if it was able to process the specified file, or 'false' if it could not be read.
+static bool skill_read_csvdb( const char* directory, const char* filename, int mincolumns, bool (*parseproc)(char* split[], int columns, int current) )
 {
-	int i,j,k,l,lines;
-	FILE *fp;
-	char line[1024],path[1024],*p;
-
-	// load 'skill_db.txt'
-	memset(skill_db,0,sizeof(skill_db));
-	sprintf(path, "%s/skill_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
+	FILE* fp;
+	int lines = 0;
+	int entries = 0;
+	char path[1024], line[1024];
+
+	// open file
+	snprintf(path, sizeof(path), "%s/%s", directory, filename);
+	fp = fopen(path,"r");
+	if( fp == NULL )
+	{
+		ShowError("skill_read_db: can't read %s\n", path);
+		return false;
 	}
-	lines = 0;
-	while(fgets(line, sizeof(line), fp))
+
+	// process rows one by one
+	while( fgets(line, sizeof(line), fp) )
 	{
 		char* split[50];
+		int columns;
+
 		lines++;
-		if(line[0]=='/' && line[1]=='/')
+		if( line[0] == '/' && line[1] == '/' )
 			continue;
-		j = skill_split_str(line,split,17);
-		if( j < 2 )
+		//TODO: strip trailing // comment
+		//TODO: strip trailing whitespace
+		//TODO: skip empty lines
+
+		memset(split,0,sizeof(split));
+		columns = skill_split_str(line,split,ARRAYLENGTH(split));
+		if( columns < 2 ) // FIXME: assumes db has at least 2 mandatory columns
 			continue; // empty line
-		if( j < 17 )
+		if( columns < mincolumns )
 		{
-			ShowError("skill_readdb: Insufficient columns in line %d of \"%s\" (skill with id %d), skipping.\n", lines, path, atoi(split[0]));
+			ShowError("skill_read_csvdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincolumns);
 			continue; // not enough columns
 		}
-
-		i = atoi(split[0]);
-		if (i >= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX) {
-			ShowWarning("read skill_db: Can't use skill id %d as guild skills are placed there!\n");
-			continue;
+		if( columns > ARRAYLENGTH(split) )
+		{
+			ShowError("skill_read_csvdb: Too many columns in line %d of \"%s\" (found %d, capacity %d). Increase the capacity in the source code please.\n", lines, path, columns, ARRAYLENGTH(split) );
+			continue; // source code problem
 		}
-		i = skill_get_index(i);
-		if (i == 0) // invalid skill id
-			continue;
-		
-		skill_split_atoi(split[1],skill_db[i].range);
-		skill_db[i].hit=atoi(split[2]);
-		skill_db[i].inf=atoi(split[3]);
-		skill_split_atoi(split[4],skill_db[i].element);
-		skill_db[i].nk=(int)strtol(split[5], NULL, 0);
-		skill_split_atoi(split[6],skill_db[i].splash);
-		skill_db[i].max=atoi(split[7]);
-		skill_split_atoi(split[8],skill_db[i].num);
-
-		if(strcmpi(split[9],"yes") == 0)
-			skill_db[i].castcancel=1;
-		else
-			skill_db[i].castcancel=0;
-		skill_db[i].cast_def_rate=atoi(split[10]);
-		skill_db[i].inf2=(int)strtol(split[11], NULL, 0);
-		skill_split_atoi(split[12], skill_db[i].maxcount);
-		if(strcmpi(split[13],"weapon") == 0)
-			skill_db[i].skill_type=BF_WEAPON;
-		else if(strcmpi(split[13],"magic") == 0)
-			skill_db[i].skill_type=BF_MAGIC;
-		else if(strcmpi(split[13],"misc") == 0)
-			skill_db[i].skill_type=BF_MISC;
-		else
-			skill_db[i].skill_type=0;
-		skill_split_atoi(split[14],skill_db[i].blewcount);
-		safestrncpy(skill_db[i].name, split[15], sizeof(skill_db[i].name));
-		safestrncpy(skill_db[i].desc, split[16], sizeof(skill_db[i].desc));
-	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
 
-	// load 'skill_require_db.txt'
-	sprintf(path, "%s/skill_require_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
+		// parse this row
+		if( parseproc(split, columns, entries) )
+			entries++;
 	}
-	while(fgets(line, sizeof(line), fp))
-	{
-		char *split[50];
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		j = skill_split_str(line,split,32);
-		if( j < 32 )
-			continue;
 
-		i = atoi(split[0]);
-		i = skill_get_index(i);
-		if(i == 0) // invalid skill id
-			continue;
+	fclose(fp);
+	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path);
 
-		skill_split_atoi(split[1],skill_db[i].hp);
-		skill_split_atoi(split[2],skill_db[i].mhp);
-		skill_split_atoi(split[3],skill_db[i].sp);
-		skill_split_atoi(split[4],skill_db[i].hp_rate);
-		skill_split_atoi(split[5],skill_db[i].sp_rate);
-		skill_split_atoi(split[6],skill_db[i].zeny);
-		
-		p = split[7];
-		for(j=0;j<32;j++){
-			l = atoi(p);
-			if (l==99) {
-				skill_db[i].weapon = 0xffffffff;
-				break;
-			}
-			else
-				skill_db[i].weapon |= 1<<l;
-			p=strchr(p,':');
-			if(!p)
-				break;
-			p++;
-		}
+	return true;
+}
 
-		p = split[8];
-		for(j=0;j<32;j++){
-			l = atoi(p);
-			if (l==99) {
-				skill_db[i].ammo = 0xffffffff;
-				break;
-			}
-			else if (l)
-				skill_db[i].ammo |= 1<<l;
-			p=strchr(p,':');
-			if(!p)
-				break;
-			p++;
-		}
-		skill_split_atoi(split[9],skill_db[i].ammo_qty);
-
-		if( strcmpi(split[10],"hiding")==0 ) skill_db[i].state=ST_HIDING;
-		else if( strcmpi(split[10],"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
-		else if( strcmpi(split[10],"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
-		else if( strcmpi(split[10],"riding")==0 ) skill_db[i].state=ST_RIDING;
-		else if( strcmpi(split[10],"falcon")==0 ) skill_db[i].state=ST_FALCON;
-		else if( strcmpi(split[10],"cart")==0 ) skill_db[i].state=ST_CART;
-		else if( strcmpi(split[10],"shield")==0 ) skill_db[i].state=ST_SHIELD;
-		else if( strcmpi(split[10],"sight")==0 ) skill_db[i].state=ST_SIGHT;
-		else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
-		else if( strcmpi(split[10],"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
-		else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
-		else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
-		else if( strcmpi(split[10],"water")==0 ) skill_db[i].state=ST_WATER;
-		else skill_db[i].state=ST_NONE;
-
-		skill_split_atoi(split[11],skill_db[i].spiritball);
-		for (j = 0; j < 10; j++) {
-			skill_db[i].itemid[j]=atoi(split[12+ 2*j]);
-			skill_db[i].amount[j]=atoi(split[13+ 2*j]);
-		}
+static bool skill_parse_row_skilldb(char* split[], int columns, int current)
+{// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+	int i = atoi(split[0]);
+	if( i >= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX ) {
+		ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild skill mapping)!\n");
+		return false;
 	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
-	// load 'skill_cast_db.txt'
-	sprintf(path, "%s/skill_cast_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
+	if( i >= HM_SKILLRANGEMIN && i <= HM_SKILLRANGEMAX ) {
+		ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with homunculus skill mapping)!\n");
+		return false;
 	}
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
 
-	l=0;
-	while(fgets(line, sizeof(line), fp))
-	{
-		char *split[50];
-		l++;
-		memset(split,0,sizeof(split));	// [Valaris] thanks to fov
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		j = skill_split_str(line,split,6);
-		if( j < 2 )
-			continue; //Blank line.
-		if( j < 6) {
-			ShowWarning("skill_cast_db.txt: Insufficient number of fields at line %d\n", l);
-			continue;
-		}
-		i = atoi(split[0]);
-		i = skill_get_index(i);
-		if(i == 0) // invalid skill id
-			continue;
+	skill_split_atoi(split[1],skill_db[i].range);
+	skill_db[i].hit = atoi(split[2]);
+	skill_db[i].inf = atoi(split[3]);
+	skill_split_atoi(split[4],skill_db[i].element);
+	skill_db[i].nk = (int)strtol(split[5], NULL, 0);
+	skill_split_atoi(split[6],skill_db[i].splash);
+	skill_db[i].max = atoi(split[7]);
+	skill_split_atoi(split[8],skill_db[i].num);
+
+	if( strcmpi(split[9],"yes") == 0 )
+		skill_db[i].castcancel = 1;
+	else
+		skill_db[i].castcancel = 0;
+	skill_db[i].cast_def_rate = atoi(split[10]);
+	skill_db[i].inf2 = (int)strtol(split[11], NULL, 0);
+	skill_split_atoi(split[12],skill_db[i].maxcount);
+	if( strcmpi(split[13],"weapon") == 0 )
+		skill_db[i].skill_type = BF_WEAPON;
+	else if( strcmpi(split[13],"magic") == 0 )
+		skill_db[i].skill_type = BF_MAGIC;
+	else if( strcmpi(split[13],"misc") == 0 )
+		skill_db[i].skill_type = BF_MISC;
+	else
+		skill_db[i].skill_type = 0;
+	skill_split_atoi(split[14],skill_db[i].blewcount);
+	safestrncpy(skill_db[i].name, split[15], sizeof(skill_db[i].name));
+	safestrncpy(skill_db[i].desc, split[16], sizeof(skill_db[i].desc));
 
-		skill_split_atoi(split[1],skill_db[i].cast);
-		skill_split_atoi(split[2],skill_db[i].delay);
-		skill_split_atoi(split[3],skill_db[i].walkdelay);
-		skill_split_atoi(split[4],skill_db[i].upkeep_time);
-		skill_split_atoi(split[5],skill_db[i].upkeep_time2);
-	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+	return true;
+}
 
-	// load 'skill_unit_db.txt'
-	sprintf(path, "%s/skill_unit_db.txt", db_path);
-	fp=fopen(path,"r");
-	if (fp==NULL) {
-		ShowError("can't read %s\n", path);
-		return 1;
-	}
-	k = 0;
-	while (fgets(line, sizeof(line), fp))
-	{
-		char *split[50];
-		if (line[0]=='/' && line[1]=='/')
-			continue;
-		j = skill_split_str(line,split,8);
-		if ( j < 8 )
-			continue;
+static bool skill_parse_row_requiredb(char* split[], int columns, int current)
+{// SkillID,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10
+	char* p;
+	int j;
 
-		i = atoi(split[0]);
-		i = skill_get_index(i);
-		if(i == 0) // invalid skill id
-			continue;
-		skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
-		skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
-		skill_split_atoi(split[3],skill_db[i].unit_layout_type);
-		skill_split_atoi(split[4],skill_db[i].unit_range);
-		skill_db[i].unit_interval = atoi(split[5]);
-
-		if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
-		else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
-		else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY;
-		else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD;
-		else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL;
-		else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY;
-		else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF;
-		else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE;
-		else skill_db[i].unit_target = strtol(split[6],NULL,16);
-
-		skill_db[i].unit_flag = strtol(split[7],NULL,16);
-
-		if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
-			skill_db[i].unit_target=BCT_NOENEMY;
-
-		//By default, target just characters.
-		skill_db[i].unit_target |= BL_CHAR;
-		if (skill_db[i].unit_flag&UF_NOPC)
-			skill_db[i].unit_target &= ~BL_PC;
-		if (skill_db[i].unit_flag&UF_NOMOB)
-			skill_db[i].unit_target &= ~BL_MOB;
-		if (skill_db[i].unit_flag&UF_SKILL)
-			skill_db[i].unit_target |= BL_SKILL;
-		k++;
-	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-	skill_init_unit_layout();
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
 
-	// load 'produce_db.txt'
-	memset(skill_produce_db,0,sizeof(skill_produce_db));
-	sprintf(path, "%s/produce_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n",path);
-		return 1;
-	}
-	k = 0;
-	while(fgets(line, sizeof(line), fp))
+	skill_split_atoi(split[1],skill_db[i].hp);
+	skill_split_atoi(split[2],skill_db[i].mhp);
+	skill_split_atoi(split[3],skill_db[i].sp);
+	skill_split_atoi(split[4],skill_db[i].hp_rate);
+	skill_split_atoi(split[5],skill_db[i].sp_rate);
+	skill_split_atoi(split[6],skill_db[i].zeny);
+	
+	//FIXME: document this
+	p = split[7];
+	for( j = 0; j < 32; j++ )
 	{
-		char* split[4 + MAX_PRODUCE_RESOURCE * 2];
-		int x,y;
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		memset(split,0,sizeof(split));
-		j = skill_split_str(line,split,(4 + MAX_PRODUCE_RESOURCE * 2));
-		if( j < 4 ) // at least base data needed
-			continue;
-		i=atoi(split[0]);
-		if(i<=0) continue;
-
-		skill_produce_db[k].nameid=i;
-		skill_produce_db[k].itemlv=atoi(split[1]);
-		skill_produce_db[k].req_skill=atoi(split[2]);
-		skill_produce_db[k].req_skill_lv=atoi(split[3]);
-
-		for(x=4,y=0; split[x] && split[x+1] && y<MAX_PRODUCE_RESOURCE; x+=2,y++){
-			skill_produce_db[k].mat_id[y]=atoi(split[x]);
-			skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
-		}
-		k++;
-		if(k >= MAX_SKILL_PRODUCE_DB)
+		int l = atoi(p);
+		if( l == 99 ) // magic value?
 		{
-			ShowError("Reached the max number of produce_db entries (%d), consider raising the value of MAX_SKILL_PRODUCE_DB and recompile.\n", MAX_SKILL_PRODUCE_DB);
+			skill_db[i].weapon = 0xffffffff;
 			break;
 		}
+		else
+			skill_db[i].weapon |= 1<<l;
+		p = strchr(p,':');
+		if(!p)
+			break;
+		p++;
 	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
-
-	// load 'create_arrow_db.txt'
-	memset(skill_arrow_db,0,sizeof(skill_arrow_db));
-	sprintf(path, "%s/create_arrow_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
-	}
-	k = 0;
-	while(fgets(line, sizeof(line), fp))
+	
+	//FIXME: document this
+	p = split[8];
+	for( j = 0; j < 32; j++ )
 	{
-		char* split[16];
-		int x,y;
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		memset(split,0,sizeof(split));
-		j = skill_split_str(line,split,1+2*5);
-		if( j < 3 ) // at least 1 entry
-			continue;
-		i=atoi(split[0]);
-		if(i<=0)
-			continue;
-
-		skill_arrow_db[k].nameid=i;
-
-		for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){
-			skill_arrow_db[k].cre_id[y]=atoi(split[x]);
-			skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]);
+		int l = atoi(p);
+		if( l == 99 ) // magic value?
+		{
+			skill_db[i].ammo = 0xffffffff;
+			break;
 		}
-		k++;
-		if(k >= MAX_SKILL_ARROW_DB)
+		else if( l ) // 0 not allowed?
+			skill_db[i].ammo |= 1<<l;
+		p = strchr(p,':');
+		if( !p )
 			break;
+		p++;
+	}
+	skill_split_atoi(split[9],skill_db[i].ammo_qty);
+
+	if(      strcmpi(split[10],"hiding")==0 ) skill_db[i].state = ST_HIDING;
+	else if( strcmpi(split[10],"cloaking")==0 ) skill_db[i].state = ST_CLOAKING;
+	else if( strcmpi(split[10],"hidden")==0 ) skill_db[i].state = ST_HIDDEN;
+	else if( strcmpi(split[10],"riding")==0 ) skill_db[i].state = ST_RIDING;
+	else if( strcmpi(split[10],"falcon")==0 ) skill_db[i].state = ST_FALCON;
+	else if( strcmpi(split[10],"cart")==0 ) skill_db[i].state = ST_CART;
+	else if( strcmpi(split[10],"shield")==0 ) skill_db[i].state = ST_SHIELD;
+	else if( strcmpi(split[10],"sight")==0 ) skill_db[i].state = ST_SIGHT;
+	else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[i].state = ST_EXPLOSIONSPIRITS;
+	else if( strcmpi(split[10],"cartboost")==0 ) skill_db[i].state = ST_CARTBOOST;
+	else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state = ST_RECOV_WEIGHT_RATE;
+	else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state = ST_MOVE_ENABLE;
+	else if( strcmpi(split[10],"water")==0 ) skill_db[i].state = ST_WATER;
+	else skill_db[i].state = ST_NONE;
+	
+	skill_split_atoi(split[11],skill_db[i].spiritball);
+	for( j = 0; j < 10; j++ ) {
+		skill_db[i].itemid[j] = atoi(split[12+ 2*j]);
+		skill_db[i].amount[j] = atoi(split[13+ 2*j]);
 	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
 
-	// load 'abra_db.txt'
-	memset(skill_abra_db,0,sizeof(skill_abra_db));
-	sprintf(path, "%s/abra_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
-	}
-	k = 0;
-	while(fgets(line, sizeof(line), fp))
-	{
-		char *split[16];
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		memset(split,0,sizeof(split));
-		j = skill_split_str(line,split,4);
-		if( j < 4 )
-			continue;
-		i=atoi(split[0]);
-		if(i<=0)
-			continue;
+	return true;
+}
 
-		skill_abra_db[i].req_lv=atoi(split[2]);
-		skill_abra_db[i].per=atoi(split[3]);
+static bool skill_parse_row_castdb(char* split[], int columns, int current)
+{// SkillID,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
+	
+	skill_split_atoi(split[1],skill_db[i].cast);
+	skill_split_atoi(split[2],skill_db[i].delay);
+	skill_split_atoi(split[3],skill_db[i].walkdelay);
+	skill_split_atoi(split[4],skill_db[i].upkeep_time);
+	skill_split_atoi(split[5],skill_db[i].upkeep_time2);
 
-		k++;
-		if(k >= MAX_SKILL_ABRA_DB)
-			break;
-	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+	return true;
+}
 
-	// load 'skill_castnodex_db.txt'
-	sprintf(path, "%s/skill_castnodex_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
-	}
-	while(fgets(line, sizeof(line), fp))
-	{
-		char *split[50];
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		memset(split,0,sizeof(split));
-		j = skill_split_str(line,split,3);
-		if( j < 2 ) //3rd is optional
-			continue;
-		i = atoi(split[0]);
-		i = skill_get_index(i);
-		if(i == 0) // invalid skill id
-			continue;
+static bool skill_parse_row_unitdb(char* split[], int columns, int current)
+{// ID,unit ID,unit ID 2,layout,range,interval,target,flag
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
+	
+	skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
+	skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
+	skill_split_atoi(split[3],skill_db[i].unit_layout_type);
+	skill_split_atoi(split[4],skill_db[i].unit_range);
+	skill_db[i].unit_interval = atoi(split[5]);
+
+	if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target = BCT_NOENEMY;
+	else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target = BCT_NOENEMY;
+	else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target = BCT_PARTY;
+	else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target = BCT_PARTY|BCT_GUILD;
+	else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target = BCT_ALL;
+	else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target = BCT_ENEMY;
+	else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target = BCT_SELF;
+	else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target = BCT_NOONE;
+	else skill_db[i].unit_target = strtol(split[6],NULL,16);
+
+	skill_db[i].unit_flag = strtol(split[7],NULL,16);
+
+	if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+		skill_db[i].unit_target = BCT_NOENEMY;
+
+	//By default, target just characters.
+	skill_db[i].unit_target |= BL_CHAR;
+	if (skill_db[i].unit_flag&UF_NOPC)
+		skill_db[i].unit_target &= ~BL_PC;
+	if (skill_db[i].unit_flag&UF_NOMOB)
+		skill_db[i].unit_target &= ~BL_MOB;
+	if (skill_db[i].unit_flag&UF_SKILL)
+		skill_db[i].unit_target |= BL_SKILL;
 
-		skill_split_atoi(split[1],skill_db[i].castnodex);
-		if (!split[2])
-			continue;
-		skill_split_atoi(split[2],skill_db[i].delaynodex);
-	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+	return true;
+}
 
-	// load 'skill_nocast_db.txt'
-	sprintf(path, "%s/skill_nocast_db.txt", db_path);
-	fp=fopen(path,"r");
-	if(fp==NULL){
-		ShowError("can't read %s\n", path);
-		return 1;
+static bool skill_parse_row_producedb(char* split[], int columns, int current)
+{// ProduceItemID,ItemLV,RequireSkill,RequireSkillLv,MaterialID1,MaterialAmount1,......
+	int x,y;
+
+	int i = atoi(split[0]);
+	if( !i )
+		return false;
+	if( current == MAX_SKILL_PRODUCE_DB )
+		return false;
+
+	skill_produce_db[current].nameid = i;
+	skill_produce_db[current].itemlv = atoi(split[1]);
+	skill_produce_db[current].req_skill = atoi(split[2]);
+	skill_produce_db[current].req_skill_lv = atoi(split[3]);
+	
+	for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ )
+	{
+		skill_produce_db[current].mat_id[y] = atoi(split[x]);
+		skill_produce_db[current].mat_amount[y] = atoi(split[x+1]);
 	}
-	k = 0;
-	while(fgets(line, sizeof(line), fp))
+
+	if( current == MAX_SKILL_PRODUCE_DB-1 )
+		ShowWarning("Reached the max number of produce_db entries (%d), consider raising the value of MAX_SKILL_PRODUCE_DB and recompile.\n", MAX_SKILL_PRODUCE_DB);
+
+	return true;
+}
+
+static bool skill_parse_row_createarrowdb(char* split[], int columns, int current)
+{// SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5
+	int x,y;
+
+	int i = atoi(split[0]);
+	if( !i )
+		return false;
+	if( current == MAX_SKILL_ARROW_DB )
+		return false;
+
+	skill_arrow_db[current].nameid = i;
+	
+	for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ )
 	{
-		char *split[16];
-		if(line[0]=='/' && line[1]=='/')
-			continue;
-		memset(split,0,sizeof(split));
-		j = skill_split_str(line,split,2);
-		if( j < 2 )
-			continue;
-		i = atoi(split[0]);
-		i = skill_get_index(i);
-		if(i == 0) // invalid skill id
-			continue;
-		skill_db[i].nocast|=atoi(split[1]);
-		k++;
+		skill_arrow_db[current].cre_id[y] = atoi(split[x]);
+		skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]);
 	}
-	fclose(fp);
-	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
 
+	//TODO?: add capacity warning here
+
+	return true;
+}
+
+static bool skill_parse_row_abradb(char* split[], int columns, int current)
+{// SkillID,DummyName,RequiredHocusPocusLevel,Rate
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i )
+		return false;
+	if( current == MAX_SKILL_ABRA_DB )
+		return false;
+
+	skill_abra_db[i].req_lv = atoi(split[2]);
+	skill_abra_db[i].per = atoi(split[3]);
+
+	//TODO?: add capacity warning here
+
+	return true;
+}
+
+static bool skill_parse_row_castnodexdb(char* split[], int columns, int current)
+{// Skill id,Cast,Delay (optional)
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
+	
+	skill_split_atoi(split[1],skill_db[i].castnodex);
+	if( !split[2] ) // optional column
+		return false;
+	skill_split_atoi(split[2],skill_db[i].delaynodex);
+
+	return true;
+}
+
+static bool skill_parse_row_nocastdb(char* split[], int columns, int current)
+{// SkillID,Flag
+	int i = atoi(split[0]);
+	i = skill_get_index(i);
+	if( !i ) // invalid skill id
+		return false;
+
+	skill_db[i].nocast |= atoi(split[1]);
+
+	return true;
+}
+
+int skill_readdb(void)
+{
+	// init skill db structures
+	memset(skill_db,0,sizeof(skill_db));
+	memset(skill_produce_db,0,sizeof(skill_produce_db));
+	memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+	memset(skill_abra_db,0,sizeof(skill_abra_db));
+	
+	// load skill databases
+	skill_read_csvdb(db_path, "skill_db.txt", 17, skill_parse_row_skilldb);
+	safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name));
+	safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc));
+	skill_read_csvdb(db_path, "skill_require_db.txt", 17, skill_parse_row_requiredb);
+	skill_read_csvdb(db_path, "skill_cast_db.txt", 6, skill_parse_row_castdb);
+	skill_read_csvdb(db_path, "skill_unit_db.txt", 8, skill_parse_row_unitdb);
+	skill_init_unit_layout();
+	skill_read_csvdb(db_path, "produce_db.txt", 4, skill_parse_row_producedb);
+	skill_read_csvdb(db_path, "create_arrow_db.txt", 1+2, skill_parse_row_createarrowdb);
+	skill_read_csvdb(db_path, "abra_db.txt", 4, skill_parse_row_abradb);
+	skill_read_csvdb(db_path, "skill_castnodex_db.txt", 2, skill_parse_row_castnodexdb);
+	skill_read_csvdb(db_path, "skill_nocast_db.txt", 2, skill_parse_row_nocastdb);
+	
 	return 0;
 }
 

+ 1 - 1
src/map/skill.h

@@ -61,7 +61,7 @@
 
 // ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX
 struct s_skill_db {
-	char name[20];
+	char name[NAME_LENGTH];
 	char desc[40];
 	int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max;
 	int num[MAX_SKILL_LEVEL];