Explorar o código

Fixes HP/SP table issues (#6361)

* Fixes #6360.
* Adds missing HP/SP values for Rebellion, Baby Rebellion, Expanded Super Novice, Expanded Super Baby, Baby Summoner, Star Emperor, Baby Star Emperor, Soul Reaper, and Baby Soul Reaper.
* Adds official HP/SP values for Summoner, Baby Summoner, Star Emperor, Baby Star Emperor, Soul Reaper, and Baby Soul Reaper. These are missing levels 176-200 though.
* Fixes the BonusStats parser to properly check for the max job level.
* Adds several logical checks for blocking level 0 during parse.
* Adds several logical checks for skipping content over a job's max level for HP/SP and BEXP/JEXP.
* Fixes the SP values being filled against the max job level instead of base level if the HP/SP Table wasn't being used.
* The parser will now properly fill the HP/SP array with 0's so that the loadingFinished() function can properly calculate a value to insert if something is missing.
* Adds a better solution to CSV2YAML when checking for the max base level when converting HP/SP.
* Fixes the CSV2YAML generating bad job bonus stat conversions.
Thanks to @kaninhot004 and @Lemongrass3110!
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
Aleos %!s(int64=3) %!d(string=hai) anos
pai
achega
fe0d445500
Modificáronse 4 ficheiros con 750 adicións e 521 borrados
  1. 653 297
      db/re/job_basepoints.yml
  2. 0 167
      db/re/job_stats.yml
  3. 78 49
      src/map/pc.cpp
  4. 19 8
      src/tool/csv2yaml.cpp

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 653 - 297
db/re/job_basepoints.yml


+ 0 - 167
db/re/job_stats.yml

@@ -8535,24 +8535,6 @@ Body:
         Str: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
-      - Level: 63
-        Str: 1
-      - Level: 64
-        Str: 1
-      - Level: 65
-        Str: 1
-      - Level: 66
-        Str: 1
-      - Level: 67
-        Str: 1
-      - Level: 68
-        Str: 1
-      - Level: 69
-        Str: 1
-      - Level: 70
-        Str: 1
   - Jobs:
       Oboro: true
     MaxWeight: 26000
@@ -8634,20 +8616,6 @@ Body:
         Str: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
-      - Level: 63
-        Agi: 1
-      - Level: 64
-        Agi: 1
-      - Level: 65
-        Agi: 1
-      - Level: 66
-        Int: 1
-      - Level: 68
-        Vit: 1
-      - Level: 70
-        Vit: 1
   - Jobs:
       Rebellion: true
     MaxWeight: 28000
@@ -8812,8 +8780,6 @@ Body:
         Agi: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
   - Jobs:
       Baby_Summoner: true
     HpFactor: 100
@@ -8892,8 +8858,6 @@ Body:
         Agi: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
   - Jobs:
       Baby_Ninja: true
     MaxWeight: 26000
@@ -9036,18 +9000,6 @@ Body:
         Str: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
-      - Level: 63
-        Str: 1
-      - Level: 64
-        Str: 1
-      - Level: 65
-        Str: 1
-      - Level: 69
-        Agi: 1
-      - Level: 70
-        Agi: 1
   - Jobs:
       Baby_Oboro: true
     MaxWeight: 26000
@@ -9129,20 +9081,6 @@ Body:
         Str: 1
       - Level: 50
         Dex: 1
-      - Level: 59
-        Str: 1
-      - Level: 63
-        Str: 1
-      - Level: 64
-        Str: 1
-      - Level: 65
-        Str: 1
-      - Level: 66
-        Str: 1
-      - Level: 67
-        Str: 1
-      - Level: 68
-        Str: 1
   - Jobs:
       Baby_Taekwon: true
     MaxWeight: 28000
@@ -9915,108 +9853,3 @@ Body:
         Int: 1
       - Level: 59
         Int: 1
-    MaxWeight: 28000
-    HpFactor: 90
-    HpMultiplicator: 650
-    SpFactor: 470
-    BaseASPD:
-      Fist: 40
-      Dagger: 50
-      1hSword: 50
-      2hSword: 50
-      1hSpear: 50
-      2hSpear: 50
-      1hAxe: 50
-      2hAxe: 50
-      Mace: 50
-      2hMace: 50
-      Staff: 50
-      Bow: 50
-      Knuckle: 50
-      Musical: 50
-      Whip: 50
-      Book: 50
-      Katar: 50
-      Revolver: 50
-      Rifle: 50
-      Gatling: 50
-      Shotgun: 50
-      Grenade: 50
-      Huuma: 50
-      2hStaff: 50
-      Shield: 6
-    BonusStats:
-      - Level: 1
-        Str: 1
-      - Level: 2
-        Dex: 1
-      - Level: 5
-        Agi: 1
-      - Level: 7
-        Int: 1
-      - Level: 8
-        Str: 1
-      - Level: 9
-        Luk: 1
-      - Level: 11
-        Dex: 1
-      - Level: 12
-        Str: 1
-      - Level: 13
-        Agi: 1
-      - Level: 15
-        Int: 1
-      - Level: 16
-        Luk: 1
-      - Level: 17
-        Vit: 1
-      - Level: 19
-        Str: 1
-      - Level: 20
-        Dex: 1
-      - Level: 21
-        Agi: 1
-      - Level: 23
-        Str: 1
-      - Level: 24
-        Vit: 1
-      - Level: 25
-        Int: 1
-      - Level: 27
-        Dex: 1
-      - Level: 29
-        Agi: 1
-      - Level: 30
-        Dex: 1
-      - Level: 31
-        Str: 1
-      - Level: 34
-        Dex: 1
-      - Level: 35
-        Agi: 1
-      - Level: 36
-        Luk: 1
-      - Level: 37
-        Vit: 1
-      - Level: 38
-        Dex: 1
-      - Level: 39
-        Str: 1
-      - Level: 41
-        Agi: 1
-      - Level: 42
-        Vit: 1
-      - Level: 43
-        Str: 1
-      - Level: 45
-        Dex: 1
-      - Level: 47
-        Agi: 1
-      - Level: 48
-        Str: 1
-      - Level: 50
-        Dex: 1
-      - Level: 56
-        Str: 1
-      - Level: 59
-        Agi: 1

+ 78 - 49
src/map/pc.cpp

@@ -12249,9 +12249,22 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 			std::shared_ptr<s_job_info> job = job_db.find(static_cast<uint16>(job_id));
 			bool exists = job != nullptr;
 
-			if (!exists)
+			if (!exists) {
 				job = std::make_shared<s_job_info>();
 
+				job->job_bonus.resize(MAX_LEVEL);
+				std::fill(job->job_bonus.begin(), job->job_bonus.end(), std::array<uint16, PARAM_MAX> { 0 });
+
+				job->base_hp.resize(MAX_LEVEL);
+				std::fill(job->base_hp.begin(), job->base_hp.end(), 0);
+
+				job->base_sp.resize(MAX_LEVEL);
+				std::fill(job->base_sp.begin(), job->base_sp.end(), 0);
+
+				job->base_ap.resize(MAX_LEVEL);
+				std::fill(job->base_ap.begin(), job->base_ap.end(), 0);
+			}
+
 			if (this->nodeExists(node, "MaxWeight")) {
 				uint32 weight;
 
@@ -12336,34 +12349,6 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 				}
 			}
 
-			if (this->nodeExists(node, "BonusStats")) {
-				const YAML::Node &bonusNode = node["BonusStats"];
-				job->job_bonus.resize(MAX_LEVEL);
-
-				for (const YAML::Node &levelNode : bonusNode) {
-					uint16 level;
-
-					if (!this->asUInt16(levelNode, "Level", level))
-						return 0;
-
-					if (level > MAX_LEVEL) {
-						this->invalidWarning(levelNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
-						return 0;
-					}
-
-					for (uint8 idx = PARAM_STR; idx < PARAM_MAX; idx++) {
-						if (this->nodeExists(levelNode, parameter_names[idx])) {
-							int16 change;
-
-							if (!this->asInt16(levelNode, parameter_names[idx], change))
-								return 0;
-
-							job->job_bonus[level - 1][idx] = change;
-						}
-					}
-				}
-			}
-
 			if (this->nodeExists(node, "MaxStats")) {
 				const YAML::Node &statNode = node["MaxStats"];
 
@@ -12396,7 +12381,7 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 				if (!this->asUInt16(node, "MaxBaseLevel", level))
 					return 0;
 
-				if (level > MAX_LEVEL) {
+				if (level == 0 || level > MAX_LEVEL) {
 					this->invalidWarning(node["MaxBaseLevel"], "MaxBaseLevel must be between 1~MAX_LEVEL for %s, capping to MAX_LEVEL.\n", job_name.c_str());
 					level = MAX_LEVEL;
 				}
@@ -12414,7 +12399,10 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 					if (!this->asUInt16(bexpNode, "Level", level))
 						return 0;
 
-					if (level < 1 || level > MAX_LEVEL) {
+					if (level > job->max_base_level)
+						continue;
+
+					if (level == 0 || level > MAX_LEVEL) {
 						this->invalidWarning(bexpNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
 						return 0;
 					}
@@ -12436,13 +12424,12 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 				if (!this->asUInt16(node, "MaxJobLevel", level))
 					return 0;
 
-				if (level > MAX_LEVEL) {
+				if (level == 0 || level > MAX_LEVEL) {
 					this->invalidWarning(node["MaxJobLevel"], "MaxJobLevel must be between 1~MAX_LEVEL for %s, capping to MAX_LEVEL.\n", job_name.c_str());
 					level = MAX_LEVEL;
 				}
 
 				job->max_job_level = level;
-				job->job_bonus.resize(level);
 			} else {
 				if (!exists)
 					job->max_job_level = MAX_LEVEL;
@@ -12455,7 +12442,10 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 					if (!this->asUInt16(jexpNode, "Level", level))
 						return 0;
 
-					if (level < 1 || level > MAX_LEVEL) {
+					if (level > job->max_job_level)
+						continue;
+
+					if (level == 0 || level > MAX_LEVEL) {
 						this->invalidWarning(jexpNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
 						return 0;
 					}
@@ -12471,17 +12461,45 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 				}
 			}
 
+			if (this->nodeExists(node, "BonusStats")) {
+				const YAML::Node &bonusNode = node["BonusStats"];
+
+				for (const YAML::Node &levelNode : bonusNode) {
+					uint16 level;
+
+					if (!this->asUInt16(levelNode, "Level", level))
+						return 0;
+
+					if (level == 0 || level > MAX_LEVEL) {
+						this->invalidWarning(levelNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
+						return 0;
+					}
+
+					for (uint8 idx = PARAM_STR; idx < PARAM_MAX; idx++) {
+						if (this->nodeExists(levelNode, parameter_names[idx])) {
+							int16 change;
+
+							if (!this->asInt16(levelNode, parameter_names[idx], change))
+								return 0;
+
+							job->job_bonus[level - 1][idx] = change;
+						}
+					}
+				}
+			}
+
 #ifdef HP_SP_TABLES
 			if (this->nodeExists(node, "BaseHp")) {
-				job->base_hp.resize(job->max_base_level, 1);
-
 				for (const YAML::Node &bhpNode : node["BaseHp"]) {
 					uint16 level;
 
 					if (!this->asUInt16(bhpNode, "Level", level))
 						return 0;
 
-					if (level > MAX_LEVEL) {
+					if (level > job->max_base_level)
+						continue;
+
+					if (level == 0 || level > MAX_LEVEL) {
 						this->invalidWarning(bhpNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
 						return 0;
 					}
@@ -12498,15 +12516,16 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 			}
 
 			if (this->nodeExists(node, "BaseSp")) {
-				job->base_sp.resize(job->max_base_level, 1);
-
 				for (const YAML::Node &bspNode : node["BaseSp"]) {
 					uint16 level;
 
 					if (!this->asUInt16(bspNode, "Level", level))
 						return 0;
 
-					if (level > MAX_LEVEL) {
+					if (level > job->max_base_level)
+						continue;
+
+					if (level == 0 || level > MAX_LEVEL) {
 						this->invalidWarning(bspNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
 						return 0;
 					}
@@ -12523,15 +12542,16 @@ uint64 JobDatabase::parseBodyNode(const YAML::Node &node) {
 			}
 
 			if (this->nodeExists(node, "BaseAp")) {
-				job->base_ap.resize(job->max_base_level, 1);
-
 				for (const YAML::Node &bapNode : node["BaseAp"]) {
 					uint16 level;
 
 					if (!this->asUInt16(bapNode, "Level", level))
 						return 0;
 
-					if (level > MAX_LEVEL) {
+					if (level > job->max_base_level)
+						continue;
+
+					if (level == 0 || level > MAX_LEVEL) {
 						this->invalidWarning(bapNode["Level"], "Level must be between 1~MAX_LEVEL for %s.\n", job_name.c_str());
 						return 0;
 					}
@@ -12575,21 +12595,30 @@ void JobDatabase::loadingFinished() {
 			ShowWarning("Class %s (%d) does not have a job exp table.\n", job_name(job_id), job_id);
 
 		// Init and checking the empty value of Base HP/SP [Cydh]
-		if (job->base_hp.size() == 0)
+		if (job->base_hp.empty())
 			job->base_hp.resize(maxBaseLv);
 		for (uint16 j = 0; j < maxBaseLv; j++) {
 			if (job->base_hp[j] == 0)
 				job->base_hp[j] = pc_calc_basehp(j + 1, job_id);
 		}
-		if (job->base_sp.size() == 0)
-			job->base_sp.resize(maxJobLv);
-		for (uint16 j = 0; j < maxJobLv; j++) {
+		if (job->base_sp.empty())
+			job->base_sp.resize(maxBaseLv);
+		for (uint16 j = 0; j < maxBaseLv; j++) {
 			if (job->base_sp[j] == 0)
 				job->base_sp[j] = pc_calc_basesp(j + 1, job_id);
 		}
 
-		// Resize for the maximum job level
-		job->job_bonus.resize(maxJobLv);
+		// Resize to the maximum base level
+		if (job->base_hp.capacity() > maxBaseLv)
+			job->base_hp.erase(job->base_hp.begin() + maxBaseLv, job->base_hp.end());
+		if (job->base_sp.capacity() > maxBaseLv)
+			job->base_sp.erase(job->base_sp.begin() + maxBaseLv, job->base_sp.end());
+		if (job->base_ap.capacity() > maxBaseLv)
+			job->base_ap.erase(job->base_ap.begin() + maxBaseLv, job->base_ap.end());
+
+		// Resize to the maximum job level
+		if (job->job_bonus.capacity() > maxJobLv)
+			job->job_bonus.erase(job->job_bonus.begin() + maxJobLv, job->job_bonus.end());
 
 		for (uint16 parameter = PARAM_STR; parameter < PARAM_MAX; parameter++) {
 			// Store total

+ 19 - 8
src/tool/csv2yaml.cpp

@@ -4162,8 +4162,11 @@ static bool read_constdb(char* fields[], int columns, int current) {
 static bool pc_readdb_job2(char* fields[], int columns, int current) {
 	std::vector<int> stats;
 
+	stats.resize(MAX_LEVEL);
+	std::fill(stats.begin(), stats.end(), 0); // Fill with 0 so we don't produce arbitrary stats
+
 	for (int i = 1; i < columns; i++)
-		stats.insert(stats.begin() + i - 1, atoi(fields[i]));
+		stats[i - 1] = atoi(fields[i]);
 
 	job_db2.insert({ atoi(fields[0]), stats });
 	return true;
@@ -4258,17 +4261,25 @@ static bool pc_readdb_job_basehpsp(char* fields[], int columns, int current) {
 	body << YAML::BeginSeq;
 
 	int j = 0, job_id = jobs[0], endlvl = 0;
-	auto it_level = exp_base_level.find(job_id);
 
-	if (it_level != exp_base_level.end())
-		endlvl = it_level->second;
-	else {
-		ShowError("pc_readdb_job_basehpsp: The job_exp database needs to be imported into memory before converting the job_basehpsp_db database.\n");
-		return false;
+	// Find the highest level in the group of jobs
+	for (int i = 0; i < job_count; i++) {
+		auto it_level = exp_base_level.find(jobs[i]);
+		int tmplvl;
+
+		if (it_level != exp_base_level.end())
+			tmplvl = it_level->second;
+		else {
+			ShowError("pc_readdb_job_basehpsp: The job_exp database needs to be imported into memory before converting the job_basehpsp_db database.\n");
+			return false;
+		}
+
+		if (endlvl < tmplvl)
+			endlvl = tmplvl;
 	}
 
 	// These jobs don't have values less than level 99
-	if ((job_id >= JOB_RUNE_KNIGHT && job_id <= JOB_BABY_MECHANIC2) || job_id == JOB_KAGEROU || job_id == JOB_OBORO || job_id == JOB_REBELLION || job_id == JOB_BABY_KAGEROU || job_id == JOB_BABY_OBORO || job_id == JOB_BABY_REBELLION)
+	if ((job_id >= JOB_RUNE_KNIGHT && job_id <= JOB_BABY_MECHANIC2) || job_id == JOB_KAGEROU || job_id == JOB_OBORO || job_id == JOB_REBELLION || job_id == JOB_BABY_KAGEROU || job_id == JOB_BABY_OBORO || job_id == JOB_BABY_REBELLION || job_id >= JOB_STAR_EMPEROR)
 		j = 98;
 
 	if (type == 0) { // HP

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio