summaryrefslogtreecommitdiff
path: root/src/map/pc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/pc.c')
-rw-r--r--src/map/pc.c378
1 files changed, 238 insertions, 140 deletions
diff --git a/src/map/pc.c b/src/map/pc.c
index 7ad0ecfe4..94121eba9 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -61,15 +61,13 @@ struct fame_list taekwon_fame_list[MAX_FAME_LIST];
static unsigned short equip_pos[EQI_MAX]={EQP_ACC_L,EQP_ACC_R,EQP_SHOES,EQP_GARMENT,EQP_HEAD_LOW,EQP_HEAD_MID,EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_AMMO};
#define MOTD_LINE_SIZE 128
-char motd_text[MOTD_LINE_SIZE][256]; // Message of the day buffer [Valaris]
+static char motd_text[MOTD_LINE_SIZE][CHAT_SIZE_MAX]; // Message of the day buffer [Valaris]
struct duel duel_list[MAX_DUEL];
int duel_count = 0;
-extern int item_delays; // [Paradox924X]
-
//Links related info to the sd->hate_mob[]/sd->feel_map[] entries
-const struct sg_data sg_info[3] = {
+const struct sg_data sg_info[MAX_PC_FEELHATE] = {
{ SG_SUN_ANGER, SG_SUN_BLESS, SG_SUN_COMFORT, "PC_FEEL_SUN", "PC_HATE_MOB_SUN", is_day_of_sun },
{ SG_MOON_ANGER, SG_MOON_BLESS, SG_MOON_COMFORT, "PC_FEEL_MOON", "PC_HATE_MOB_MOON", is_day_of_moon },
{ SG_STAR_ANGER, SG_STAR_BLESS, SG_STAR_COMFORT, "PC_FEEL_STAR", "PC_HATE_MOB_STAR", is_day_of_star }
@@ -532,11 +530,6 @@ int pc_setinventorydata(struct map_session_data *sd)
for(i=0;i<MAX_INVENTORY;i++) {
id = sd->status.inventory[i].nameid;
sd->inventory_data[i] = id?itemdb_search(id):NULL;
- if(sd->inventory_data[i] && sd->inventory_data[i]->delay > 0) { // Load delays
- sd->item_delay[item_delays].nameid = sd->inventory_data[i]->nameid;
- sd->item_delay[item_delays].tick = 0;
- ++item_delays;
- }
}
return 0;
}
@@ -612,7 +605,7 @@ int pc_setequipindex(struct map_session_data *sd)
if( sd->status.inventory[i].equip & EQP_HAND_L )
{
- if( sd->inventory_data[i] && sd->inventory_data[i]->type == 4 )
+ if( sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON )
sd->weapontype2 = sd->inventory_data[i]->look;
else
sd->weapontype2 = 0;
@@ -847,6 +840,23 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
sd->class_ = MAPID_NOVICE;
} else
sd->class_ = i;
+
+ // Checks and fixes to character status data, that are required
+ // in case of configuration change or stuff, which cannot be
+ // checked on char-server.
+ if( sd->status.hair < MIN_HAIR_STYLE || sd->status.hair > MAX_HAIR_STYLE )
+ {
+ sd->status.hair = MIN_HAIR_STYLE;
+ }
+ if( sd->status.hair_color < MIN_HAIR_COLOR || sd->status.hair_color > MAX_HAIR_COLOR )
+ {
+ sd->status.hair_color = MIN_HAIR_COLOR;
+ }
+ if( sd->status.clothes_color < MIN_CLOTH_COLOR || sd->status.clothes_color > MAX_CLOTH_COLOR )
+ {
+ sd->status.clothes_color = MIN_CLOTH_COLOR;
+ }
+
//Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation.
if(!sd->status.hp) pc_setdead(sd);
sd->state.connect_new = 1;
@@ -910,11 +920,11 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
sd->hate_mob[i] = -1;
// 位置の設定
- if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, 0)) != 0) {
+ if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, CLR_OUTSIGHT)) != 0) {
ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i);
// try warping to a default map instead (church graveyard)
- if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, 0) != 0) {
+ if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, CLR_OUTSIGHT) != 0) {
// if we fail again
clif_authfail_fd(sd->fd, 0);
return false;
@@ -1037,7 +1047,7 @@ int pc_reg_received(struct map_session_data *sd)
}
//SG map and mob read [Komurka]
- for(i=0;i<3;i++) //for now - someone need to make reading from txt/sql
+ for(i=0;i<MAX_PC_FEELHATE;i++) //for now - someone need to make reading from txt/sql
{
if ((j = pc_readglobalreg(sd,sg_info[i].feel_var))!=0) {
sd->feel_map[i].index = j;
@@ -1206,7 +1216,7 @@ int pc_calc_skilltree(struct map_session_data *sd)
f = 1;
if(!battle_config.skillfree) {
- for(j = 0; j < 5; j++) {
+ for(j = 0; j < MAX_PC_SKILL_REQUIRE; j++) {
if((k=skill_tree[c][i].need[j].id))
{
if (!sd->status.skill[k].id || sd->status.skill[k].flag == 13)
@@ -1303,7 +1313,7 @@ static void pc_check_skilltree(struct map_session_data *sd, int skill)
if( sd->status.skill[id].id ) //Already learned
continue;
- for( j = 0; j < 5; j++ )
+ for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ )
{
if( (k = skill_tree[c][i].need[j].id) )
{
@@ -1439,7 +1449,7 @@ int pc_disguise(struct map_session_data *sd, int class_)
if (sd->bl.prev != NULL) {
pc_stop_walking(sd, 0);
- clif_clearunit_area(&sd->bl, 0);
+ clif_clearunit_area(&sd->bl, CLR_OUTSIGHT);
}
if (!class_) {
@@ -1507,13 +1517,7 @@ static int pc_bonus_autospell_onskill(struct s_autospell *spell, int max, short
for( i = 0; i < max && spell[i].id; i++ )
{
- if( spell[i].flag == src_skill && spell[i].id == id && spell[i].lv == lv && (spell[i].card_id == card_id || spell[i].rate <= 0 || rate < 0) )
- {
- if( !battle_config.autospell_stacking && spell[i].rate > 0 && rate > 0 )
- return 0;
- rate += spell[i].rate;
- break;
- }
+ ; // each autospell works independently
}
if( i == max )
@@ -2358,6 +2362,10 @@ int pc_bonus(struct map_session_data *sd,int type,int val)
if(sd->state.lr_flag != 2)
sd->add_heal2_rate += val;
break;
+ case SP_ADD_ITEM_HEAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->itemhealrate2 += val;
+ break;
default:
ShowWarning("pc_bonus: unknown type %d %d !\n",type,val);
break;
@@ -3195,6 +3203,9 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount)
nullpo_ret(sd);
+ if(amount > MAX_AMOUNT)
+ return ADDITEM_OVERAMOUNT;
+
if(!itemdb_isstackable(nameid))
return ADDITEM_NEW;
@@ -3206,8 +3217,6 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount)
}
}
- if(amount > MAX_AMOUNT)
- return ADDITEM_OVERAMOUNT;
return ADDITEM_NEW;
}
@@ -3536,7 +3545,7 @@ int pc_isUseitem(struct map_session_data *sd,int n)
if( item == NULL )
return 0;
//Not consumable item
- if( item->type != IT_HEALING && item->type != IT_USABLE )
+ if( item->type != IT_HEALING && item->type != IT_USABLE && item->type != IT_CASH )
return 0;
if( !item->script ) //if it has no script, you can't really consume it!
return 0;
@@ -3662,7 +3671,7 @@ int pc_isUseitem(struct map_session_data *sd,int n)
*------------------------------------------*/
int pc_useitem(struct map_session_data *sd,int n)
{
- unsigned int delay, tick = gettick();
+ unsigned int tick = gettick();
int amount, i, nameid;
struct script_code *script;
@@ -3691,13 +3700,6 @@ int pc_useitem(struct map_session_data *sd,int n)
// Store information for later use before it is lost (via pc_delitem) [Paradox924X]
nameid = sd->inventory_data[n]->nameid;
- delay = sd->inventory_data[n]->delay;
-
- if( sd->inventory_data[n]->delay > 0 ) { // Check if there is a delay on this item [Paradox924X]
- ARR_FIND(0, item_delays, i, sd->item_delay[i].nameid == nameid);
- if( i < item_delays && DIFF_TICK(sd->item_delay[i].tick, tick) > 0 )
- return 0; // Delay has not expired yet
- }
//Since most delay-consume items involve using a "skill-type" target cursor,
//perform a skill-use check before going through. [Skotlex]
@@ -3706,6 +3708,27 @@ int pc_useitem(struct map_session_data *sd,int n)
if( sd->inventory_data[n]->flag.delay_consume && ( sd->ud.skilltimer != -1 /*|| !status_check_skilluse(&sd->bl, &sd->bl, ALL_RESURRECTION, 0)*/ ) )
return 0;
+ if( sd->inventory_data[n]->delay > 0 ) { // Check if there is a delay on this item [Paradox924X]
+ ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == nameid || !sd->item_delay[i].nameid);
+ if( i < MAX_ITEMDELAYS )
+ {
+ if( sd->item_delay[i].nameid )
+ {// found
+ if( DIFF_TICK(sd->item_delay[i].tick, tick) > 0 )
+ return 0; // Delay has not expired yet
+ }
+ else
+ {// not yet used item (all slots are initially empty)
+ sd->item_delay[i].nameid = nameid;
+ }
+ sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay;
+ }
+ else
+ {// should not happen
+ ShowError("pc_useitem: Exceeded item delay array capacity! (nameid=%d, char_id=%d)\n", nameid, sd->status.char_id);
+ }
+ }
+
sd->itemid = sd->status.inventory[n].nameid;
sd->itemindex = n;
if(sd->catch_target_class != -1) //Abort pet catching.
@@ -3741,10 +3764,8 @@ int pc_useitem(struct map_session_data *sd,int n)
//Update item use time.
sd->canuseitem_tick = tick + battle_config.item_use_interval;
- if( itemdb_iscashfood(sd->status.inventory[n].nameid) )
+ if( itemdb_iscashfood(nameid) )
sd->canusecashfood_tick = tick + battle_config.cashfood_use_interval;
- if( delay > 0 && i < item_delays )
- sd->item_delay[i].tick = tick + delay;
run_script(script,0,sd->bl.id,fake_nd->bl.id);
potion_flag = 0;
@@ -4038,7 +4059,7 @@ int pc_steal_coin(struct map_session_data *sd,struct block_list *target)
* 1 - Invalid map index.
* 2 - Map not in this map-server, and failed to locate alternate map-server.
*------------------------------------------*/
-int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, uint8 clrtype)
+int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype)
{
struct party_data *p;
int m;
@@ -4196,7 +4217,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
/*==========================================
* PCのランダムワ?プ
*------------------------------------------*/
-int pc_randomwarp(struct map_session_data *sd, int type)
+int pc_randomwarp(struct map_session_data *sd, clr_type type)
{
int x,y,i=0;
int m;
@@ -4219,6 +4240,45 @@ int pc_randomwarp(struct map_session_data *sd, int type)
return 0;
}
+
+/// Warps one player to another.
+/// @param sd player to warp.
+/// @param pl_sd player to warp to.
+int pc_warpto(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ if( map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) )
+ {
+ return -2;
+ }
+
+ if( map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) )
+ {
+ return -3;
+ }
+
+ return pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, CLR_TELEPORT);
+}
+
+
+/// Recalls one player to another.
+/// @param sd player to warp to.
+/// @param pl_sd player to warp.
+int pc_recall(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ if( map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) )
+ {
+ return -2;
+ }
+
+ if( map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) )
+ {
+ return -3;
+ }
+
+ return pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN);
+}
+
+
/*==========================================
* Records a memo point at sd's current position
* pos - entry to replace, (-1: shift oldest entry out)
@@ -4704,7 +4764,7 @@ int pc_follow_timer(int tid, unsigned int tick, int id, intptr data)
if (!check_distance_bl(&sd->bl, tbl, 5))
unit_walktobl(&sd->bl, tbl, 5, 0);
} else
- pc_setpos(sd, map_id2index(tbl->m), tbl->x, tbl->y, 3);
+ pc_setpos(sd, map_id2index(tbl->m), tbl->x, tbl->y, CLR_TELEPORT);
}
sd->followtimer = add_timer(
tick + 1000, // increase time a bit to loosen up map's load
@@ -4767,6 +4827,7 @@ int pc_checkbaselevelup(struct map_session_data *sd)
clif_updatestatus(sd,SP_STATUSPOINT);
clif_updatestatus(sd,SP_BASELEVEL);
+ clif_updatestatus(sd,SP_BASEEXP);
clif_updatestatus(sd,SP_NEXTBASEEXP);
status_calc_pc(sd,0);
status_percent_heal(&sd->bl,100,100);
@@ -4781,7 +4842,7 @@ int pc_checkbaselevelup(struct map_session_data *sd)
if (sd->state.snovice_dead_flag)
sd->state.snovice_dead_flag = 0; //Reenable steelbody resurrection on dead.
} else
- if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON || (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR)
+ if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON )
{
sc_start(&sd->bl,status_skill2sc(AL_INCAGI),100,10,600000);
sc_start(&sd->bl,status_skill2sc(AL_BLESSING),100,10,600000);
@@ -4814,6 +4875,7 @@ int pc_checkjoblevelup(struct map_session_data *sd)
} while ((next=pc_nextjobexp(sd)) > 0 && sd->status.job_exp >= next);
clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_JOBEXP);
clif_updatestatus(sd,SP_NEXTJOBEXP);
clif_updatestatus(sd,SP_SKILLPOINT);
status_calc_pc(sd,0);
@@ -5272,8 +5334,8 @@ int pc_resetlvl(struct map_session_data* sd,int type)
sd->status.skill_point=0;
sd->status.base_level=1;
sd->status.job_level=1;
- sd->status.base_exp=sd->status.base_exp=0;
- sd->status.job_exp=sd->status.job_exp=0;
+ sd->status.base_exp=0;
+ sd->status.job_exp=0;
if(sd->sc.option !=0)
sd->sc.option = 0;
@@ -5317,6 +5379,8 @@ int pc_resetlvl(struct map_session_data* sd,int type)
clif_updatestatus(sd,SP_BASELEVEL);
clif_updatestatus(sd,SP_JOBLEVEL);
clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
clif_updatestatus(sd,SP_NEXTBASEEXP);
clif_updatestatus(sd,SP_NEXTJOBEXP);
clif_updatestatus(sd,SP_SKILLPOINT);
@@ -5498,7 +5562,7 @@ int pc_resetfeel(struct map_session_data* sd)
int i;
nullpo_ret(sd);
- for (i=0; i<3; i++)
+ for (i=0; i<MAX_PC_FEELHATE; i++)
{
sd->feel_map[i].m = -1;
sd->feel_map[i].index = 0;
@@ -5563,7 +5627,7 @@ int pc_skillheal2_bonus(struct map_session_data *sd, int skill_num)
return bonus;
}
-void pc_respawn(struct map_session_data* sd, uint8 clrtype)
+void pc_respawn(struct map_session_data* sd, clr_type clrtype)
{
if( !pc_isdead(sd) )
return; // not applicable
@@ -5582,7 +5646,7 @@ static int pc_respawn_timer(int tid, unsigned int tick, int id, intptr data)
if( sd != NULL )
{
sd->pvp_point=0;
- pc_respawn(sd,0);
+ pc_respawn(sd,CLR_OUTSIGHT);
}
return 0;
@@ -5656,7 +5720,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
}
pc_setglobalreg(sd,"PC_DIE_COUNTER",sd->die_counter+1);
- pc_setglobalreg(sd,"killerrid",src?src->id:0);
+ pc_setparam(sd, SP_KILLERRID, src?src->id:0);
if( sd->state.bg_id )
{
struct battleground_data *bg;
@@ -5710,7 +5774,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
if (src && src->type == BL_PC)
{
struct map_session_data *ssd = (struct map_session_data *)src;
- pc_setglobalreg(ssd, "killedrid", sd->bl.id);
+ pc_setparam(ssd, SP_KILLEDRID, sd->bl.id);
npc_script_event(ssd, NPCE_KILLPC);
if (battle_config.pk_mode&2) {
@@ -5966,6 +6030,8 @@ int pc_readparam(struct map_session_data* sd,int type)
case SP_KARMA: val = sd->status.karma; break;
case SP_MANNER: val = sd->status.manner; break;
case SP_FAME: val = sd->status.fame; break;
+ case SP_KILLERRID: val = sd->killerrid; break;
+ case SP_KILLEDRID: val = sd->killedrid; break;
}
return val;
@@ -5992,11 +6058,15 @@ int pc_setparam(struct map_session_data *sd,int type,int val)
}
sd->status.base_level = (unsigned int)val;
sd->status.base_exp = 0;
- clif_updatestatus(sd, SP_BASELEVEL);
+ // clif_updatestatus(sd, SP_BASELEVEL); // Gets updated at the bottom
clif_updatestatus(sd, SP_NEXTBASEEXP);
clif_updatestatus(sd, SP_STATUSPOINT);
clif_updatestatus(sd, SP_BASEEXP);
status_calc_pc(sd, 0);
+ if(sd->status.party_id)
+ {
+ party_send_levelup(sd);
+ }
break;
case SP_JOBLEVEL:
if ((unsigned int)val >= sd->status.job_level) {
@@ -6006,11 +6076,10 @@ int pc_setparam(struct map_session_data *sd,int type,int val)
}
sd->status.job_level = (unsigned int)val;
sd->status.job_exp = 0;
- clif_updatestatus(sd, SP_JOBLEVEL);
+ // clif_updatestatus(sd, SP_JOBLEVEL); // Gets updated at the bottom
clif_updatestatus(sd, SP_NEXTJOBEXP);
clif_updatestatus(sd, SP_JOBEXP);
status_calc_pc(sd, 0);
- clif_updatestatus(sd,type);
break;
case SP_SKILLPOINT:
sd->status.skill_point = val;
@@ -6083,6 +6152,15 @@ int pc_setparam(struct map_session_data *sd,int type,int val)
case SP_FAME:
sd->status.fame = val;
break;
+ case SP_KILLERRID:
+ sd->killerrid = val;
+ return 1;
+ case SP_KILLEDRID:
+ sd->killedrid = val;
+ return 1;
+ default:
+ ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type);
+ return 0;
}
clif_updatestatus(sd,type);
@@ -6122,6 +6200,8 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp)
// A potion produced by an Alchemist in the Fame Top 10 gets +50% effect [DracoRPG]
if (potion_flag > 1)
bonus += bonus*(potion_flag-1)*50/100;
+ //All item bonuses.
+ bonus += sd->itemhealrate2;
//Item Group bonuses
bonus += bonus*itemdb_group_bonus(sd, itemid)/100;
//Individual item bonuses.
@@ -6358,10 +6438,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val)
switch(type){
case LOOK_HAIR: //Use the battle_config limits! [Skotlex]
- if (val < battle_config.min_hair_style)
- val = battle_config.min_hair_style;
- else if (val > battle_config.max_hair_style)
- val = battle_config.max_hair_style;
+ val = cap_value(val, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
+
if (sd->status.hair != val)
{
sd->status.hair=val;
@@ -6383,10 +6461,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val)
sd->status.head_mid=val;
break;
case LOOK_HAIR_COLOR: //Use the battle_config limits! [Skotlex]
- if (val < battle_config.min_hair_color)
- val = battle_config.min_hair_color;
- else if (val > battle_config.max_hair_color)
- val = battle_config.max_hair_color;
+ val = cap_value(val, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
+
if (sd->status.hair_color != val)
{
sd->status.hair_color=val;
@@ -6396,10 +6472,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val)
}
break;
case LOOK_CLOTHES_COLOR: //Use the battle_config limits! [Skotlex]
- if (val < battle_config.min_cloth_color)
- val = battle_config.min_cloth_color;
- else if (val > battle_config.max_cloth_color)
- val = battle_config.max_cloth_color;
+ val = cap_value(val, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+
sd->status.clothes_color=val;
break;
case LOOK_SHIELD:
@@ -7099,10 +7173,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
if(id) {
if(id->type == IT_WEAPON) {
sd->status.shield = 0;
- if(sd->status.inventory[n].equip == EQP_HAND_L)
- sd->weapontype2 = id->look;
- else
- sd->weapontype2 = 0;
+ sd->weapontype2 = id->look;
}
else
if(id->type == IT_ARMOR) {
@@ -7934,17 +8005,61 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max)
/*==========================================
* DB reading.
* exp.txt - required experience values
- * job_db1.txt - weight, hp, sp, aspd
- * job_db2.txt - job level stat bonuses
* skill_tree.txt - skill tree for every class
* attr_fix.txt - elemental adjustment table
- * size_fix.txt - size adjustment table for weapons
- * refine_db.txt - refining data table
+ * statpoint.txt - status points per base level
*------------------------------------------*/
+static bool pc_readdb_skilltree(char* fields[], int columns, int current)
+{
+ unsigned char joblv = 0, skilllv;
+ unsigned short skillid;
+ int idx, class_;
+ unsigned int i, offset = 3, skillidx;
+
+ class_ = atoi(fields[0]);
+ skillid = (unsigned short)atoi(fields[1]);
+ skilllv = (unsigned char)atoi(fields[2]);
+
+ if(columns==4+MAX_PC_SKILL_REQUIRE*2)
+ {// job level requirement extra column
+ joblv = (unsigned char)atoi(fields[3]);
+ offset++;
+ }
+
+ if(!pcdb_checkid(class_))
+ {
+ ShowWarning("pc_readdb_skilltree: Invalid job class %d specified.\n", class_);
+ return false;
+ }
+ idx = pc_class2idx(class_);
+
+ //This is to avoid adding two lines for the same skill. [Skotlex]
+ ARR_FIND( 0, MAX_SKILL_TREE, skillidx, skill_tree[idx][skillidx].id == 0 || skill_tree[idx][skillidx].id == skillid );
+ if( skillidx == MAX_SKILL_TREE )
+ {
+ ShowWarning("pc_readdb_skilltree: Unable to load skill %hu into job %d's tree. Maximum number of skills per class has been reached.\n", skillid, class_);
+ return false;
+ }
+ else if(skill_tree[idx][skillidx].id)
+ {
+ ShowNotice("pc_readdb_skilltree: Overwriting skill %hu for job class %d.\n", skillid, class_);
+ }
+
+ skill_tree[idx][skillidx].id = skillid;
+ skill_tree[idx][skillidx].max = skilllv;
+ skill_tree[idx][skillidx].joblv = joblv;
+
+ for(i = 0; i < MAX_PC_SKILL_REQUIRE; i++)
+ {
+ skill_tree[idx][skillidx].need[i].id = atoi(fields[i*2+offset]);
+ skill_tree[idx][skillidx].need[i].lv = atoi(fields[i*2+offset+1]);
+ }
+ return true;
+}
+
int pc_readdb(void)
{
int i,j,k;
- unsigned int stat;
FILE *fp;
char line[24000],*p;
@@ -8033,53 +8148,7 @@ int pc_readdb(void)
// スキルツリ?
memset(skill_tree,0,sizeof(skill_tree));
- sprintf(line, "%s/skill_tree.txt", db_path);
- fp=fopen(line,"r");
- if(fp==NULL){
- ShowError("can't read %s\n", line);
- return 1;
- }
-
- while(fgets(line, sizeof(line), fp))
- {
- char *split[50];
- int f=0, m=3, idx;
- if(line[0]=='/' && line[1]=='/')
- continue;
- for(j=0,p=line;j<14 && p;j++){
- split[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(j<13)
- continue;
- if (j == 14) {
- f=1; // MinJobLvl has been added
- m++;
- }
- // check for bounds [celest]
- idx = atoi(split[0]);
- if(!pcdb_checkid(idx))
- continue;
- idx = pc_class2idx(idx);
- k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
- ARR_FIND( 0, MAX_SKILL_TREE, j, skill_tree[idx][j].id == 0 || skill_tree[idx][j].id == k );
- if( j == MAX_SKILL_TREE )
- {
- ShowWarning("Unable to load skill %d into job %d's tree. Maximum number of skills per class has been reached.\n", k, atoi(split[0]));
- continue;
- }
- skill_tree[idx][j].id=k;
- skill_tree[idx][j].max=atoi(split[2]);
- if (f) skill_tree[idx][j].joblv=atoi(split[3]);
-
- for(k=0;k<5;k++){
- skill_tree[idx][j].need[k].id=atoi(split[k*2+m]);
- skill_tree[idx][j].need[k].lv=atoi(split[k*2+m+1]);
- }
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","skill_tree.txt");
+ sv_readdb(db_path, "skill_tree.txt", ',', 3+MAX_PC_SKILL_REQUIRE*2, 4+MAX_PC_SKILL_REQUIRE*2, -1, &pc_readdb_skilltree);
// ?性修正テ?ブル
for(i=0;i<4;i++)
@@ -8135,7 +8204,6 @@ int pc_readdb(void)
// スキルツリ?
memset(statp,0,sizeof(statp));
i=1;
- stat = 45; // base points
sprintf(line, "%s/statpoint.txt", db_path);
fp=fopen(line,"r");
if(fp == NULL){
@@ -8144,6 +8212,7 @@ int pc_readdb(void)
} else {
while(fgets(line, sizeof(line), fp))
{
+ int stat;
if(line[0]=='/' && line[1]=='/')
continue;
if ((stat=strtoul(line,NULL,10))<0)
@@ -8157,10 +8226,9 @@ int pc_readdb(void)
ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt");
}
// generate the remaining parts of the db if necessary
- for (; i <= MAX_LEVEL; i++) {
- stat += (i+15)/5;
- statp[i] = stat;
- }
+ statp[0] = 45; // seed value
+ for (; i <= MAX_LEVEL; i++)
+ statp[i] = statp[i-1] + (i-1+15)/5;
return 0;
}
@@ -8168,30 +8236,60 @@ int pc_readdb(void)
// Read MOTD on startup. [Valaris]
int pc_read_motd(void)
{
- FILE *fp;
- int ln=0,i=0;
+ char* buf, * ptr;
+ unsigned int lines = 0, entries = 0;
+ size_t len;
+ FILE* fp;
+
+ // clear old MOTD
+ memset(motd_text, 0, sizeof(motd_text));
+
+ // read current MOTD
+ if( ( fp = fopen(motd_txt, "r") ) != NULL )
+ {
+ while( entries < MOTD_LINE_SIZE && fgets(motd_text[entries], sizeof(motd_text[entries]), fp) )
+ {
+ lines++;
+
+ buf = motd_text[entries];
- memset(motd_text,0,sizeof(motd_text));
- if ((fp = fopen(motd_txt, "r")) != NULL) {
- while ((ln < MOTD_LINE_SIZE) && fgets(motd_text[ln], sizeof(motd_text[ln])-1, fp) != NULL) {
- if(motd_text[ln][0] == '/' && motd_text[ln][1] == '/')
+ if( buf[0] == '/' && buf[1] == '/' )
+ {
continue;
- for(i=0; motd_text[ln][i]; i++) {
- if (motd_text[ln][i] == '\r' || motd_text[ln][i]== '\n') {
- if(i)
- motd_text[ln][i]=0;
- else
- motd_text[ln][0]=' ';
- ln++;
- break;
+ }
+
+ len = strlen(buf);
+
+ while( len && ( buf[len-1] == '\r' || buf[len-1] == '\n' ) )
+ {// strip trailing EOL characters
+ len--;
+ }
+
+ if( len )
+ {
+ buf[len] = 0;
+
+ if( ( ptr = strstr(buf, " :") ) != NULL && ptr-buf >= NAME_LENGTH )
+ {// crashes newer clients
+ ShowWarning("Found sequence '"CL_WHITE" :"CL_RESET"' on line '"CL_WHITE"%u"CL_RESET"' in '"CL_WHITE"%s"CL_RESET"'. This can cause newer clients to crash.\n", lines, motd_txt);
}
}
+ else
+ {// empty line
+ buf[0] = ' ';
+ buf[1] = 0;
+ }
+ entries++;
}
fclose(fp);
+
+ ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, motd_txt);
}
else
- ShowWarning("In function pc_read_motd() -> File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt);
-
+ {
+ ShowWarning("File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt);
+ }
+
return 0;
}