Browse Source

Extends the second roll Make/Amount

Atemo 1 year ago
parent
commit
348803193c
2 changed files with 221 additions and 200 deletions
  1. 220 200
      src/map/skill.cpp
  2. 1 0
      src/map/skill.hpp

+ 220 - 200
src/map/skill.cpp

@@ -21489,90 +21489,121 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 	if (!item_db.exists(nameid))
 	if (!item_db.exists(nameid))
 		return false;
 		return false;
 
 
-	int i, sc, ele, equip, wlv, make_per = 0, flag = 0, skill_lv = 0;
-	int num = -1; // exclude the recipe
-	struct status_data *status = status_get_status_data(&sd->bl);
+	int skill_lv = 0;
 
 
 	if( sd->skill_id_old == skill_id )
 	if( sd->skill_id_old == skill_id )
 		skill_lv = sd->skill_lv_old;
 		skill_lv = sd->skill_lv_old;
 
 
+	qty = max(1, qty);
+
 	if (produce == nullptr) {
 	if (produce == nullptr) {
 		produce = skill_can_produce_mix(sd,nameid,-1, qty);
 		produce = skill_can_produce_mix(sd,nameid,-1, qty);
 		if( produce == nullptr )
 		if( produce == nullptr )
 			return false;
 			return false;
 	}
 	}
 
 
-	if (qty < 1)
-		qty = 1;
-
-	if (!skill_id) //A skill can be specified for some override cases.
+	if (!skill_id) // A skill can be specified for some override cases.
 		skill_id = produce->req_skill;
 		skill_id = produce->req_skill;
-
-	if( skill_id == GC_RESEARCHNEWPOISON )
+	else if( skill_id == GC_RESEARCHNEWPOISON )
 		skill_id = GC_CREATENEWPOISON;
 		skill_id = GC_CREATENEWPOISON;
 
 
-	int slot[3];
-
-	slot[0] = slot1;
-	slot[1] = slot2;
-	slot[2] = slot3;
+	int num_required = -1; // exclude the recipe
 
 
-	for (i = 0, sc = 0, ele = 0; i < 3; i++) { //Note that qty should always be one if you are using these!
-		short j;
-		if (slot[i] <= 0)
-			continue;
-		j = pc_search_inventory(sd,slot[i]);
-		if (j < 0)
+	for ( const auto &it : produce->materials ) {
+		if (!item_db.exists(it.first))
 			continue;
 			continue;
-		if (slot[i] == ITEMID_STAR_CRUMB) {
-			pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
-			sc++;
-		}
-		if (slot[i] >= ITEMID_FLAME_HEART && slot[i] <= ITEMID_GREAT_NATURE && ele == 0) {
-			static const int ele_table[4] = { ELE_FIRE, ELE_WATER, ELE_WIND, ELE_EARTH };
-			pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
-			ele = ele_table[slot[i]-ITEMID_FLAME_HEART];
-		}
-	}
 
 
-	for (const auto &mat : produce->materials) {
-		short x, j;
-		t_itemid id = mat.first;
+		num_required++;
+		int16 qty_required;
 
 
-		if (!item_db.exists(id))
-			continue;
-		num++;
-		x = (skill_id == RK_RUNEMASTERY ? 1 : qty) * mat.second;
-		do {
-			int y = 0;
+		if (skill_id == RK_RUNEMASTERY)
+			qty_required = it.second;
+		else
+			qty_required = static_cast<int16>(qty * it.second);
 
 
-			j = pc_search_inventory(sd,id);
+		do {
+			int16 index = pc_search_inventory(sd, it.first);
 
 
-			if (j >= 0) {
-				y = sd->inventory.u.items_inventory[j].amount;
-				if (y > x)
-					y = x;
-				pc_delitem(sd,j,y,0,0,LOG_TYPE_PRODUCE);
-			} else {
-				ShowError("skill_produce_mix: material item error\n");
+			if (index < 0) {
+				ShowError("skill_produce_mix: material item error.\n");
 				return false;
 				return false;
 			}
 			}
-			x -= y;
-		} while( j >= 0 && x > 0 );
+
+			int16 inv_amount = sd->inventory.u.items_inventory[index].amount;
+			inv_amount = min(inv_amount, qty_required);
+
+			pc_delitem(sd,index,inv_amount,0,0,LOG_TYPE_PRODUCE);
+
+			qty_required -= inv_amount;
+		} while( qty_required > 0 );
 	}
 	}
 
 
-	if ((equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB)) && itemdb_type(nameid) == IT_WEAPON )
-		wlv = itemdb_wlv(nameid);
-	else
-		wlv = 0;
 
 
-	if (!equip) {
+	int star_crumb = 0, ele = 0, flag = 0;
+
+	std::vector<int32> slots = { slot1, slot2, slot3 };	// TODO int32 to t_itemid ?
+
+	std::unordered_map<t_itemid, int32> ele_table = {
+		{ ITEMID_FLAME_HEART, ELE_FIRE },
+		{ ITEMID_MISTIC_FROZEN, ELE_WATER },
+		{ ITEMID_ROUGH_WIND, ELE_WIND },
+		{ ITEMID_GREAT_NATURE, ELE_EARTH },
+	};
+
+	for ( const auto &it : slots ) {	// Note that qty should always be one if you are using these!
+		if (it <= 0)
+			continue;
+		int16 index = pc_search_inventory(sd, it);
+		if (index < 0)
+			continue;
+		if (it == ITEMID_STAR_CRUMB) {
+			pc_delitem(sd,index,1,1,0,LOG_TYPE_PRODUCE);
+			star_crumb++;
+		}
+		else if (ele == 0 && ele_table.count(it) > 0) {
+			pc_delitem(sd,index,1,1,0,LOG_TYPE_PRODUCE);
+			ele = ele_table[it];
+		}
+	}
+
+	bool is_equip_type = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB);
+	int weapon_level = (is_equip_type && itemdb_type(nameid) == IT_WEAPON) ? itemdb_wlv(nameid) : 0;
+	int make_per = 0;
+	struct status_data *status = status_get_status_data(&sd->bl);
+	
+	if (is_equip_type) {	// Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
+		make_per = 5000 + ((sd->class_&JOBL_THIRD) ? 1400 : sd->status.job_level*20) + status->dex*10 + status->luk*10; // Base
+		make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
+		// Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10
+		make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100;
+		//  Oridecon Research bonus (custom): +1/+2/+3/+4/+5
+		if( weapon_level >= 3 ){
+			make_per += pc_checkskill(sd, BS_ORIDEOCON) * 100;
+		}
+		// Element Stone: -20%
+		if( ele > 0 ){
+			make_per -= 2000;
+		}
+		// Star Crumb: -15% each
+		make_per -= star_crumb * 1500;
+		//  Weapon level malus: -0/-10/-20/-30
+		if( weapon_level > 1 ){
+			make_per -= ( weapon_level * 1000 );
+		}
+		if      (pc_search_inventory(sd,ITEMID_EMPERIUM_ANVIL) > -1) make_per+= 1000; // Emperium Anvil: +10
+		else if (pc_search_inventory(sd,ITEMID_GOLDEN_ANVIL) > -1)   make_per+= 500; // Golden Anvil: +5
+		else if (pc_search_inventory(sd,ITEMID_ORIDECON_ANVIL) > -1) make_per+= 300; // Oridecon Anvil: +3
+		else if (pc_search_inventory(sd,ITEMID_ANVIL) > -1)          make_per+= 0; // Anvil: +0?
+		if (battle_config.wp_rate != 100)
+			make_per = make_per * battle_config.wp_rate / 100;
+	}
+	else {
 		switch (skill_id) {
 		switch (skill_id) {
 			case BS_IRON:
 			case BS_IRON:
 			case BS_STEEL:
 			case BS_STEEL:
-			case BS_ENCHANTEDSTONE:
+			case BS_ENCHANTEDSTONE: {
 				// Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
 				// Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
-				i = pc_checkskill(sd,skill_id);
+				int i = pc_checkskill(sd,skill_id);
 				make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance
 				make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance
 				switch (nameid) {
 				switch (nameid) {
 					case ITEMID_IRON:
 					case ITEMID_IRON:
@@ -21589,6 +21620,7 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 						break;
 						break;
 				}
 				}
 				break;
 				break;
+			}
 			case ASC_CDP:
 			case ASC_CDP:
 				make_per = (2000 + 40*status->dex + 20*status->luk);
 				make_per = (2000 + 40*status->dex + 20*status->luk);
 				break;
 				break;
@@ -21693,10 +21725,9 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 			case GN_CHANGEMATERIAL:
 			case GN_CHANGEMATERIAL:
 				make_per = produce->baserate * 10;
 				make_per = produce->baserate * 10;
 				break;
 				break;
-			case GN_S_PHARMACY:
-				{
+			case GN_S_PHARMACY: {
 					int difficulty = (620 - 20 * skill_lv); // (620 - 20 * Skill Level)
 					int difficulty = (620 - 20 * skill_lv); // (620 - 20 * Skill Level)
-					const int production_count[] = { 7, 8, 8, 9, 9, 10, 10, 11, 11, 12 };
+					std::vector<int32> production_count = { 7, 8, 8, 9, 9, 10, 10, 11, 11, 12 };
 
 
 					switch (nameid) { // Item difficulty factor
 					switch (nameid) { // Item difficulty factor
 						case ITEMID_HP_INCREASE_POTION_SMALL:
 						case ITEMID_HP_INCREASE_POTION_SMALL:
@@ -21727,7 +21758,9 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 					make_per = status->int_ + status->dex / 2 + status->luk + sd->status.job_level + (30 + rnd() % 120 + 1) + // Caster's INT + (Caster's DEX / 2) + Caster's LUK + Caster's Job Level + Random number between (30 ~ 150) +
 					make_per = status->int_ + status->dex / 2 + status->luk + sd->status.job_level + (30 + rnd() % 120 + 1) + // Caster's INT + (Caster's DEX / 2) + Caster's LUK + Caster's Job Level + Random number between (30 ~ 150) +
 						sd->status.base_level + 5 * (pc_checkskill(sd, AM_LEARNINGPOTION) - 20) + pc_checkskill(sd, CR_FULLPROTECTION) * (6 + rnd() % 4 + 1); // Caster's Base Level + (5 x (Potion Research Skill Level - 20)) + (Full Chemical Protection Skill Level x Random number between (6 ~ 10))
 						sd->status.base_level + 5 * (pc_checkskill(sd, AM_LEARNINGPOTION) - 20) + pc_checkskill(sd, CR_FULLPROTECTION) * (6 + rnd() % 4 + 1); // Caster's Base Level + (5 x (Potion Research Skill Level - 20)) + (Full Chemical Protection Skill Level x Random number between (6 ~ 10))
 					make_per -= difficulty;
 					make_per -= difficulty;
-					qty = production_count[skill_lv - 1];
+
+					if (util::vector_exists(production_count, skill_lv-1))
+						qty = production_count[skill_lv-1];
 
 
 					// Determine quantity from difficulty
 					// Determine quantity from difficulty
 					if (make_per < 1)
 					if (make_per < 1)
@@ -21806,9 +21839,7 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 				make_per = 100000;
 				make_per = 100000;
 				break;
 				break;
 			default:
 			default:
-				if (sd->menuskill_id == AM_PHARMACY &&
-					sd->menuskill_val > 10 && sd->menuskill_val <= 20)
-				{	//Assume Cooking Dish
+				if (sd->menuskill_id == AM_PHARMACY && sd->menuskill_val > 10 && sd->menuskill_val <= 20) {	//Assume Cooking Dish
 					if (sd->menuskill_val >= 15) //Legendary Cooking Set.
 					if (sd->menuskill_val >= 15) //Legendary Cooking Set.
 						make_per = 10000; //100% Success
 						make_per = 10000; //100% Success
 					else
 					else
@@ -21816,59 +21847,44 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 							+ 20  * (sd->status.base_level + 1)
 							+ 20  * (sd->status.base_level + 1)
 							+ 20  * (status->dex + 1)
 							+ 20  * (status->dex + 1)
 							+ 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80))
 							+ 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80))
-							- 400 * (produce->itemlv - 11 + 1)
+							- 400 * (produce->itemlv - 11 + 1)	// TODO
 							- 10  * (100 - status->luk + 1)
 							- 10  * (100 - status->luk + 1)
-							- 500 * (num - 1)
+							- 500 * (num_required - 1)
 							- 100 * (rnd()%4 + 1);
 							- 100 * (rnd()%4 + 1);
 					break;
 					break;
 				}
 				}
 				make_per = 5000;
 				make_per = 5000;
 				break;
 				break;
 		}
 		}
-	} else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
-		make_per = 5000 + ((sd->class_&JOBL_THIRD)?1400:sd->status.job_level*20) + status->dex*10 + status->luk*10; // Base
-		make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
-		// Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10
-		make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100;
-		//  Oridecon Research bonus (custom): +1/+2/+3/+4/+5
-		if( wlv >= 3 ){
-			make_per += pc_checkskill(sd, BS_ORIDEOCON) * 100;
-		}
-		// Element Stone: -20%
-		if( ele ){
-			make_per -= 2000;
-		}
-		// Star Crumb: -15% each
-		make_per -= sc * 1500;
-		//  Weapon level malus: -0/-10/-20/-30
-		if( wlv > 1 ){
-			make_per -= ( wlv * 1000 );
-		}
-		if      (pc_search_inventory(sd,ITEMID_EMPERIUM_ANVIL) > -1) make_per+= 1000; // Emperium Anvil: +10
-		else if (pc_search_inventory(sd,ITEMID_GOLDEN_ANVIL) > -1)   make_per+= 500; // Golden Anvil: +5
-		else if (pc_search_inventory(sd,ITEMID_ORIDECON_ANVIL) > -1) make_per+= 300; // Oridecon Anvil: +3
-		else if (pc_search_inventory(sd,ITEMID_ANVIL) > -1)          make_per+= 0; // Anvil: +0?
-		if (battle_config.wp_rate != 100)
-			make_per = make_per * battle_config.wp_rate / 100;
 	}
 	}
 
 
 	if (sd->class_&JOBL_BABY) //if it's a Baby Class
 	if (sd->class_&JOBL_BABY) //if it's a Baby Class
 		make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847)
 		make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847)
 
 
-	if (make_per < 1) make_per = 1;
+	make_per = max(1, make_per);
 
 
 	if (qty > 1 || rnd()%10000 < make_per){ //Success, or crafting multiple items.
 	if (qty > 1 || rnd()%10000 < make_per){ //Success, or crafting multiple items.
-		struct item tmp_item;
-		memset(&tmp_item,0,sizeof(tmp_item));
+		struct item tmp_item = {0};
+
 		tmp_item.nameid = nameid;
 		tmp_item.nameid = nameid;
-		tmp_item.amount = 1;
 		tmp_item.identify = 1;
 		tmp_item.identify = 1;
-		if (equip) {
+
+		if (is_equip_type) {
+			tmp_item.amount = 1;
 			tmp_item.card[0] = CARD0_FORGE;
 			tmp_item.card[0] = CARD0_FORGE;
-			tmp_item.card[1] = ((sc*5)<<8)+ele;
+			tmp_item.card[1] = ((star_crumb*5)<<8)+ele;
 			tmp_item.card[2] = GetWord(sd->status.char_id,0); // CharId
 			tmp_item.card[2] = GetWord(sd->status.char_id,0); // CharId
 			tmp_item.card[3] = GetWord(sd->status.char_id,1);
 			tmp_item.card[3] = GetWord(sd->status.char_id,1);
-		} else {
+
+			//		if(log_config.produce > 0)
+			//			log_produce(sd,nameid,slot1,slot2,slot3,1);
+			//TODO update PICKLOG
+			clif_produceeffect(sd,0,nameid);
+			clif_misceffect(&sd->bl,3);
+			if (weapon_level >= 3 && ((ele > 0 ? 1 : 0) + star_crumb) >= 3) // Fame point system [DracoRPG]
+				pc_addfame(*sd, battle_config.fame_forge); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
+		}
+		else {
 			//Flag is only used on the end, so it can be used here. [Skotlex]
 			//Flag is only used on the end, so it can be used here. [Skotlex]
 			switch (skill_id) {
 			switch (skill_id) {
 				case BS_DAGGER:
 				case BS_DAGGER:
@@ -21903,57 +21919,52 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 				tmp_item.card[2] = GetWord(sd->status.char_id,0); // CharId
 				tmp_item.card[2] = GetWord(sd->status.char_id,0); // CharId
 				tmp_item.card[3] = GetWord(sd->status.char_id,1);
 				tmp_item.card[3] = GetWord(sd->status.char_id,1);
 			}
 			}
-		}
 
 
-//		if(log_config.produce > 0)
-//			log_produce(sd,nameid,slot1,slot2,slot3,1);
-//TODO update PICKLOG
+			//		if(log_config.produce > 0)
+			//			log_produce(sd,nameid,slot1,slot2,slot3,1);
+			//TODO update PICKLOG
 
 
-		if (equip) {
-			clif_produceeffect(sd,0,nameid);
-			clif_misceffect(&sd->bl,3);
-			if (wlv >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
-				pc_addfame(*sd, battle_config.fame_forge); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
-		} else {
-			int fame = 0;
 			tmp_item.amount = 0;
 			tmp_item.amount = 0;
 
 
-			for (i = 0; i < qty; i++) {	//Apply quantity modifiers.
-				if ((skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY || skill_id == MT_M_MACHINE || skill_id == BO_BIONIC_PHARMACY) && make_per > 1) {
-					tmp_item.amount = qty;
-					break;
+			if ((skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY || skill_id == MT_M_MACHINE || skill_id == BO_BIONIC_PHARMACY) && make_per > 1)
+				tmp_item.amount = qty;
+			else {
+				int fame = 0;
+
+				for (int i = 0; i < qty; i++) {	//Apply quantity modifiers.
+					if (qty == 1 || rnd()%10000 < make_per) { //Success
+						tmp_item.amount++;
+						if (nameid < ITEMID_RED_SLIM_POTION || nameid > ITEMID_WHITE_SLIM_POTION)
+							continue;
+						if (skill_id != AM_PHARMACY &&
+							skill_id != AM_TWILIGHT1 &&
+							skill_id != AM_TWILIGHT2 &&
+							skill_id != AM_TWILIGHT3)
+							continue;
+						//Add fame as needed.
+						switch(++sd->potion_success_counter) {
+							case 3:
+								fame += battle_config.fame_pharmacy_3; // Success to prepare 3 Condensed Potions in a row
+								break;
+							case 5:
+								fame += battle_config.fame_pharmacy_5; // Success to prepare 5 Condensed Potions in a row
+								break;
+							case 7:
+								fame += battle_config.fame_pharmacy_7; // Success to prepare 7 Condensed Potions in a row
+								break;
+							case 10:
+								fame += battle_config.fame_pharmacy_10; // Success to prepare 10 Condensed Potions in a row
+								sd->potion_success_counter = 0;
+								break;
+						}
+					} else //Failure
+						sd->potion_success_counter = 0;
 				}
 				}
-				if (qty == 1 || rnd()%10000 < make_per) { //Success
-					tmp_item.amount++;
-					if (nameid < ITEMID_RED_SLIM_POTION || nameid > ITEMID_WHITE_SLIM_POTION)
-						continue;
-					if (skill_id != AM_PHARMACY &&
-						skill_id != AM_TWILIGHT1 &&
-						skill_id != AM_TWILIGHT2 &&
-						skill_id != AM_TWILIGHT3)
-						continue;
-					//Add fame as needed.
-					switch(++sd->potion_success_counter) {
-						case 3:
-							fame += battle_config.fame_pharmacy_3; // Success to prepare 3 Condensed Potions in a row
-							break;
-						case 5:
-							fame += battle_config.fame_pharmacy_5; // Success to prepare 5 Condensed Potions in a row
-							break;
-						case 7:
-							fame += battle_config.fame_pharmacy_7; // Success to prepare 7 Condensed Potions in a row
-							break;
-						case 10:
-							fame += battle_config.fame_pharmacy_10; // Success to prepare 10 Condensed Potions in a row
-							sd->potion_success_counter = 0;
-							break;
-					}
-				} else //Failure
-					sd->potion_success_counter = 0;
+
+				if (fame > 0)
+					pc_addfame(*sd, fame);
 			}
 			}
 
 
-			if (fame)
-				pc_addfame(*sd, fame);
 			//Visual effects and the like.
 			//Visual effects and the like.
 			switch (skill_id) {
 			switch (skill_id) {
 				case AM_PHARMACY:
 				case AM_PHARMACY:
@@ -21980,76 +21991,73 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 			}
 			}
 		}
 		}
 
 
-		if (skill_id == GN_CHANGEMATERIAL && tmp_item.amount) { //Success
+		if (tmp_item.amount > 0) { // Success from first roll (BaseRate)
 			bool is_produce_success = false;
 			bool is_produce_success = false;
 			bool isStackable = itemdb_isstackable(tmp_item.nameid);
 			bool isStackable = itemdb_isstackable(tmp_item.nameid);
 
 
-			if (!produce->qty.empty()) {
-				for (const auto &qtyit : produce->qty) {
-					if (rnd()%1000 < qtyit.second){
-						uint16 total_qty = qty * qtyit.first;
-						tmp_item.amount = (isStackable ? total_qty : 1);
-						for ( int l = 0; l < total_qty; l += tmp_item.amount ) {
-							if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
-								clif_additem(sd,0,0,flag);
-								if( battle_config.skill_drop_items_full ){
-									map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
-								}
-							}
-						}
-						is_produce_success = true;
+			for ( const auto &qtyit : produce->qty ) {
+				if (rnd()%1000 >= qtyit.second)
+					continue;
+				uint16 total_qty = qty * qtyit.first;
+				tmp_item.amount = (isStackable ? total_qty : 1);
+
+				for ( int i = 0; i < total_qty; i += tmp_item.amount ) {
+					e_additem_result flag_item = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE);
+
+					if (flag_item != ADDITEM_SUCCESS)
+						continue;
+					clif_additem(sd,0,0,flag_item);
+					if( battle_config.skill_drop_items_full ){
+						map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
 					}
 					}
 				}
 				}
+				is_produce_success = true;
 			}
 			}
-			if (is_produce_success) {
-				clif_produceeffect(sd,6,nameid);
-				clif_misceffect(&sd->bl,5);
-				clif_msg_skill(sd,skill_id,ITEM_PRODUCE_SUCCESS);
-				return true;
-			}
-		} else if (tmp_item.amount) { //Success
-			if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
-				clif_additem(sd,0,0,flag);
-				if( battle_config.skill_drop_items_full ){
-					map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+
+			if (is_produce_success) { // Success from second roll (additional Rate / amount)
+				switch (skill_id) {
+					case RK_RUNEMASTERY:
+						clif_produceeffect(sd, 4, nameid);
+						clif_misceffect(&sd->bl, 5);
+						break;
+					case GN_MIX_COOKING:
+					case GN_MAKEBOMB:
+					case GN_S_PHARMACY:
+						clif_produceeffect(sd, 6, nameid);
+						clif_misceffect(&sd->bl, 5);
+						clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
+						break;
+					case MT_M_MACHINE:
+						clif_produceeffect(sd, 0, nameid);
+						clif_misceffect(&sd->bl, 3);
+						clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
+						break;
+					case BO_BIONIC_PHARMACY:
+						clif_produceeffect(sd, 2, nameid);
+						clif_misceffect(&sd->bl, 5);
+						clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
+						break;
+					case GN_CHANGEMATERIAL:
+						clif_produceeffect(sd,6, nameid);
+						clif_misceffect(&sd->bl,5);
+						clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
+						break;
 				}
 				}
+				return true;
 			}
 			}
-			switch (skill_id) {
-				case RK_RUNEMASTERY:
-					clif_produceeffect(sd, 4, nameid);
-					clif_misceffect(&sd->bl, 5);
-					break;
-				case GN_MIX_COOKING:
-				case GN_MAKEBOMB:
-				case GN_S_PHARMACY:
-					clif_produceeffect(sd, 6, nameid);
-					clif_misceffect(&sd->bl, 5);
-					clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
-					break;
-				case MT_M_MACHINE:
-					clif_produceeffect(sd, 0, nameid);
-					clif_misceffect(&sd->bl, 3);
-					clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
-					break;
-				case BO_BIONIC_PHARMACY:
-					clif_produceeffect(sd, 2, nameid);
-					clif_misceffect(&sd->bl, 5);
-					clif_msg_skill(sd, skill_id, ITEM_PRODUCE_SUCCESS);
-					break;
-			}
-			return true;
 		}
 		}
 	}
 	}
 
 
 	//Failure
 	//Failure
-//	if(log_config.produce)
-//		log_produce(sd,nameid,slot1,slot2,slot3,0);
-//TODO update PICKLOG
+	//	if(log_config.produce)
+	//		log_produce(sd,nameid,slot1,slot2,slot3,0);
+	//TODO update PICKLOG
 
 
-	if (equip) {
+	if (is_equip_type) {
 		clif_produceeffect(sd,1,nameid);
 		clif_produceeffect(sd,1,nameid);
 		clif_misceffect(&sd->bl,2);
 		clif_misceffect(&sd->bl,2);
-	} else {
+	}
+	else {
 		switch (skill_id) {
 		switch (skill_id) {
 			case ASC_CDP: //25% Damage yourself, and display same effect as failed potion.
 			case ASC_CDP: //25% Damage yourself, and display same effect as failed potion.
 				status_percent_damage(NULL, &sd->bl, -25, 0, true);
 				status_percent_damage(NULL, &sd->bl, -25, 0, true);
@@ -22074,12 +22082,12 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 				break;
 				break;
 			case GN_MIX_COOKING:
 			case GN_MIX_COOKING:
 				if (qty == 0) {
 				if (qty == 0) {
-					item tmp_item;
+					int i;
+					struct item tmp_item = {0};
+
 					const t_itemid compensation[5] = { ITEMID_BLACK_LUMP, ITEMID_BLACK_HARD_LUMP, ITEMID_VERY_HARD_LUMP, ITEMID_BLACK_MASS, ITEMID_MYSTERIOUS_POWDER };
 					const t_itemid compensation[5] = { ITEMID_BLACK_LUMP, ITEMID_BLACK_HARD_LUMP, ITEMID_VERY_HARD_LUMP, ITEMID_BLACK_MASS, ITEMID_MYSTERIOUS_POWDER };
 					int rate = rnd() % 1000 + 1;
 					int rate = rnd() % 1000 + 1;
 
 
-					memset(&tmp_item, 0, sizeof(tmp_item));
-
 					if (rate < 500)
 					if (rate < 500)
 						i = 0;
 						i = 0;
 					else if (rate < 750)
 					else if (rate < 750)
@@ -24699,7 +24707,7 @@ uint64 SkillProduceDatabase::parseBodyNode(const ryml::NodeRef &node) {
 			}
 			}
 		}
 		}
 
 
-		if (this->nodeExists(subit, "Make")) {	// note: Quantity is only used for skill changematerial (itemlv 26)
+		if (this->nodeExists(subit, "Make")) {
 			const ryml::NodeRef &QuantityNode = subit["Make"];
 			const ryml::NodeRef &QuantityNode = subit["Make"];
 
 
 			for (const auto &Quantityit : QuantityNode) {
 			for (const auto &Quantityit : QuantityNode) {
@@ -24782,6 +24790,18 @@ uint64 SkillProduceDatabase::parseBodyNode(const ryml::NodeRef &node) {
 	return 1;
 	return 1;
 }
 }
 
 
+void SkillProduceDatabase::loadingFinished() {
+	for ( auto &produce : *this ) {
+		for ( auto &data : produce.second->data ) {
+			if (!data.second->qty.empty())
+				continue;
+			data.second->qty.insert({ 1, 1000 });
+		}
+	}
+
+	TypesafeYamlDatabase::loadingFinished();
+}
+
 const std::string SkillArrowDatabase::getDefaultLocation() {
 const std::string SkillArrowDatabase::getDefaultLocation() {
 	return std::string(db_path) + "/create_arrow_db.yml";
 	return std::string(db_path) + "/create_arrow_db.yml";
 }
 }

+ 1 - 0
src/map/skill.hpp

@@ -466,6 +466,7 @@ public:
 	const std::string getDefaultLocation() override;
 	const std::string getDefaultLocation() override;
 	uint64 parseBodyNode(const ryml::NodeRef& node) override;
 	uint64 parseBodyNode(const ryml::NodeRef& node) override;
 	bool addItemConsumed(const ryml::NodeRef& node, std::shared_ptr<s_skill_produce_db_entry> &entry, bool isConsumed);
 	bool addItemConsumed(const ryml::NodeRef& node, std::shared_ptr<s_skill_produce_db_entry> &entry, bool isConsumed);
+	void loadingFinished() override;
 };
 };
 
 
 extern SkillProduceDatabase skill_produce_db;
 extern SkillProduceDatabase skill_produce_db;