summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog-Trunk.txt5
-rw-r--r--src/map/atcommand.c10
-rw-r--r--src/map/battle.c6
-rw-r--r--src/map/clif.c10
-rw-r--r--src/map/skill.c696
-rw-r--r--src/map/skill.h2
6 files changed, 337 insertions, 392 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index ed953d723..62a6f4e02 100644
--- a/Changelog-Trunk.txt
+++ b/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
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 9b15ca0f6..0109cf290 100644
--- a/src/map/atcommand.c
+++ b/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;
}
diff --git a/src/map/battle.c b/src/map/battle.c
index 3a865a4a4..1d283e6a1 100644
--- a/src/map/battle.c
+++ b/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_);
diff --git a/src/map/clif.c b/src/map/clif.c
index 1d254cf8d..3bdaa3396 100644
--- a/src/map/clif.c
+++ b/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++;
}
diff --git a/src/map/skill.c b/src/map/skill.c
index 5c96ba3f2..d63269a7b 100644
--- a/src/map/skill.c
+++ b/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;
}
diff --git a/src/map/skill.h b/src/map/skill.h
index c78607b36..5ddd5b27c 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -61,7 +61,7 @@
// スキルデ?タベ?ス
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];