Converts the Skill Tree Tables file into YAML (#6070)
* Converts the Skill Tree Tables file into YAML. * Includes CSV2YAML converter. * Corrected the tree according to the client (SkillInfoList) * Removed 3rd jobs tree from pre-renewal, some required stats not being loaded by default Co-authored-by: Aleos <aleos89@users.noreply.github.com> Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
parent
8eef9f9d36
commit
b644bcfe69
@ -1,2 +0,0 @@
|
||||
//JobNo,Skill-ID,MaxLv{,BaseLvReq,JobLvReq},Prerequisite Skill-ID-1,Prerequisite Skill-ID-1-Lv,PrereqSkill-ID-2,PrereqSkill-ID-2-Lv,PrereqSkill-ID-3,PrereqSkill-ID-3-Lv,PrereqSkill-ID-4,PrereqSkill-ID-4-Lv,PrereqSkill-ID-5,PrereqSkill-ID-5-Lv//CLASS_SKILLNAME#Skill Name#
|
||||
|
40
db/import-tmpl/skill_tree.yml
Normal file
40
db/import-tmpl/skill_tree.yml
Normal file
@ -0,0 +1,40 @@
|
||||
# This file is a part of rAthena.
|
||||
# Copyright(C) 2021 rAthena Development Team
|
||||
# https://rathena.org - https://github.com/rathena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
############################################################################
|
||||
# Skill Tree Database
|
||||
###########################################################################
|
||||
#
|
||||
# Skill Tree Settings
|
||||
#
|
||||
###########################################################################
|
||||
# - Job Job name.
|
||||
# Inherit Map of job name from which Job will inherit the skill tree. (Default: null)
|
||||
# Note that Job doesn't inherit the child skills, it only inherits the skills defined in Tree of the given job name.
|
||||
# Tree: List of skills available for the job. (Default: null)
|
||||
# - Name Skill name.
|
||||
# MaxLevel Max level of the skill. Set to 0 to remove the skill.
|
||||
# Exclude Whether the skill is excluded from being inherited. (Default: false)
|
||||
# BaseLevel Minimum base level required to unlock the skill. (Default: 0)
|
||||
# JobLevel Minimum job level required to unlock the skill. (Default: 0)
|
||||
# Requires: List of skills required to unlock the skill. (Default: null)
|
||||
# - Name Skill name.
|
||||
# Level Skill level required. Set to 0 to remove the skill.
|
||||
###########################################################################
|
||||
|
||||
Header:
|
||||
Type: SKILL_TREE_DB
|
||||
Version: 1
|
File diff suppressed because it is too large
Load Diff
3578
db/pre-re/skill_tree.yml
Normal file
3578
db/pre-re/skill_tree.yml
Normal file
File diff suppressed because it is too large
Load Diff
6577
db/re/skill_tree.txt
6577
db/re/skill_tree.txt
File diff suppressed because it is too large
Load Diff
6644
db/re/skill_tree.yml
Normal file
6644
db/re/skill_tree.yml
Normal file
File diff suppressed because it is too large
Load Diff
48
db/skill_tree.yml
Normal file
48
db/skill_tree.yml
Normal file
@ -0,0 +1,48 @@
|
||||
# This file is a part of rAthena.
|
||||
# Copyright(C) 2021 rAthena Development Team
|
||||
# https://rathena.org - https://github.com/rathena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
############################################################################
|
||||
# Skill Tree Database
|
||||
###########################################################################
|
||||
#
|
||||
# Skill Tree Settings
|
||||
#
|
||||
###########################################################################
|
||||
# - Job Job name.
|
||||
# Inherit Map of job name from which Job will inherit the skill tree. (Default: null)
|
||||
# Note that Job doesn't inherit the child skills, it only inherits the skills defined in Tree of the given job name.
|
||||
# Tree: List of skills available for the job. (Default: null)
|
||||
# - Name Skill name.
|
||||
# MaxLevel Max level of the skill. Set to 0 to remove the skill.
|
||||
# Exclude Whether the skill is excluded from being inherited. (Default: false)
|
||||
# BaseLevel Minimum base level required to unlock the skill. (Default: 0)
|
||||
# JobLevel Minimum job level required to unlock the skill. (Default: 0)
|
||||
# Requires: List of skills required to unlock the skill. (Default: null)
|
||||
# - Name Skill name.
|
||||
# Level Skill level required. Set to 0 to remove the skill.
|
||||
###########################################################################
|
||||
|
||||
Header:
|
||||
Type: SKILL_TREE_DB
|
||||
Version: 1
|
||||
|
||||
Footer:
|
||||
Imports:
|
||||
- Path: db/pre-re/skill_tree.yml
|
||||
Mode: Prerenewal
|
||||
- Path: db/re/skill_tree.yml
|
||||
Mode: Renewal
|
||||
- Path: db/import/skill_tree.yml
|
20
doc/yaml/db/skill_tree.yml
Normal file
20
doc/yaml/db/skill_tree.yml
Normal file
@ -0,0 +1,20 @@
|
||||
###########################################################################
|
||||
# Skill Tree Database
|
||||
###########################################################################
|
||||
#
|
||||
# Skill Tree Settings
|
||||
#
|
||||
###########################################################################
|
||||
# - Job Job name.
|
||||
# Inherit Map of job name from which Job will inherit the skill tree. (Default: null)
|
||||
# Note that Job doesn't inherit the child skills, it only inherits the skills defined in Tree of the given job name.
|
||||
# Tree: List of skills available for the job. (Default: null)
|
||||
# - Name Skill name.
|
||||
# MaxLevel Max level of the skill. Set to 0 to remove the skill.
|
||||
# Exclude Whether the skill is excluded from being inherited. (Default: false)
|
||||
# BaseLevel Minimum base level required to unlock the skill. (Default: 0)
|
||||
# JobLevel Minimum job level required to unlock the skill. (Default: 0)
|
||||
# Requires: List of skills required to unlock the skill. (Default: null)
|
||||
# - Name Skill name.
|
||||
# Level Skill level required. Set to 0 to remove the skill.
|
||||
###########################################################################
|
@ -6058,8 +6058,6 @@ ACMD_FUNC(skilltree)
|
||||
{
|
||||
struct map_session_data *pl_sd = NULL;
|
||||
uint16 skill_id;
|
||||
int meets, j, c=0;
|
||||
struct skill_tree_entry *ent;
|
||||
nullpo_retr(-1, sd);
|
||||
|
||||
memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
|
||||
@ -6075,33 +6073,29 @@ ACMD_FUNC(skilltree)
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = pc_mapid2jobid( pc_calc_skilltree_normalize_job( pl_sd ), pl_sd->status.sex );
|
||||
int c = pc_mapid2jobid( pc_calc_skilltree_normalize_job( pl_sd ), pl_sd->status.sex );
|
||||
|
||||
sprintf(atcmd_output, msg_txt(sd,1168), job_name(c), pc_checkskill(pl_sd, NV_BASIC)); // Player is using %s skill tree (%d basic points).
|
||||
clif_displaymessage(fd, atcmd_output);
|
||||
|
||||
c = pc_class2idx(c);
|
||||
auto entry = skill_tree_db.get_skill_data(c, skill_id);
|
||||
|
||||
ARR_FIND( 0, MAX_SKILL_TREE, j, skill_tree[c][j].skill_id == 0 || skill_tree[c][j].skill_id == skill_id );
|
||||
if( j == MAX_SKILL_TREE || skill_tree[c][j].skill_id == 0 )
|
||||
{
|
||||
if (entry == nullptr) {
|
||||
clif_displaymessage(fd, msg_txt(sd,1169)); // The player cannot use that skill.
|
||||
return 0;
|
||||
}
|
||||
|
||||
ent = &skill_tree[c][j];
|
||||
bool meets = true;
|
||||
|
||||
meets = 1;
|
||||
for(j=0;j<MAX_PC_SKILL_REQUIRE;j++)
|
||||
{
|
||||
if( ent->need[j].skill_id && pc_checkskill(sd,ent->need[j].skill_id) < ent->need[j].skill_lv)
|
||||
{
|
||||
sprintf(atcmd_output, msg_txt(sd,1170), ent->need[j].skill_lv, skill_get_desc(ent->need[j].skill_id)); // Player requires level %d of skill %s.
|
||||
for (const auto &it : entry->need) {
|
||||
if (pc_checkskill(sd, it.first) < it.second) {
|
||||
sprintf(atcmd_output, msg_txt(sd,1170), it.second, skill_get_desc(it.first)); // Player requires level %d of skill %s.
|
||||
clif_displaymessage(fd, atcmd_output);
|
||||
meets = 0;
|
||||
meets = false;
|
||||
}
|
||||
}
|
||||
if (meets == 1) {
|
||||
|
||||
if (meets) {
|
||||
clif_displaymessage(fd, msg_txt(sd,1171)); // The player meets all the requirements for that skill.
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_damage_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_damage_db.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_db.yml')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_nocast_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_nocast_db.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_tree.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_tree.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\skill_tree.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\skill_tree.yml')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\spellbook_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\spellbook_db.yml')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\statpoint.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\statpoint.yml')" />
|
||||
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\status_disabled.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\status_disabled.txt')" />
|
||||
|
222
src/map/mob.cpp
222
src/map/mob.cpp
@ -3966,7 +3966,7 @@ static bool mob_clone_disabled_skills(uint16 skill_id) {
|
||||
int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, enum e_mode mode, int flag, unsigned int duration)
|
||||
{
|
||||
int mob_id;
|
||||
int i,j,inf, fd;
|
||||
int inf, fd;
|
||||
struct mob_data *md;
|
||||
struct status_data *status;
|
||||
|
||||
@ -4015,122 +4015,132 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
|
||||
sd->fd = 0;
|
||||
|
||||
//Go Backwards to give better priority to advanced skills.
|
||||
for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) {
|
||||
uint16 skill_id = skill_tree[pc_class2idx(sd->status.class_)][j].skill_id;
|
||||
uint16 sk_idx = 0;
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(sd->status.class_);
|
||||
|
||||
if (!skill_id || !(sk_idx = skill_get_index(skill_id)) || sd->status.skill[sk_idx].lv < 1 ||
|
||||
skill_get_inf2_(skill_id, { INF2_ISWEDDING, INF2_ISGUILD }) ||
|
||||
mob_clone_disabled_skills(skill_id)
|
||||
)
|
||||
continue;
|
||||
//Normal aggressive mob, disable skills that cannot help them fight
|
||||
//against players (those with flags UF_NOMOB and UF_NOPC are specific
|
||||
//to always aid players!) [Skotlex]
|
||||
if (!(flag&1) &&
|
||||
skill_get_unit_id(skill_id) &&
|
||||
skill_get_unit_flag_(skill_id, { UF_NOMOB, UF_NOPC }))
|
||||
continue;
|
||||
/**
|
||||
* The clone should be able to cast the skill (e.g. have the required weapon) bugreport:5299)
|
||||
**/
|
||||
if( !skill_check_condition_castbegin(sd,skill_id,sd->status.skill[sk_idx].lv) )
|
||||
continue;
|
||||
if( tree != nullptr && !tree->skills.empty() ){
|
||||
std::vector<uint16> skill_list;
|
||||
|
||||
std::shared_ptr<s_mob_skill> ms = std::make_shared<s_mob_skill>();
|
||||
for (const auto &it : tree->skills)
|
||||
skill_list.push_back(it.first);
|
||||
std::sort(skill_list.rbegin(), skill_list.rend());
|
||||
|
||||
ms->skill_id = skill_id;
|
||||
ms->skill_lv = sd->status.skill[sk_idx].lv;
|
||||
ms->state = MSS_ANY;
|
||||
ms->permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5%
|
||||
ms->emotion = -1;
|
||||
ms->cancel = 0;
|
||||
ms->casttime = skill_castfix(&sd->bl,skill_id, ms->skill_lv);
|
||||
ms->delay = 5000+skill_delayfix(&sd->bl,skill_id, ms->skill_lv);
|
||||
ms->msg_id = 0;
|
||||
for (const auto &it : skill_list) {
|
||||
if (db->skill.size() >= MAX_MOBSKILL)
|
||||
break;
|
||||
uint16 skill_id = it;
|
||||
uint16 sk_idx = 0;
|
||||
|
||||
inf = skill_get_inf(skill_id);
|
||||
if (inf&INF_ATTACK_SKILL) {
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
if (skill_get_range(skill_id, ms->skill_lv) > 3)
|
||||
ms->state = MSS_ANYTARGET;
|
||||
else
|
||||
ms->state = MSS_BERSERK;
|
||||
} else if(inf&INF_GROUND_SKILL) {
|
||||
if (skill_get_inf2(skill_id, INF2_ISTRAP)) { //Traps!
|
||||
ms->state = MSS_IDLE;
|
||||
ms->target = MST_AROUND2;
|
||||
ms->delay = 60000;
|
||||
} else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy
|
||||
ms->state = MSS_ANYTARGET;
|
||||
if (!skill_id || !(sk_idx = skill_get_index(skill_id)) || sd->status.skill[sk_idx].lv < 1 ||
|
||||
skill_get_inf2_(skill_id, { INF2_ISWEDDING, INF2_ISGUILD }) ||
|
||||
mob_clone_disabled_skills(skill_id)
|
||||
)
|
||||
continue;
|
||||
//Normal aggressive mob, disable skills that cannot help them fight
|
||||
//against players (those with flags UF_NOMOB and UF_NOPC are specific
|
||||
//to always aid players!) [Skotlex]
|
||||
if (!(flag&1) &&
|
||||
skill_get_unit_id(skill_id) &&
|
||||
skill_get_unit_flag_(skill_id, { UF_NOMOB, UF_NOPC }))
|
||||
continue;
|
||||
/**
|
||||
* The clone should be able to cast the skill (e.g. have the required weapon) bugreport:5299)
|
||||
**/
|
||||
if( !skill_check_condition_castbegin(sd,skill_id,sd->status.skill[sk_idx].lv) )
|
||||
continue;
|
||||
|
||||
std::shared_ptr<s_mob_skill> ms = std::make_shared<s_mob_skill>();
|
||||
|
||||
ms->skill_id = skill_id;
|
||||
ms->skill_lv = sd->status.skill[sk_idx].lv;
|
||||
ms->state = MSS_ANY;
|
||||
ms->permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5%
|
||||
ms->emotion = -1;
|
||||
ms->cancel = 0;
|
||||
ms->casttime = skill_castfix(&sd->bl,skill_id, ms->skill_lv);
|
||||
ms->delay = 5000+skill_delayfix(&sd->bl,skill_id, ms->skill_lv);
|
||||
ms->msg_id = 0;
|
||||
|
||||
inf = skill_get_inf(skill_id);
|
||||
if (inf&INF_ATTACK_SKILL) {
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
} else { //Target allies
|
||||
ms->target = MST_FRIEND;
|
||||
ms->cond1 = MSC_FRIENDHPLTMAXRATE;
|
||||
ms->cond2 = 95;
|
||||
}
|
||||
} else if (inf&INF_SELF_SKILL) {
|
||||
if (skill_get_inf2(skill_id, INF2_NOTARGETSELF)) { //auto-select target skill.
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
if (skill_get_range(skill_id, ms->skill_lv) > 3) {
|
||||
if (skill_get_range(skill_id, ms->skill_lv) > 3)
|
||||
ms->state = MSS_ANYTARGET;
|
||||
} else {
|
||||
ms->state = MSS_BERSERK;
|
||||
}
|
||||
} else { //Self skill
|
||||
ms->target = MST_SELF;
|
||||
ms->cond1 = MSC_MYHPLTMAXRATE;
|
||||
ms->cond2 = 90;
|
||||
ms->permillage = 2000;
|
||||
//Delay: Remove the stock 5 secs and add half of the support time.
|
||||
ms->delay += -5000 +(skill_get_time(skill_id, ms->skill_lv) + skill_get_time2(skill_id, ms->skill_lv))/2;
|
||||
if (ms->delay < 5000)
|
||||
ms->delay = 5000; //With a minimum of 5 secs.
|
||||
}
|
||||
} else if (inf&INF_SUPPORT_SKILL) {
|
||||
ms->target = MST_FRIEND;
|
||||
ms->cond1 = MSC_FRIENDHPLTMAXRATE;
|
||||
ms->cond2 = 90;
|
||||
if (skill_id == AL_HEAL)
|
||||
ms->permillage = 5000; //Higher skill rate usage for heal.
|
||||
else if (skill_id == ALL_RESURRECTION)
|
||||
ms->cond2 = 1;
|
||||
//Delay: Remove the stock 5 secs and add half of the support time.
|
||||
ms->delay += -5000 +(skill_get_time(skill_id, ms->skill_lv) + skill_get_time2(skill_id, ms->skill_lv))/2;
|
||||
if (ms->delay < 2000)
|
||||
ms->delay = 2000; //With a minimum of 2 secs.
|
||||
|
||||
if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self.
|
||||
ms->target = MST_SELF;
|
||||
ms->cond1 = MSC_MYHPLTMAXRATE;
|
||||
db->skill.insert(db->skill.begin() + i, ms);
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered.
|
||||
case MO_TRIPLEATTACK:
|
||||
case TF_DOUBLE:
|
||||
case GS_CHAINACTION:
|
||||
else
|
||||
ms->state = MSS_BERSERK;
|
||||
} else if(inf&INF_GROUND_SKILL) {
|
||||
if (skill_get_inf2(skill_id, INF2_ISTRAP)) { //Traps!
|
||||
ms->state = MSS_IDLE;
|
||||
ms->target = MST_AROUND2;
|
||||
ms->delay = 60000;
|
||||
} else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy
|
||||
ms->state = MSS_ANYTARGET;
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
ms->permillage = skill_id==MO_TRIPLEATTACK?(3000-ms->skill_lv*100):(ms->skill_lv*500);
|
||||
ms->delay -= 5000; //Remove the added delay as these could trigger on "all hits".
|
||||
break;
|
||||
default: //Untreated Skill
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (battle_config.mob_skill_rate!= 100)
|
||||
ms->permillage = ms->permillage*battle_config.mob_skill_rate/100;
|
||||
if (battle_config.mob_skill_delay != 100)
|
||||
ms->delay = ms->delay*battle_config.mob_skill_delay/100;
|
||||
} else { //Target allies
|
||||
ms->target = MST_FRIEND;
|
||||
ms->cond1 = MSC_FRIENDHPLTMAXRATE;
|
||||
ms->cond2 = 95;
|
||||
}
|
||||
} else if (inf&INF_SELF_SKILL) {
|
||||
if (skill_get_inf2(skill_id, INF2_NOTARGETSELF)) { //auto-select target skill.
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
if (skill_get_range(skill_id, ms->skill_lv) > 3) {
|
||||
ms->state = MSS_ANYTARGET;
|
||||
} else {
|
||||
ms->state = MSS_BERSERK;
|
||||
}
|
||||
} else { //Self skill
|
||||
ms->target = MST_SELF;
|
||||
ms->cond1 = MSC_MYHPLTMAXRATE;
|
||||
ms->cond2 = 90;
|
||||
ms->permillage = 2000;
|
||||
//Delay: Remove the stock 5 secs and add half of the support time.
|
||||
ms->delay += -5000 +(skill_get_time(skill_id, ms->skill_lv) + skill_get_time2(skill_id, ms->skill_lv))/2;
|
||||
if (ms->delay < 5000)
|
||||
ms->delay = 5000; //With a minimum of 5 secs.
|
||||
}
|
||||
} else if (inf&INF_SUPPORT_SKILL) {
|
||||
ms->target = MST_FRIEND;
|
||||
ms->cond1 = MSC_FRIENDHPLTMAXRATE;
|
||||
ms->cond2 = 90;
|
||||
if (skill_id == AL_HEAL)
|
||||
ms->permillage = 5000; //Higher skill rate usage for heal.
|
||||
else if (skill_id == ALL_RESURRECTION)
|
||||
ms->cond2 = 1;
|
||||
//Delay: Remove the stock 5 secs and add half of the support time.
|
||||
ms->delay += -5000 +(skill_get_time(skill_id, ms->skill_lv) + skill_get_time2(skill_id, ms->skill_lv))/2;
|
||||
if (ms->delay < 2000)
|
||||
ms->delay = 2000; //With a minimum of 2 secs.
|
||||
|
||||
db->skill.push_back(ms);
|
||||
++i;
|
||||
if (db->skill.size() < MAX_MOBSKILL) { //duplicate this so it also triggers on self.
|
||||
ms->target = MST_SELF;
|
||||
ms->cond1 = MSC_MYHPLTMAXRATE;
|
||||
db->skill.push_back(ms);
|
||||
}
|
||||
} else {
|
||||
switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered.
|
||||
case MO_TRIPLEATTACK:
|
||||
case TF_DOUBLE:
|
||||
case GS_CHAINACTION:
|
||||
ms->state = MSS_BERSERK;
|
||||
ms->target = MST_TARGET;
|
||||
ms->cond1 = MSC_ALWAYS;
|
||||
ms->permillage = skill_id==MO_TRIPLEATTACK?(3000-ms->skill_lv*100):(ms->skill_lv*500);
|
||||
ms->delay -= 5000; //Remove the added delay as these could trigger on "all hits".
|
||||
break;
|
||||
default: //Untreated Skill
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (battle_config.mob_skill_rate!= 100)
|
||||
ms->permillage = ms->permillage*battle_config.mob_skill_rate/100;
|
||||
if (battle_config.mob_skill_delay != 100)
|
||||
ms->delay = ms->delay*battle_config.mob_skill_delay/100;
|
||||
|
||||
db->skill.push_back(ms);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
566
src/map/pc.cpp
566
src/map/pc.cpp
@ -70,8 +70,8 @@ static inline bool pc_attendance_rewarded_today( struct map_session_data* sd );
|
||||
|
||||
PlayerStatPointDatabase statpoint_db;
|
||||
|
||||
// h-files are for declarations, not for implementations... [Shinomori]
|
||||
struct skill_tree_entry skill_tree[CLASS_COUNT][MAX_SKILL_TREE];
|
||||
SkillTreeDatabase skill_tree_db;
|
||||
|
||||
// timer for night.day implementation
|
||||
int day_timer_tid = INVALID_TIMER;
|
||||
int night_timer_tid = INVALID_TIMER;
|
||||
@ -2085,6 +2085,7 @@ void pc_calc_skilltree(struct map_session_data *sd)
|
||||
ShowError( "pc_calc_skilltree: Unable to normalize job %s(%d) for character %s (%d:%d)\n", job_name( sd->status.class_ ), sd->status.class_, sd->status.name, sd->status.account_id, sd->status.char_id );
|
||||
return;
|
||||
}
|
||||
uint16 job_id = class_;
|
||||
class_ = pc_class2idx(class_);
|
||||
|
||||
for (const auto &skill : skill_db) {
|
||||
@ -2125,19 +2126,18 @@ void pc_calc_skilltree(struct map_session_data *sd)
|
||||
|
||||
// Removes Taekwon Ranker skill bonus
|
||||
if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) {
|
||||
uint16 c_ = pc_class2idx(JOB_TAEKWON);
|
||||
|
||||
for (uint16 i = 0; i < MAX_SKILL_TREE; i++) {
|
||||
uint16 sk_id = skill_tree[c_][i].skill_id;
|
||||
uint16 sk_idx = 0;
|
||||
|
||||
if (!sk_id || !(sk_idx = skill_get_index(skill_tree[c_][i].skill_id)))
|
||||
continue;
|
||||
|
||||
if (sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[sk_idx].flag != SKILL_FLAG_PERM_GRANTED) {
|
||||
if (sk_id == NV_BASIC || sk_id == NV_FIRSTAID || sk_id == WE_CALLBABY)
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(JOB_TAEKWON);
|
||||
|
||||
if (tree != nullptr && !tree->skills.empty()) {
|
||||
for (const auto &it : tree->skills) {
|
||||
uint16 sk_idx = skill_get_index(it.first);
|
||||
if (sk_idx == 0)
|
||||
continue;
|
||||
sd->status.skill[sk_idx].id = 0;
|
||||
if (sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[sk_idx].flag != SKILL_FLAG_PERM_GRANTED) {
|
||||
if (it.first == NV_BASIC || it.first == NV_FIRSTAID || it.first == WE_CALLBABY)
|
||||
continue;
|
||||
sd->status.skill[sk_idx].id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2146,13 +2146,16 @@ void pc_calc_skilltree(struct map_session_data *sd)
|
||||
pc_grant_allskills(sd, false);
|
||||
|
||||
int flag;
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(job_id);
|
||||
|
||||
do {
|
||||
uint16 skid = 0;
|
||||
|
||||
flag = 0;
|
||||
for (uint16 i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[class_][i].skill_id) > 0; i++) {
|
||||
if (tree == nullptr || tree->skills.empty())
|
||||
break;
|
||||
|
||||
for (const auto &skillsit : tree->skills) {
|
||||
bool fail = false;
|
||||
uint16 skid = skillsit.first;
|
||||
uint16 sk_idx = skill_get_index(skid);
|
||||
|
||||
if (sd->status.skill[sk_idx].id)
|
||||
@ -2160,39 +2163,43 @@ void pc_calc_skilltree(struct map_session_data *sd)
|
||||
|
||||
if (!battle_config.skillfree) {
|
||||
// Checking required skills
|
||||
for(uint8 j = 0; j < MAX_PC_SKILL_REQUIRE; j++) {
|
||||
uint16 sk_need_id = skill_tree[class_][i].need[j].skill_id;
|
||||
uint16 sk_need_idx = 0;
|
||||
std::shared_ptr<s_skill_tree_entry> entry = skillsit.second;
|
||||
|
||||
if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) {
|
||||
short sk_need = sk_need_id;
|
||||
if (entry != nullptr && !entry->need.empty()) {
|
||||
for (const auto &it : entry->need) {
|
||||
uint16 sk_need_id = it.first;
|
||||
uint16 sk_need_idx = skill_get_index(sk_need_id);
|
||||
|
||||
if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
|
||||
sk_need = 0; //Not learned.
|
||||
else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level
|
||||
sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
||||
else
|
||||
sk_need = pc_checkskill(sd,sk_need_id);
|
||||
if (sk_need_idx > 0) {
|
||||
uint16 sk_need = sk_need_id;
|
||||
|
||||
if (sk_need < skill_tree[class_][i].need[j].skill_lv) {
|
||||
fail = true;
|
||||
break;
|
||||
if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
|
||||
sk_need = 0; //Not learned.
|
||||
else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level
|
||||
sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
||||
else
|
||||
sk_need = pc_checkskill(sd,sk_need_id);
|
||||
|
||||
if (sk_need < it.second) {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sd->status.base_level < skill_tree[class_][i].baselv) { //We need to get the actual class in this case
|
||||
if (sd->status.base_level < entry->baselv) { //We need to get the actual class in this case
|
||||
int c_ = pc_mapid2jobid(sd->class_, sd->status.sex);
|
||||
|
||||
c_ = pc_class2idx(c_);
|
||||
if (class_ == c_ || (class_ != c_ && sd->status.base_level < skill_tree[class_][i].baselv))
|
||||
if (class_ == c_ || (class_ != c_ && sd->status.base_level < entry->baselv))
|
||||
fail = true; // base level requirement wasn't satisfied
|
||||
}
|
||||
if (sd->status.job_level < skill_tree[class_][i].joblv) { //We need to get the actual class in this case
|
||||
if (sd->status.job_level < entry->joblv) { //We need to get the actual class in this case
|
||||
int c_ = pc_mapid2jobid(sd->class_, sd->status.sex);
|
||||
|
||||
c_ = pc_class2idx(c_);
|
||||
if (class_ == c_ || (class_ != c_ && sd->status.job_level < skill_tree[class_][i].joblv))
|
||||
if (class_ == c_ || (class_ != c_ && sd->status.job_level < entry->joblv))
|
||||
fail = true; // job level requirement wasn't satisfied
|
||||
}
|
||||
}
|
||||
@ -2227,20 +2234,25 @@ void pc_calc_skilltree(struct map_session_data *sd)
|
||||
- (c > 0) to avoid grant Novice Skill Tree in case of Skill Reset (need more logic)
|
||||
- (sd->status.skill_point == 0) to wait until all skill points are assigned to avoid problems with Job Change quest. */
|
||||
|
||||
for( uint16 i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[class_][i].skill_id) > 0; i++ ) {
|
||||
uint16 sk_idx = skill_get_index(skid);
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(job_id);
|
||||
|
||||
if (sk_idx == 0)
|
||||
continue;
|
||||
if (tree != nullptr && !tree->skills.empty()) {
|
||||
for (const auto &it : tree->skills) {
|
||||
skid = it.first;
|
||||
uint16 sk_idx = skill_get_index(skid);
|
||||
|
||||
if( skill_get_inf2_(skid, { INF2_ISQUEST, INF2_ISWEDDING }) )
|
||||
continue; //Do not include Quest/Wedding skills.
|
||||
if( sd->status.skill[sk_idx].id == 0 ) {
|
||||
sd->status.skill[sk_idx].id = skid;
|
||||
sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill.
|
||||
} else if( skid != NV_BASIC )
|
||||
sd->status.skill[sk_idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[sk_idx].lv; // Remember original level
|
||||
sd->status.skill[sk_idx].lv = skill_tree_get_max(skid, sd->status.class_);
|
||||
if (sk_idx == 0)
|
||||
continue;
|
||||
|
||||
if( skill_get_inf2_(skid, { INF2_ISQUEST, INF2_ISWEDDING }) )
|
||||
continue; //Do not include Quest/Wedding skills.
|
||||
if( sd->status.skill[sk_idx].id == 0 ) {
|
||||
sd->status.skill[sk_idx].id = skid;
|
||||
sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill.
|
||||
} else if( skid != NV_BASIC )
|
||||
sd->status.skill[sk_idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[sk_idx].lv; // Remember original level
|
||||
sd->status.skill[sk_idx].lv = skill_tree_get_max(skid, sd->status.class_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2274,13 +2286,15 @@ static void pc_check_skilltree(struct map_session_data *sd)
|
||||
ShowError("pc_check_skilltree: Unable to normalize job %d for character %s (%d:%d)\n", sd->status.class_, sd->status.name, sd->status.account_id, sd->status.char_id);
|
||||
return;
|
||||
}
|
||||
c = pc_class2idx(c);
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(c);
|
||||
if (tree == nullptr || tree->skills.empty())
|
||||
return;
|
||||
|
||||
do {
|
||||
uint16 skid = 0;
|
||||
|
||||
flag = 0;
|
||||
for (int i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].skill_id) > 0; i++ ) {
|
||||
|
||||
for (const auto &skillsit : tree->skills) {
|
||||
uint16 skid = skillsit.first;
|
||||
uint16 sk_idx = skill_get_index(skid);
|
||||
bool fail = false;
|
||||
|
||||
@ -2288,30 +2302,34 @@ static void pc_check_skilltree(struct map_session_data *sd)
|
||||
continue;
|
||||
|
||||
// Checking required skills
|
||||
for (uint8 j = 0; j < MAX_PC_SKILL_REQUIRE; j++) {
|
||||
uint16 sk_need_id = skill_tree[c][i].need[j].skill_id;
|
||||
uint16 sk_need_idx = 0;
|
||||
std::shared_ptr<s_skill_tree_entry> entry = skillsit.second;
|
||||
|
||||
if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) {
|
||||
short sk_need = sk_need_id;
|
||||
if (entry != nullptr && !entry->need.empty()) {
|
||||
for (const auto &it : entry->need) {
|
||||
uint16 sk_need_id = it.first;
|
||||
uint16 sk_need_idx = skill_get_index(sk_need_id);
|
||||
|
||||
if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
|
||||
sk_need = 0; //Not learned.
|
||||
else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level
|
||||
sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
||||
else
|
||||
sk_need = pc_checkskill(sd,sk_need_id);
|
||||
if (sk_need_id > 0) {
|
||||
short sk_need = sk_need_id;
|
||||
|
||||
if (sk_need < skill_tree[c][i].need[j].skill_lv) {
|
||||
fail = true;
|
||||
break;
|
||||
if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
|
||||
sk_need = 0; //Not learned.
|
||||
else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level
|
||||
sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
|
||||
else
|
||||
sk_need = pc_checkskill(sd,sk_need_id);
|
||||
|
||||
if (sk_need < it.second) {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( fail )
|
||||
continue;
|
||||
if (sd->status.base_level < skill_tree[c][i].baselv || sd->status.job_level < skill_tree[c][i].joblv)
|
||||
if (sd->status.base_level < entry->baselv || sd->status.job_level < entry->joblv)
|
||||
continue;
|
||||
|
||||
std::shared_ptr<s_skill_db> skill = skill_db.find(skid);
|
||||
@ -7980,23 +7998,28 @@ int pc_allskillup(struct map_session_data *sd)
|
||||
|
||||
if (!pc_grant_allskills(sd, true)) {
|
||||
uint16 sk_id;
|
||||
for (i = 0; i < MAX_SKILL_TREE && (sk_id = skill_tree[pc_class2idx(sd->status.class_)][i].skill_id) > 0;i++){
|
||||
uint16 sk_idx = skill_get_index(sk_id);
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(sd->status.class_);
|
||||
|
||||
if (sk_id == 0 || sk_idx == 0)
|
||||
continue;
|
||||
if (tree != nullptr && !tree->skills.empty()) {
|
||||
for (const auto &skillsit : tree->skills) {
|
||||
sk_id = skillsit.first;
|
||||
uint16 sk_idx = skill_get_index(sk_id);
|
||||
|
||||
std::shared_ptr<s_skill_db> skill = skill_db.find(sk_id);
|
||||
if (sk_idx == 0)
|
||||
continue;
|
||||
|
||||
if (
|
||||
(skill->inf2[INF2_ISQUEST] && !battle_config.quest_skill_learn) ||
|
||||
((skill->inf2[INF2_ISWEDDING] || skill->inf2[INF2_ISSPIRIT])) ||
|
||||
sk_id == SG_DEVIL
|
||||
)
|
||||
continue; //Cannot be learned normally.
|
||||
std::shared_ptr<s_skill_db> skill = skill_db.find(sk_id);
|
||||
|
||||
sd->status.skill[sk_idx].id = sk_id;
|
||||
sd->status.skill[sk_idx].lv = skill_tree_get_max(sk_id, sd->status.class_); // celest
|
||||
if (
|
||||
(skill->inf2[INF2_ISQUEST] && !battle_config.quest_skill_learn) ||
|
||||
((skill->inf2[INF2_ISWEDDING] || skill->inf2[INF2_ISSPIRIT])) ||
|
||||
sk_id == SG_DEVIL
|
||||
)
|
||||
continue; //Cannot be learned normally.
|
||||
|
||||
sd->status.skill[sk_idx].id = sk_id;
|
||||
sd->status.skill[sk_idx].lv = skill_tree_get_max(sk_id, sd->status.class_); // celest
|
||||
}
|
||||
}
|
||||
}
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
@ -9585,13 +9608,15 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
|
||||
}
|
||||
|
||||
if ( (b_class&MAPID_UPPERMASK) != (sd->class_&MAPID_UPPERMASK) ) { //Things to remove when changing class tree.
|
||||
const int class_ = pc_class2idx(sd->status.class_);
|
||||
uint16 skill_id;
|
||||
for(i = 0; i < MAX_SKILL_TREE && (skill_id = skill_tree[class_][i].skill_id) > 0; i++) {
|
||||
//Remove status specific to your current tree skills.
|
||||
enum sc_type sc = status_skill2sc(skill_id);
|
||||
if (sc > SC_COMMON_MAX && sd->sc.data[sc])
|
||||
status_change_end(&sd->bl, sc, INVALID_TIMER);
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(sd->status.class_);
|
||||
|
||||
if (tree != nullptr && !tree->skills.empty()) {
|
||||
for (const auto &skillsit : tree->skills) {
|
||||
//Remove status specific to your current tree skills.
|
||||
enum sc_type sc = status_skill2sc(skillsit.first);
|
||||
if (sc > SC_COMMON_MAX && sd->sc.data[sc])
|
||||
status_change_end(&sd->bl, sc, INVALID_TIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12073,103 +12098,304 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max)
|
||||
return i;
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
* sub DB reading.
|
||||
* Function used to read skill_tree.txt
|
||||
*------------------------------------------*/
|
||||
static bool pc_readdb_skilltree(char* fields[], int columns, int current)
|
||||
{
|
||||
uint32 baselv, joblv, baselv_max, joblv_max;
|
||||
uint16 skill_id, skill_lv, skill_lv_max;
|
||||
int idx, class_;
|
||||
unsigned int i, offset, skill_idx;
|
||||
|
||||
class_ = atoi(fields[0]);
|
||||
skill_id = (uint16)atoi(fields[1]);
|
||||
skill_lv = (uint16)atoi(fields[2]);
|
||||
std::shared_ptr<s_skill_tree_entry> SkillTreeDatabase::get_skill_data(int class_, uint16 skill_id) {
|
||||
std::shared_ptr<s_skill_tree> tree = this->find(class_);
|
||||
|
||||
if (columns == 5 + MAX_PC_SKILL_REQUIRE * 2) { // Base/Job level requirement extra columns
|
||||
baselv = (uint32)atoi(fields[3]);
|
||||
joblv = (uint32)atoi(fields[4]);
|
||||
offset = 5;
|
||||
}
|
||||
else if (columns == 3 + MAX_PC_SKILL_REQUIRE * 2) {
|
||||
baselv = joblv = 0;
|
||||
offset = 3;
|
||||
}
|
||||
else {
|
||||
ShowWarning("pc_readdb_skilltree: Invalid number of colums in skill %hu of job %d's tree.\n", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
if (tree != nullptr)
|
||||
return util::umap_find(tree->skills, skill_id);
|
||||
|
||||
if(!pcdb_checkid(class_))
|
||||
{
|
||||
ShowWarning("pc_readdb_skilltree: Invalid job class %d specified.\n", class_);
|
||||
return false;
|
||||
}
|
||||
idx = pc_class2idx(class_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!skill_get_index(skill_id)) {
|
||||
ShowWarning("pc_readdb_skilltree: Unable to load skill %hu into job %d's tree.\n", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
if (skill_lv > (skill_lv_max = skill_get_max(skill_id))) {
|
||||
ShowWarning("pc_readdb_skilltree: Skill %hu's level %hu exceeds job %d's max level %hu. Capping skill level.\n", skill_id, skill_lv, class_, skill_lv_max);
|
||||
skill_lv = skill_lv_max;
|
||||
}
|
||||
if (baselv > (baselv_max = job_db.get_maxBaseLv(class_))) {
|
||||
ShowWarning("pc_readdb_skilltree: Skill %hu's base level requirement %d exceeds job %d's max base level %d. Capping skill base level.\n", skill_id, baselv, class_, baselv_max);
|
||||
baselv = baselv_max;
|
||||
}
|
||||
if (joblv > (joblv_max = job_db.get_maxJobLv(class_))) {
|
||||
ShowWarning("pc_readdb_skilltree: Skill %hu's job level requirement %d exceeds job %d's max job level %d. Capping skill job level.\n", skill_id, joblv, class_, joblv_max);
|
||||
joblv = joblv_max;
|
||||
}
|
||||
const std::string SkillTreeDatabase::getDefaultLocation() {
|
||||
return std::string(db_path) + "/skill_tree.yml";
|
||||
}
|
||||
|
||||
//This is to avoid adding two lines for the same skill. [Skotlex]
|
||||
ARR_FIND( 0, MAX_SKILL_TREE, skill_idx, skill_tree[idx][skill_idx].skill_id == 0 || skill_tree[idx][skill_idx].skill_id == skill_id );
|
||||
if( skill_idx == MAX_SKILL_TREE )
|
||||
{
|
||||
ShowWarning("pc_readdb_skilltree: Unable to load skill %hu into job %d's tree. Maximum number of skills per job has been reached.\n", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
else if(skill_tree[idx][skill_idx].skill_id)
|
||||
{
|
||||
ShowNotice("pc_readdb_skilltree: Overwriting skill %hu for job %d.\n", skill_id, class_);
|
||||
/**
|
||||
* Reads and parses an entry from the skill_tree.
|
||||
* @param node: YAML node containing the entry.
|
||||
* @return count of successfully parsed rows
|
||||
*/
|
||||
uint64 SkillTreeDatabase::parseBodyNode(const YAML::Node &node) {
|
||||
std::string job_name;
|
||||
|
||||
if (!this->asString(node, "Job", job_name))
|
||||
return 0;
|
||||
|
||||
int64 constant;
|
||||
std::string job_name_constant = "JOB_" + job_name;
|
||||
|
||||
if (!script_get_constant(job_name_constant.c_str(), &constant) || !pcdb_checkid(constant)) {
|
||||
this->invalidWarning(node["Job"], "Invalid job %s.\n", job_name.c_str());
|
||||
return 0;
|
||||
}
|
||||
uint16 job_id = static_cast<uint16>(constant);
|
||||
|
||||
skill_tree[idx][skill_idx].skill_id = skill_id;
|
||||
skill_tree[idx][skill_idx].skill_lv = skill_lv;
|
||||
skill_tree[idx][skill_idx].baselv = baselv;
|
||||
skill_tree[idx][skill_idx].joblv = joblv;
|
||||
std::shared_ptr<s_skill_tree> tree = this->find(job_id);
|
||||
bool exists = tree != nullptr;
|
||||
|
||||
for(i = 0; i < MAX_PC_SKILL_REQUIRE; i++)
|
||||
{
|
||||
skill_id = (uint16)atoi(fields[i * 2 + offset]);
|
||||
skill_lv = (uint16)atoi(fields[i * 2 + offset + 1]);
|
||||
if (!exists)
|
||||
tree = std::make_shared<s_skill_tree>();
|
||||
|
||||
if (skill_id == 0) {
|
||||
if (skill_tree[idx][skill_idx].need[i].skill_id > 0) { // Remove pre-requisite
|
||||
skill_tree[idx][skill_idx].need[i].skill_id = 0;
|
||||
skill_tree[idx][skill_idx].need[i].skill_lv = 0;
|
||||
if (this->nodeExists(node, "Inherit")) {
|
||||
const YAML::Node &InheritNode = node["Inherit"];
|
||||
|
||||
for (const auto &Inheritit : InheritNode) {
|
||||
std::string inheritname = Inheritit.first.as<std::string>();
|
||||
std::string inheritname_constant = "JOB_" + inheritname;
|
||||
|
||||
if (!script_get_constant(inheritname_constant.c_str(), &constant) || !pcdb_checkid(constant)) {
|
||||
this->invalidWarning(InheritNode[inheritname], "Invalid job %s.\n", inheritname.c_str());
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (skill_id > MAX_SKILL_ID || !skill_get_index(skill_id)) {
|
||||
ShowWarning("pc_readdb_skilltree: Unable to load requirement skill %hu into job %d's tree.", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
if (skill_lv > (skill_lv_max = skill_get_max(skill_id))) {
|
||||
ShowWarning("pc_readdb_skilltree: Skill %hu's level %hu exceeds job %d's max level %hu. Capping skill level.\n", skill_id, skill_lv, class_, skill_lv_max);
|
||||
skill_lv = skill_lv_max;
|
||||
}
|
||||
|
||||
skill_tree[idx][skill_idx].need[i].skill_id = skill_id;
|
||||
skill_tree[idx][skill_idx].need[i].skill_lv = skill_lv;
|
||||
bool active;
|
||||
|
||||
if (!this->asBool(InheritNode, inheritname, active))
|
||||
return 0;
|
||||
|
||||
uint16 inherit_job = static_cast<uint16>(constant);
|
||||
|
||||
if (!active) {
|
||||
if (exists)
|
||||
util::vector_erase_if_exists(tree->inherit_job, inherit_job);
|
||||
}
|
||||
else {
|
||||
if (!util::vector_exists(tree->inherit_job, inherit_job))
|
||||
tree->inherit_job.push_back(inherit_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->nodeExists(node, "Tree")) {
|
||||
for (const auto &it : node["Tree"]) {
|
||||
std::string skill_name;
|
||||
|
||||
if (!this->asString(it, "Name", skill_name))
|
||||
return 0;
|
||||
|
||||
uint16 skill_id = skill_name2id(skill_name.c_str());
|
||||
|
||||
if (skill_id == 0) {
|
||||
this->invalidWarning(it["Name"], "Invalid skill name \"%s\".\n", skill_name.c_str());
|
||||
return 0;
|
||||
}
|
||||
if (!skill_get_index(skill_id)) {
|
||||
this->invalidWarning(it["Name"], "Unable to load skill %s into job %hu's tree.\n", skill_name.c_str(), job_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_skill_tree_entry> entry;
|
||||
bool skill_exists = tree->skills.count(skill_id) > 0;
|
||||
|
||||
if (skill_exists)
|
||||
entry = tree->skills[skill_id];
|
||||
else
|
||||
entry = std::make_shared<s_skill_tree_entry>();
|
||||
|
||||
entry->skill_id = skill_id;
|
||||
|
||||
uint16 max_lv;
|
||||
|
||||
if (!this->asUInt16(it, "MaxLevel", max_lv))
|
||||
return 0;
|
||||
|
||||
if (max_lv > MAX_SKILL_LEVEL) {
|
||||
this->invalidWarning(it["MaxLevel"], "MaxLevel exceeds the maximum skill level of %d, skipping.\n", MAX_SKILL_LEVEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 skill_lv_max = skill_get_max(skill_id);
|
||||
|
||||
if (max_lv > skill_lv_max) {
|
||||
this->invalidWarning(it["MaxLevel"], "Skill %s's level %hu exceeds the skill's max level %hu. Capping skill level.\n", skill_name.c_str(), max_lv, skill_lv_max);
|
||||
max_lv = skill_lv_max;
|
||||
}
|
||||
|
||||
// if (max_lv == 0) { // skill lvl 0 removed on loadingFinished (because of inherit)
|
||||
// if (!skill_exists || entry->skill_id.erase(skill_id) == 0)
|
||||
// this->invalidWarning(it["Name"], "Failed to erase %s, the skill doesn't exist in for job %s, skipping.\n", skill_name.c_str(), job_name.c_str());
|
||||
// continue;
|
||||
// }
|
||||
|
||||
entry->max_lv = max_lv;
|
||||
|
||||
if (this->nodeExists(it, "BaseLevel")) {
|
||||
uint32 baselv;
|
||||
|
||||
if (!this->asUInt32(it, "BaseLevel", baselv))
|
||||
return 0;
|
||||
|
||||
uint32 baselv_max = job_db.get_maxBaseLv(job_id);
|
||||
|
||||
if (baselv > baselv_max) {
|
||||
this->invalidWarning(it["BaseLevel"], "Skill %hu's base level requirement %hu exceeds job %s's max base level %d. Capping skill base level.\n",
|
||||
skill_id, baselv, job_name.c_str(), baselv_max);
|
||||
baselv = baselv_max;
|
||||
}
|
||||
entry->baselv = baselv;
|
||||
} else {
|
||||
if (!skill_exists)
|
||||
entry->baselv = 0;
|
||||
}
|
||||
|
||||
if (this->nodeExists(it, "JobLevel")) {
|
||||
uint32 joblv;
|
||||
|
||||
if (!this->asUInt32(it, "JobLevel", joblv))
|
||||
return 0;
|
||||
|
||||
uint32 joblv_max = job_db.get_maxJobLv(job_id);
|
||||
|
||||
if (joblv > joblv_max) {
|
||||
this->invalidWarning(it["JobLevel"], "Skill %hu's job level requirement %hu exceeds job %s's max job level %d. Capping skill job level.\n",
|
||||
skill_id, joblv, job_name.c_str(), joblv_max);
|
||||
joblv = joblv_max;
|
||||
}
|
||||
entry->joblv = joblv;
|
||||
} else {
|
||||
if (!skill_exists)
|
||||
entry->joblv = 0;
|
||||
}
|
||||
|
||||
if (this->nodeExists(it, "Requires")) {
|
||||
for (const auto &Requiresit : it["Requires"]) {
|
||||
if (!this->nodesExist(Requiresit, { "Name" }))
|
||||
return 0;
|
||||
|
||||
std::string skill_name_req;
|
||||
|
||||
if (!this->asString(Requiresit, "Name", skill_name_req))
|
||||
return 0;
|
||||
|
||||
uint16 skill_id_req = skill_name2id(skill_name_req.c_str());
|
||||
|
||||
if (skill_id_req == 0) {
|
||||
this->invalidWarning(Requiresit["Name"], "Invalid skill name \"%s\".\n", skill_name_req.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 lv_req;
|
||||
|
||||
if (!this->asUInt16(Requiresit, "Level", lv_req))
|
||||
return 0;
|
||||
|
||||
if (lv_req > MAX_SKILL_LEVEL) {
|
||||
this->invalidWarning(Requiresit["Level"], "Level exceeds the maximum skill level of %d, skipping.\n", MAX_SKILL_LEVEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 lv_req_max = skill_get_max(skill_id_req);
|
||||
|
||||
if (lv_req > lv_req_max) {
|
||||
this->invalidWarning(it["MaxLevel"], "Required skill %s's level %hu exceeds the skill's max level %hu. Capping skill level.\n", skill_name.c_str(), lv_req, lv_req_max);
|
||||
lv_req = lv_req_max;
|
||||
}
|
||||
|
||||
if (lv_req == 0) {
|
||||
if (entry->need.erase(skill_id_req) == 0)
|
||||
this->invalidWarning(Requiresit["Name"], "Failed to erase %s, the skill doesn't exist in for job %s, skipping.\n", skill_name_req.c_str(), job_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
entry->need[skill_id_req] = lv_req;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->nodeExists(it, "Exclude")) {
|
||||
bool exclude;
|
||||
|
||||
if (!this->asBool(it, "Exclude", exclude))
|
||||
return 0;
|
||||
|
||||
entry->exclude_inherit = exclude;
|
||||
} else {
|
||||
if (!skill_exists)
|
||||
entry->exclude_inherit = false;
|
||||
}
|
||||
|
||||
if (!skill_exists)
|
||||
tree->skills.insert({ skill_id, entry });
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
this->put(job_id, tree);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkillTreeDatabase::loadingFinished() {
|
||||
std::unordered_map<uint16, std::shared_ptr<s_skill_tree>> job_tree; // get the data from skill_tree_db before populate it
|
||||
|
||||
for (auto &data : *this) {
|
||||
if (data.second->inherit_job.empty())
|
||||
continue;
|
||||
|
||||
std::shared_ptr<s_skill_tree> skill_tree = std::make_shared<s_skill_tree>();
|
||||
|
||||
uint32 baselv_max = job_db.get_maxBaseLv(data.first);
|
||||
uint32 joblv_max = job_db.get_maxJobLv(data.first);
|
||||
|
||||
for (const auto &inherit_job : data.second->inherit_job) {
|
||||
std::shared_ptr<s_skill_tree> tree = this->find(inherit_job);
|
||||
if (tree == nullptr || tree->skills.empty())
|
||||
continue;
|
||||
|
||||
for (const auto &it : tree->skills) {
|
||||
if (it.second->exclude_inherit)
|
||||
continue;
|
||||
if (data.second->skills.count(it.first) > 0) // skill already in the skill tree
|
||||
continue;
|
||||
|
||||
if (skill_tree->skills.count(it.first) > 0) // replaced by the last inheritance
|
||||
skill_tree->skills[it.first] = it.second;
|
||||
else
|
||||
skill_tree->skills.insert({ it.first, it.second });
|
||||
std::shared_ptr<s_skill_tree_entry> skill = skill_tree->skills[it.first];
|
||||
|
||||
if (skill->baselv > baselv_max) {
|
||||
ShowWarning("SkillTreeDatabase: Skill %s (%hu)'s base level requirement %hu exceeds job %s's max base level %d. Capping skill base level.\n",
|
||||
skill_get_name(skill->skill_id), skill->skill_id, skill->baselv, job_name(data.first), baselv_max);
|
||||
skill->baselv = baselv_max;
|
||||
}
|
||||
if (skill->joblv > joblv_max) {
|
||||
ShowWarning("SkillTreeDatabase: Skill %s (%hu)'s job level requirement %hu exceeds job %s's max job level %d. Capping skill job level.\n",
|
||||
skill_get_name(skill->skill_id), skill->skill_id, skill->joblv, job_name(data.first), joblv_max);
|
||||
skill->joblv = joblv_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (skill_tree != nullptr && !skill_tree->skills.empty())
|
||||
job_tree.insert({ data.first, skill_tree });
|
||||
}
|
||||
|
||||
if (!job_tree.empty()) {
|
||||
for (auto &data : *this) {
|
||||
if (job_tree.count(data.first) == 0)
|
||||
continue;
|
||||
data.second->skills.insert(job_tree[data.first]->skills.begin(), job_tree[data.first]->skills.end());
|
||||
}
|
||||
}
|
||||
|
||||
// remove skills with max_lv = 0
|
||||
for (const auto &job : *this) {
|
||||
if (job.second->skills.empty())
|
||||
continue;
|
||||
|
||||
auto it = job.second->skills.begin();
|
||||
|
||||
while( it != job.second->skills.end() ){
|
||||
if( it->second->max_lv == 0 ){
|
||||
it = job.second->skills.erase( it );
|
||||
}else{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates base hp of player. Reference: http://irowiki.org/wiki/Max_HP
|
||||
* @param level: Base level of player
|
||||
@ -12844,9 +13070,7 @@ void pc_readdb(void) {
|
||||
}
|
||||
|
||||
// Reset and read skilltree - needs to be read after pc_readdb_job_exp to get max base and job levels
|
||||
memset(skill_tree, 0, sizeof(skill_tree));
|
||||
sv_readdb(db_path, DBPATH"skill_tree.txt", ',', 3 + MAX_PC_SKILL_REQUIRE * 2, 5 + MAX_PC_SKILL_REQUIRE * 2, -1, &pc_readdb_skilltree, 0);
|
||||
sv_readdb(db_path, DBIMPORT"/skill_tree.txt", ',', 3 + MAX_PC_SKILL_REQUIRE * 2, 5 + MAX_PC_SKILL_REQUIRE * 2, -1, &pc_readdb_skilltree, 1);
|
||||
skill_tree_db.reload();
|
||||
|
||||
statpoint_db.load();
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ enum e_log_pick_type : uint32;
|
||||
enum sc_type : int16;
|
||||
|
||||
#define MAX_PC_BONUS 50 /// Max bonus, usually used by item bonus
|
||||
#define MAX_PC_SKILL_REQUIRE 5 /// Max skill tree requirement
|
||||
#define MAX_PC_FEELHATE 3 /// Max feel hate info
|
||||
#define DAMAGELOG_SIZE_PC 100 /// Damage log
|
||||
#define MAX_SPIRITBALL 15 /// Max spirit balls
|
||||
@ -59,9 +58,6 @@ enum sc_type : int16;
|
||||
#define ATTENDANCE_COUNT_VAR "#AttendanceCounter"
|
||||
#define ACHIEVEMENTLEVEL "AchievementLevel"
|
||||
|
||||
//Update this max as necessary. 55 is the value needed for Super Baby currently
|
||||
//Raised to 105 since Expanded Super Baby needs it.
|
||||
#define MAX_SKILL_TREE 105
|
||||
//Total number of classes (for data storage)
|
||||
#define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC)
|
||||
|
||||
@ -1432,14 +1428,33 @@ int pc_mapid2jobid(uint64 class_, int sex); // Skotlex
|
||||
|
||||
const char * job_name(int class_);
|
||||
|
||||
struct skill_tree_entry {
|
||||
uint16 skill_id, skill_lv;
|
||||
struct s_skill_tree_entry {
|
||||
uint16 skill_id, max_lv;
|
||||
uint32 baselv, joblv;
|
||||
struct {
|
||||
uint16 skill_id, skill_lv;
|
||||
} need[MAX_PC_SKILL_REQUIRE];
|
||||
}; // Celest
|
||||
extern struct skill_tree_entry skill_tree[CLASS_COUNT][MAX_SKILL_TREE];
|
||||
std::unordered_map<uint16, uint16> need; /// skill_id, skill_lv
|
||||
bool exclude_inherit; // exclude the skill from inherit when loading the table
|
||||
};
|
||||
|
||||
struct s_skill_tree {
|
||||
std::vector<uint16> inherit_job;
|
||||
std::unordered_map<uint16, std::shared_ptr<s_skill_tree_entry>> skills; /// skill_id, entry
|
||||
};
|
||||
|
||||
class SkillTreeDatabase : public TypesafeYamlDatabase<uint16, s_skill_tree> {
|
||||
public:
|
||||
SkillTreeDatabase() : TypesafeYamlDatabase("SKILL_TREE_DB", 1) {
|
||||
|
||||
}
|
||||
|
||||
const std::string getDefaultLocation();
|
||||
uint64 parseBodyNode(const YAML::Node& node);
|
||||
void loadingFinished();
|
||||
|
||||
// Additional
|
||||
std::shared_ptr<s_skill_tree_entry> get_skill_data(int class_, uint16 skill_id);
|
||||
};
|
||||
|
||||
extern SkillTreeDatabase skill_tree_db;
|
||||
|
||||
struct sg_data {
|
||||
short anger_id;
|
||||
|
@ -11952,11 +11952,16 @@ BUILDIN_FUNC(sc_end_class)
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SKILL_TREE && (skill_id = skill_tree[pc_class2idx(class_)][i].skill_id) > 0; i++) {
|
||||
enum sc_type sc = status_skill2sc(skill_id);
|
||||
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(class_);
|
||||
|
||||
if (sc > SC_COMMON_MAX && sd->sc.data[sc])
|
||||
status_change_end(&sd->bl, sc, INVALID_TIMER);
|
||||
if( tree != nullptr ){
|
||||
for (const auto &it : tree->skills) {
|
||||
skill_id = it.first;
|
||||
enum sc_type sc = status_skill2sc(skill_id);
|
||||
|
||||
if (sc > SC_COMMON_MAX && sd->sc.data[sc])
|
||||
status_change_end(&sd->bl, sc, INVALID_TIMER);
|
||||
}
|
||||
}
|
||||
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
|
@ -311,14 +311,10 @@ bool skill_get_unit_flag_(uint16 skill_id, std::vector<e_skill_unit_flag> unit)
|
||||
return false;
|
||||
}
|
||||
|
||||
int skill_tree_get_max(uint16 skill_id, int b_class)
|
||||
{
|
||||
int i;
|
||||
b_class = pc_class2idx(b_class);
|
||||
|
||||
ARR_FIND( 0, MAX_SKILL_TREE, i, skill_tree[b_class][i].skill_id == 0 || skill_tree[b_class][i].skill_id == skill_id );
|
||||
if( i < MAX_SKILL_TREE && skill_tree[b_class][i].skill_id == skill_id )
|
||||
return skill_tree[b_class][i].skill_lv;
|
||||
int skill_tree_get_max(uint16 skill_id, int b_class) {
|
||||
auto skill = skill_tree_db.get_skill_data(b_class, skill_id);
|
||||
if (skill != nullptr)
|
||||
return skill->max_lv;
|
||||
else
|
||||
return skill_get_max(skill_id);
|
||||
}
|
||||
|
@ -138,6 +138,14 @@ static void mercenary_skill_txt_data(const std::string& modePath, const std::str
|
||||
sv_readdb(fixedPath.c_str(), "mercenary_skill_db.txt", ',', 3, 3, -1, mercenary_read_skilldb, false);
|
||||
}
|
||||
|
||||
// Skill tree database data to memory
|
||||
static void skilltree_txt_data(const std::string &modePath, const std::string &fixedPath) {
|
||||
skill_tree.clear();
|
||||
|
||||
if (fileExists(modePath + "/skill_tree.txt"))
|
||||
sv_readdb(modePath.c_str(), "skill_tree.txt", ',', 3 + MAX_PC_SKILL_REQUIRE * 2, 5 + MAX_PC_SKILL_REQUIRE * 2, -1, pc_readdb_skilltree, false);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
bool process( const std::string& type, uint32 version, const std::vector<std::string>& paths, const std::string& name, Func lambda, const std::string& rename = "" ){
|
||||
for( const std::string& path : paths ){
|
||||
@ -503,6 +511,21 @@ int do_init( int argc, char** argv ){
|
||||
})) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
skilltree_txt_data(path_db_mode, path_db);
|
||||
if (!process("SKILL_TREE_DB", 1, { path_db_mode }, "skill_tree", [](const std::string &path, const std::string &name_ext) -> bool {
|
||||
return pc_readdb_skilltree_yaml();
|
||||
})) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
skilltree_txt_data(path_db_import, path_db_import);
|
||||
if (!process("SKILL_TREE_DB", 1, { path_db_import }, "skill_tree", [](const std::string &path, const std::string &name_ext) -> bool {
|
||||
return pc_readdb_skilltree_yaml();
|
||||
})) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: add implementations ;-)
|
||||
|
||||
return 0;
|
||||
@ -4650,3 +4673,111 @@ static bool mercenary_readdb(char* str[], int columns, int current) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copied and adjusted from pc.cpp
|
||||
static bool pc_readdb_skilltree(char* fields[], int columns, int current) {
|
||||
uint16 baselv, joblv, offset;
|
||||
uint16 class_ = (uint16)atoi(fields[0]);
|
||||
uint16 skill_id = (uint16)atoi(fields[1]);
|
||||
|
||||
if (columns == 5 + MAX_PC_SKILL_REQUIRE * 2) { // Base/Job level requirement extra columns
|
||||
baselv = (uint16)atoi(fields[3]);
|
||||
joblv = (uint16)atoi(fields[4]);
|
||||
offset = 5;
|
||||
}
|
||||
else if (columns == 3 + MAX_PC_SKILL_REQUIRE * 2) {
|
||||
baselv = joblv = 0;
|
||||
offset = 3;
|
||||
}
|
||||
else {
|
||||
ShowWarning("pc_readdb_skilltree: Invalid number of colums in skill %hu of job %d's tree.\n", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* constant = constant_lookup(class_, "JOB_");
|
||||
if (constant == nullptr) {
|
||||
ShowWarning("pc_readdb_skilltree: Invalid job class %d specified.\n", class_);
|
||||
return false;
|
||||
}
|
||||
std::string job_name(constant);
|
||||
|
||||
std::string* skill_name = util::umap_find( aegis_skillnames, skill_id );
|
||||
|
||||
if( skill_name == nullptr ){
|
||||
ShowWarning("pc_readdb_skilltree: Unable to load skill %hu into job %d's tree.\n", skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 skill_lv = (uint16)atoi(fields[2]);
|
||||
|
||||
std::vector<s_skill_tree_entry_csv> *job = util::map_find(skill_tree, class_);
|
||||
bool exists = job != nullptr;
|
||||
|
||||
s_skill_tree_entry_csv entry;
|
||||
|
||||
entry.skill_name = *skill_name;
|
||||
entry.skill_id = skill_id;
|
||||
entry.skill_lv = skill_lv;
|
||||
entry.baselv = baselv;
|
||||
entry.joblv = joblv;
|
||||
|
||||
for (uint16 i = 0; i < MAX_PC_SKILL_REQUIRE; i++) {
|
||||
uint16 req_skill_id = (uint16)atoi(fields[i * 2 + offset]);
|
||||
skill_lv = (uint16)atoi(fields[i * 2 + offset + 1]);
|
||||
|
||||
if (req_skill_id == 0)
|
||||
continue;
|
||||
|
||||
skill_name = util::umap_find( aegis_skillnames, req_skill_id );
|
||||
|
||||
if( skill_name == nullptr ){
|
||||
ShowWarning("pc_readdb_skilltree: Unable to load requirement skill %hu into job %d's tree.", req_skill_id, class_);
|
||||
return false;
|
||||
}
|
||||
entry.need.insert({ *skill_name, skill_lv });
|
||||
}
|
||||
|
||||
if (exists)
|
||||
job->push_back(entry);
|
||||
else {
|
||||
std::vector<s_skill_tree_entry_csv> tree;
|
||||
|
||||
tree.push_back(entry);
|
||||
skill_tree.insert({ class_, tree });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pc_readdb_skilltree_yaml(void) {
|
||||
for (const auto &it : skill_tree) {
|
||||
body << YAML::BeginMap;
|
||||
body << YAML::Key << "Job" << YAML::Value << constant_lookup(it.first, "JOB_");
|
||||
body << YAML::Key << "Tree";
|
||||
body << YAML::BeginSeq;
|
||||
for (const auto &subit : it.second) {
|
||||
body << YAML::BeginMap;
|
||||
body << YAML::Key << "Name" << YAML::Value << subit.skill_name;
|
||||
body << YAML::Key << "MaxLevel" << YAML::Value << subit.skill_lv;
|
||||
if (!subit.need.empty()) {
|
||||
body << YAML::Key << "Requires";
|
||||
body << YAML::BeginSeq;
|
||||
for (const auto &terit : subit.need) {
|
||||
body << YAML::BeginMap;
|
||||
body << YAML::Key << "Name" << YAML::Value << terit.first;
|
||||
body << YAML::Key << "Level" << YAML::Value << terit.second;
|
||||
body << YAML::EndMap;
|
||||
}
|
||||
body << YAML::EndSeq;
|
||||
}
|
||||
if (subit.baselv > 0)
|
||||
body << YAML::Key << "BaseLevel" << YAML::Value << subit.baselv;
|
||||
if (subit.joblv > 0)
|
||||
body << YAML::Key << "JobLevel" << YAML::Value << subit.joblv;
|
||||
body << YAML::EndMap;
|
||||
}
|
||||
body << YAML::EndSeq;
|
||||
body << YAML::EndMap;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -16,6 +16,17 @@
|
||||
#define MAX_ARROW_RESULT 5 /// Max Arrow results/created
|
||||
#define MAX_SKILL_ARROW_DB 150 /// Max Arrow Creation DB
|
||||
#define MAX_ITEMRATIO_MOBS 10
|
||||
//Update this max as necessary. 55 is the value needed for Super Baby currently
|
||||
//Raised to 105 since Expanded Super Baby needs it.
|
||||
#define MAX_SKILL_TREE 105
|
||||
#define MAX_PC_SKILL_REQUIRE 5 /// Max skill tree requirement
|
||||
|
||||
struct s_skill_tree_entry_csv {
|
||||
std::string skill_name;
|
||||
uint16 skill_id, skill_lv, baselv, joblv;
|
||||
std::map<std::string, uint16> need; /// skill_id, skill_lv
|
||||
};
|
||||
std::map<uint16, std::vector<s_skill_tree_entry_csv>> skill_tree; /// job id (for order), entry
|
||||
|
||||
// Database to memory maps
|
||||
struct s_skill_unit_csv : s_skill_db {
|
||||
@ -495,5 +506,7 @@ static bool read_elemental_skilldb(char* str[], int columns, int current);
|
||||
static bool read_elementaldb(char* str[], int columns, int current);
|
||||
static bool mercenary_read_skilldb(char* str[], int columns, int current);
|
||||
static bool mercenary_readdb(char* str[], int columns, int current);
|
||||
static bool pc_readdb_skilltree(char* str[], int columns, int current);
|
||||
static bool pc_readdb_skilltree_yaml(void);
|
||||
|
||||
#endif /* CSV2YAML_HPP */
|
||||
|
Loading…
x
Reference in New Issue
Block a user