@@ -0,0 +1,52 @@
# This file is a part of rAthena.
# Copyright(C) 2020 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/>.
#
###########################################################################
# Item Random Option Group Database
###########################################################################
#
# Item Random Option Group Settings
#
###########################################################################
# - Id Item Random Option Group ID.
# Group Item Random Option Group constant.
# Slots: Slot in which an Item Random Option is guaranteed to be applied. Max of MAX_ITEM_RDM_OPT.
# - Slot Slot number.
# Options: List of possible Item Random Options for slot.
# - Option Item Random Option constant.
# MinValue Minimum value. (Default: 0)
# MaxValue Maximum value. (Default: 0)
# Param Parameter value. (Default: 0)
# Chance Chance applied specifically to this Item Random Option (1 = 0.01%, 10000 = 100%). (Default: 0)
# MaxRandom Maximum amount of random options applied. These options are not guaranteed to be applied. Max of (MAX_ITEM_RDM_OPT - Total 'Slots'). (Default: 0)
# Random: List of possible Item Random Options for remaining slots. (Optional)
# - Option Item Random Option constant.
# MinValue Minimum value. (Default: 0)
# MaxValue Maximum value. (Default: 0)
# Param Parameter value. (Default: 0)
# Chance Chance applied specifically to this Item Random Option (1 = 0.01%, 10000 = 100%). (Default: 0)
###########################################################################

Header:
Type: RANDOM_OPTION_GROUP
Version: 1

Footer:
Imports:
- Path: db/re/item_randomopt_group.yml
Mode: Renewal
- Path: db/import/item_randomopt_group.yml

This file was deleted.

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

This file was deleted.

Large diffs are not rendered by default.

@@ -16,108 +16,108 @@
// <flag> : 1 - The item is protected from steal.
// 2 - As MVP Reward

1063,1102,100,RDMOPTG_None // LUNATIC
2770,1102,500,RDMOPTG_None // C2_LUNATIC
2771,1102,500,RDMOPTG_None // C3_LUNATIC
2072,1839,50,RDMOPTG_Crimson_Weapon // JAGUAR
1584,21015,50,RDMOPTG_Crimson_Weapon // TAMRUAN
2639,21015,250,RDMOPTG_Crimson_Weapon // C4_TAMRUAN
1154,13454,50,RDMOPTG_Crimson_Weapon // PASANA
1154,28705,50,RDMOPTG_Crimson_Weapon // PASANA
2719,13454,250,RDMOPTG_Crimson_Weapon // C1_PASANA
2719,28705,250,RDMOPTG_Crimson_Weapon // C1_PASANA
1117,28604,50,RDMOPTG_Crimson_Weapon // EVIL_DRUID
1517,16040,50,RDMOPTG_Crimson_Weapon // LI_ME_MANG_RYANG
2071,28007,50,RDMOPTG_Crimson_Weapon // HEADLESS_MULE
2778,16040,250,RDMOPTG_Crimson_Weapon // C5_LI_ME_MANG_RYANG
2838,28604,50,RDMOPTG_Crimson_Weapon // C5_EVIL_DRUID
1613,13127,50,RDMOPTG_None // METALING
1386,28705,50,RDMOPTG_Crimson_Weapon // SLEEPER
2655,28705,250,RDMOPTG_Crimson_Weapon // C5_SLEEPER
2656,28705,250,RDMOPTG_Crimson_Weapon // C1_SLEEPER
2755,13127,250,RDMOPTG_None // C2_METALING
2756,13127,250,RDMOPTG_None // C3_METALING
1631,1839,50,RDMOPTG_Crimson_Weapon // CHUNG_E_
1215,1443,50,RDMOPTG_Crimson_Weapon // STEM_WORM
2641,1443,250,RDMOPTG_Crimson_Weapon // C1_STEM_WORM
1404,1939,50,RDMOPTG_Crimson_Weapon // MIYABI_NINGYO
1628,13127,50,RDMOPTG_None // MOLE
1619,28705,50,RDMOPTG_Crimson_Weapon // PORCELLIO
2700,28705,250,RDMOPTG_Crimson_Weapon // C2_PORCELLIO
2745,13127,250,RDMOPTG_None // C2_MOLE
2746,1939,250,RDMOPTG_Crimson_Weapon // C3_MIYABI_NINGYO
1102,1680,50,RDMOPTG_None // BATHORY
1155,16040,50,RDMOPTG_Crimson_Weapon // PETIT
2714,16040,250,RDMOPTG_Crimson_Weapon // C1_PETIT
2715,16040,250,RDMOPTG_Crimson_Weapon // C2_PETIT
2885,1680,250,RDMOPTG_None // C4_BATHORY
2199,28705,50,RDMOPTG_Crimson_Weapon // SIORAVA
1143,16040,50,RDMOPTG_Crimson_Weapon // MARIONETTE
1413,1995,50,RDMOPTG_Crimson_Weapon // WILD_GINSENG
2761,16040,250,RDMOPTG_Crimson_Weapon // C3_MARIONETTE
1320,1498,50,RDMOPTG_Crimson_Weapon // OWL_DUKE
1320,2025,50,RDMOPTG_None // OWL_DUKE
1316,16040,50,RDMOPTG_Crimson_Weapon // SOLIDER
2647,16040,250,RDMOPTG_Crimson_Weapon // C2_SOLIDER
2721,1498,250,RDMOPTG_Crimson_Weapon // C3_OWL_DUKE
2721,2025,250,RDMOPTG_None // C3_OWL_DUKE
1408,1839,50,RDMOPTG_Crimson_Weapon // BLOOD_BUTTERFLY
2883,1839,250,RDMOPTG_Crimson_Weapon // C1_BLOOD_BUTTERFLY
1257,28007,50,RDMOPTG_Crimson_Weapon // INJUSTICE
2792,28007,250,RDMOPTG_Crimson_Weapon // C4_INJUSTICE
1302,21015,50,RDMOPTG_Crimson_Weapon // DARK_ILLUSION
1416,1939,50,RDMOPTG_Crimson_Weapon // WICKED_NYMPH
1416,1995,50,RDMOPTG_Crimson_Weapon // WICKED_NYMPH
2617,1939,250,RDMOPTG_Crimson_Weapon // C5_WICKED_NYMPH
2617,1995,250,RDMOPTG_Crimson_Weapon // C5_WICKED_NYMPH
1405,13327,50,RDMOPTG_Crimson_Weapon // TENGU
1030,1498,50,RDMOPTG_Crimson_Weapon // ANACONDAQ
2904,1498,250,RDMOPTG_Crimson_Weapon // C4_ANACONDAQ
1205,13454,50,RDMOPTG_Crimson_Weapon // EXECUTIONER
1135,28106,50,RDMOPTG_Crimson_Weapon // KOBOLD_3
1106,28705,50,RDMOPTG_Crimson_Weapon // DESERT_WOLF
1259,1498,250,RDMOPTG_Crimson_Weapon // GRYPHON
1310,28106,50,RDMOPTG_Crimson_Weapon // MAJORUROS
2767,28106,250,RDMOPTG_Crimson_Weapon // C4_MAJORUROS
1736,1839,50,RDMOPTG_Crimson_Weapon // ALIOT
1296,16040,50,RDMOPTG_Crimson_Weapon // KOBOLD_LEADER
1204,28705,50,RDMOPTG_Crimson_Weapon // TIRFING
1204,13454,50,RDMOPTG_Crimson_Weapon // TIRFING
1993,1443,50,RDMOPTG_Crimson_Weapon // NAGA
1390,1939,50,RDMOPTG_Crimson_Weapon // VIOLY
2621,1939,250,RDMOPTG_Crimson_Weapon // C5_VIOLY
2622,1939,250,RDMOPTG_Crimson_Weapon // C1_VIOLY
2623,1939,250,RDMOPTG_Crimson_Weapon // C2_VIOLY
1295,18130,50,RDMOPTG_None // OWL_BARON
1303,2025,50,RDMOPTG_None // GIANT_HONET
2821,2025,250,RDMOPTG_None // C3_GIANT_HONET
1702,21015,50,RDMOPTG_Crimson_Weapon // RETRIBUTION
2353,28106,50,RDMOPTG_Crimson_Weapon // N_MINOROUS
2684,21015,250,RDMOPTG_Crimson_Weapon // C4_RETRIBUTION
2685,21015,250,RDMOPTG_Crimson_Weapon // C5_RETRIBUTION
2686,21015,250,RDMOPTG_Crimson_Weapon // C1_RETRIBUTION
1219,21015,50,RDMOPTG_Crimson_Weapon // KNIGHT_OF_ABYSS
1703,1939,50,RDMOPTG_Crimson_Weapon // SOLACE
2650,1939,250,RDMOPTG_Crimson_Weapon // C5_SOLACE
2041,28705,50,RDMOPTG_Crimson_Weapon // MYSTELTAINN
2041,13454,50,RDMOPTG_Crimson_Weapon // MYSTELTAINN
2041,21015,50,RDMOPTG_Crimson_Weapon // MYSTELTAINN
1830,18130,50,RDMOPTG_None // BOW_GUARDIAN
1653,28705,50,RDMOPTG_Crimson_Weapon // WHIKEBAIN
1655,1839,50,RDMOPTG_Crimson_Weapon // EREND
1655,16040,50,RDMOPTG_Crimson_Weapon // EREND
1657,1680,50,RDMOPTG_None // RAWREL
1829,21015,50,RDMOPTG_Crimson_Weapon // SWORD_GUARDIAN
2692,1680,250,RDMOPTG_None // C3_RAWREL
1654,13454,50,RDMOPTG_Crimson_Weapon // ARMAIA
1654,28106,50,RDMOPTG_Crimson_Weapon // ARMAIA
1656,1939,50,RDMOPTG_Crimson_Weapon // KAVAC
1656,18130,50,RDMOPTG_None // KAVAC
1652,13454,50,RDMOPTG_Crimson_Weapon // YGNIZEM
1652,21015,50,RDMOPTG_Crimson_Weapon // YGNIZEM
1290,28705,50,RDMOPTG_Crimson_Weapon // SKELETON_GENERAL
2658,28705,250,RDMOPTG_Crimson_Weapon // C3_SKELETON_GENERAL
2659,28705,250,RDMOPTG_Crimson_Weapon // C4_SKELETON_GENERAL
1658,21015,500,RDMOPTG_Crimson_Weapon // B_YGNIZEM
1301,16040,50,RDMOPTG_Crimson_Weapon // AM_MUT
2362,28604,50,RDMOPTG_Crimson_Weapon // N_AMON_RA
1063,1102,100,None // LUNATIC
2770,1102,500,None // C2_LUNATIC
2771,1102,500,None // C3_LUNATIC
2072,1839,50,Group_5 // JAGUAR
1584,21015,50,Group_5 // TAMRUAN
2639,21015,250,Group_5 // C4_TAMRUAN
1154,13454,50,Group_5 // PASANA
1154,28705,50,Group_5 // PASANA
2719,13454,250,Group_5 // C1_PASANA
2719,28705,250,Group_5 // C1_PASANA
1117,28604,50,Group_5 // EVIL_DRUID
1517,16040,50,Group_5 // LI_ME_MANG_RYANG
2071,28007,50,Group_5 // HEADLESS_MULE
2778,16040,250,Group_5 // C5_LI_ME_MANG_RYANG
2838,28604,50,Group_5 // C5_EVIL_DRUID
1613,13127,50,None // METALING
1386,28705,50,Group_5 // SLEEPER
2655,28705,250,Group_5 // C5_SLEEPER
2656,28705,250,Group_5 // C1_SLEEPER
2755,13127,250,None // C2_METALING
2756,13127,250,None // C3_METALING
1631,1839,50,Group_5 // CHUNG_E_
1215,1443,50,Group_5 // STEM_WORM
2641,1443,250,Group_5 // C1_STEM_WORM
1404,1939,50,Group_5 // MIYABI_NINGYO
1628,13127,50,None // MOLE
1619,28705,50,Group_5 // PORCELLIO
2700,28705,250,Group_5 // C2_PORCELLIO
2745,13127,250,None // C2_MOLE
2746,1939,250,Group_5 // C3_MIYABI_NINGYO
1102,1680,50,None // BATHORY
1155,16040,50,Group_5 // PETIT
2714,16040,250,Group_5 // C1_PETIT
2715,16040,250,Group_5 // C2_PETIT
2885,1680,250,None // C4_BATHORY
2199,28705,50,Group_5 // SIORAVA
1143,16040,50,Group_5 // MARIONETTE
1413,1995,50,Group_5 // WILD_GINSENG
2761,16040,250,Group_5 // C3_MARIONETTE
1320,1498,50,Group_5 // OWL_DUKE
1320,2025,50,None // OWL_DUKE
1316,16040,50,Group_5 // SOLIDER
2647,16040,250,Group_5 // C2_SOLIDER
2721,1498,250,Group_5 // C3_OWL_DUKE
2721,2025,250,None // C3_OWL_DUKE
1408,1839,50,Group_5 // BLOOD_BUTTERFLY
2883,1839,250,Group_5 // C1_BLOOD_BUTTERFLY
1257,28007,50,Group_5 // INJUSTICE
2792,28007,250,Group_5 // C4_INJUSTICE
1302,21015,50,Group_5 // DARK_ILLUSION
1416,1939,50,Group_5 // WICKED_NYMPH
1416,1995,50,Group_5 // WICKED_NYMPH
2617,1939,250,Group_5 // C5_WICKED_NYMPH
2617,1995,250,Group_5 // C5_WICKED_NYMPH
1405,13327,50,Group_5 // TENGU
1030,1498,50,Group_5 // ANACONDAQ
2904,1498,250,Group_5 // C4_ANACONDAQ
1205,13454,50,Group_5 // EXECUTIONER
1135,28106,50,Group_5 // KOBOLD_3
1106,28705,50,Group_5 // DESERT_WOLF
1259,1498,250,Group_5 // GRYPHON
1310,28106,50,Group_5 // MAJORUROS
2767,28106,250,Group_5 // C4_MAJORUROS
1736,1839,50,Group_5 // ALIOT
1296,16040,50,Group_5 // KOBOLD_LEADER
1204,28705,50,Group_5 // TIRFING
1204,13454,50,Group_5 // TIRFING
1993,1443,50,Group_5 // NAGA
1390,1939,50,Group_5 // VIOLY
2621,1939,250,Group_5 // C5_VIOLY
2622,1939,250,Group_5 // C1_VIOLY
2623,1939,250,Group_5 // C2_VIOLY
1295,18130,50,None // OWL_BARON
1303,2025,50,None // GIANT_HONET
2821,2025,250,None // C3_GIANT_HONET
1702,21015,50,Group_5 // RETRIBUTION
2353,28106,50,Group_5 // N_MINOROUS
2684,21015,250,Group_5 // C4_RETRIBUTION
2685,21015,250,Group_5 // C5_RETRIBUTION
2686,21015,250,Group_5 // C1_RETRIBUTION
1219,21015,50,Group_5 // KNIGHT_OF_ABYSS
1703,1939,50,Group_5 // SOLACE
2650,1939,250,Group_5 // C5_SOLACE
2041,28705,50,Group_5 // MYSTELTAINN
2041,13454,50,Group_5 // MYSTELTAINN
2041,21015,50,Group_5 // MYSTELTAINN
1830,18130,50,None // BOW_GUARDIAN
1653,28705,50,Group_5 // WHIKEBAIN
1655,1839,50,Group_5 // EREND
1655,16040,50,Group_5 // EREND
1657,1680,50,None // RAWREL
1829,21015,50,Group_5 // SWORD_GUARDIAN
2692,1680,250,None // C3_RAWREL
1654,13454,50,Group_5 // ARMAIA
1654,28106,50,Group_5 // ARMAIA
1656,1939,50,Group_5 // KAVAC
1656,18130,50,None // KAVAC
1652,13454,50,Group_5 // YGNIZEM
1652,21015,50,Group_5 // YGNIZEM
1290,28705,50,Group_5 // SKELETON_GENERAL
2658,28705,250,Group_5 // C3_SKELETON_GENERAL
2659,28705,250,Group_5 // C4_SKELETON_GENERAL
1658,21015,500,Group_5 // B_YGNIZEM
1301,16040,50,Group_5 // AM_MUT
2362,28604,50,Group_5 // N_AMON_RA
@@ -0,0 +1,11 @@
###########################################################################
# Item Random Option Database
###########################################################################
#
# Item Random Option Settings
#
###########################################################################
# - Id Item Random Option ID matching the ID defined in enumvar.lub in the client.
# Option Item Random Option constant.
# Script Bonus script used for option.
###########################################################################
@@ -0,0 +1,25 @@
###########################################################################
# Item Random Option Group Database
###########################################################################
#
# Item Random Option Group Settings
#
###########################################################################
# - Id Item Random Option Group ID.
# Group Item Random Option Group constant.
# Slots: Slot in which an Item Random Option is guaranteed to be applied. Max of MAX_ITEM_RDM_OPT.
# - Slot Slot number.
# Options: List of possible Item Random Options for slot.
# - Option Item Random Option constant.
# MinValue Minimum value. (Default: 0)
# MaxValue Maximum value. (Default: 0)
# Param Parameter value. (Default: 0)
# Chance Chance applied specifically to this Item Random Option (1 = 0.01%, 10000 = 100%). (Default: 0)
# MaxRandom Maximum amount of random options applied. These options are not guaranteed to be applied. Max of (MAX_ITEM_RDM_OPT - Total 'Slots'). (Default: 0)
# Random: List of possible Item Random Options for remaining slots. (Optional)
# - Option Item Random Option constant.
# MinValue Minimum value. (Default: 0)
# MaxValue Maximum value. (Default: 0)
# Param Parameter value. (Default: 0)
# Chance Chance applied specifically to this Item Random Option (1 = 0.01%, 10000 = 100%). (Default: 0)
###########################################################################
@@ -129,7 +129,7 @@ namespace rathena {
}

/**
* Get a random value from the given map
* Get a random value from the given unordered map
* @param map: Unordered Map to search through
* @return A random value by reference
*/
@@ -141,6 +141,19 @@ namespace rathena {
return it->second;
}

/**
* Get a random value from the given vector
* @param vec: Vector to search through
* @return A random value by reference
*/
template <typename K> K &vector_random(std::vector<K> &vec) {
auto it = vec.begin();

std::advance(it, rnd_value(0, vec.size() - 1));

return *it;
}

/**
* Get an iterator element
* @param vec: Vector to search through
@@ -7411,9 +7411,9 @@ ACMD_FUNC(mobinfo)
j = 0;
for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
int droprate;
if (mob->dropitem[i].nameid == 0 || mob->dropitem[i].p < 1 || (item_data = itemdb_exists(mob->dropitem[i].nameid)) == NULL)
if (mob->dropitem[i].nameid == 0 || mob->dropitem[i].rate < 1 || (item_data = itemdb_exists(mob->dropitem[i].nameid)) == NULL)
continue;
droprate = mob->dropitem[i].p;
droprate = mob->dropitem[i].rate;

#ifdef RENEWAL_DROP
if( battle_config.atcommand_mobinfo_type ) {
@@ -7450,7 +7450,7 @@ ACMD_FUNC(mobinfo)
if (mob->mvpitem[i].nameid == 0 || (item_data = itemdb_exists(mob->mvpitem[i].nameid)) == NULL)
continue;
//Because if there are 3 MVP drops at 50%, the first has a chance of 50%, the second 25% and the third 12.5%
mvppercent = (float)mob->mvpitem[i].p * mvpremain / 10000.0f;
mvppercent = (float)mob->mvpitem[i].rate * mvpremain / 10000.0f;
if(battle_config.item_drop_mvp_mode == 0) {
mvpremain -= mvppercent;
}

Large diffs are not rendered by default.

@@ -4,6 +4,7 @@
#ifndef ITEMDB_HPP
#define ITEMDB_HPP

#include <map>
#include <vector>

#include "../common/database.hpp"
@@ -919,28 +920,66 @@ struct item_data
// Struct for item random option [Secret]
struct s_random_opt_data
{
unsigned short id;
struct script_code *script;
};
uint16 id;
std::string name;
script_code *script;

/// Enum for Random Option Groups
enum Random_Option_Group {
RDMOPTG_None = 0,
RDMOPTG_Crimson_Weapon,
~s_random_opt_data() {
if (script)
script_free_code(script);
}
};

/// Struct for random option group entry
struct s_random_opt_group_entry {
struct s_item_randomoption option[MAX_ITEM_RDM_OPT];
uint16 id;
int16 min_value, max_value;
int8 param;
uint16 chance;
};

/// Struct for Random Option Group
struct s_random_opt_group {
uint8 id;
struct s_random_opt_group_entry *entries;
uint16 total;
uint16 id;
std::string name;
std::map<uint16, std::vector<std::shared_ptr<s_random_opt_group_entry>>> slots;
uint16 max_random;
std::vector<std::shared_ptr<s_random_opt_group_entry>> random_options;
};

class RandomOptionDatabase : public TypesafeYamlDatabase<uint16, s_random_opt_data> {
public:
RandomOptionDatabase() : TypesafeYamlDatabase("RANDOM_OPTION_DB", 1) {

}

const std::string getDefaultLocation();
uint64 parseBodyNode(const YAML::Node &node);

// Additional
bool option_exists(std::string name);
bool option_get_id(std::string name, uint16 &id);
};

extern RandomOptionDatabase random_option_db;

class RandomOptionGroupDatabase : public TypesafeYamlDatabase<uint16, s_random_opt_group> {
public:
RandomOptionGroupDatabase() : TypesafeYamlDatabase("RANDOM_OPTION_GROUP", 1) {

}

const std::string getDefaultLocation();
uint64 parseBodyNode(const YAML::Node &node);

// Additional
bool add_option(const YAML::Node &node, std::shared_ptr<s_random_opt_group_entry> &entry);
bool option_exists(std::string name);
bool option_get_id(std::string name, uint16 &id);
};

extern RandomOptionGroupDatabase random_option_group;

class ItemDatabase : public TypesafeCachedYamlDatabase<t_itemid, item_data> {
public:
ItemDatabase() : TypesafeCachedYamlDatabase("ITEM_DB", 1) {
@@ -1024,9 +1063,6 @@ char itemdb_pc_get_itemgroup(uint16 group_id, bool identify, struct map_session_

bool itemdb_parse_roulette_db(void);

struct s_random_opt_data *itemdb_randomopt_exists(short id);
struct s_random_opt_group *itemdb_randomopt_group_exists(int id);

void itemdb_reload(void);

void do_final_itemdb(void);
@@ -321,8 +321,8 @@
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_misc.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_misc.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_noequip.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_noequip.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_package.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_package.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_randomopt_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_randomopt_db.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_randomopt_group.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_randomopt_group.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_randomopt_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_randomopt_db.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_randomopt_group.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_randomopt_group.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_violetbox.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_violetbox.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\job_basehpsp_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\job_basehpsp_db.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\job_db1.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\job_db1.txt')" />
@@ -30,6 +30,7 @@
#include "guild.hpp"
#include "homunculus.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "log.hpp"
#include "map.hpp"
#include "mercenary.hpp"
@@ -2096,21 +2097,63 @@ static TIMER_FUNC(mob_ai_hard){
return 0;
}

/**
* Assign random option values to an item
* @param item_option: Random option on the item
* @param option: Options to assign
*/
void mob_setitem_option(s_item_randomoption &item_option, const std::shared_ptr<s_random_opt_group_entry> &option) {
item_option.id = option->id;
item_option.value = rnd_value(option->min_value, option->max_value);
item_option.param = option->param;
}

/**
* Set random option for item when dropped from monster
* @param itm Item data
* @param mobdrop Drop data
* @param item: Item data
* @param mobdrop: Drop data
* @author [Cydh]
**/
void mob_setdropitem_option(struct item *itm, struct s_mob_drop *mobdrop) {
struct s_random_opt_group *g = NULL;
if (!itm || !mobdrop || mobdrop->randomopt_group == RDMOPTG_None)
void mob_setdropitem_option(item *item, s_mob_drop *mobdrop) {
if (!item || !mobdrop)
return;
if ((g = itemdb_randomopt_group_exists(mobdrop->randomopt_group)) && g->total) {
int r = rnd()%g->total;
if (&g->entries[r]) {
memcpy(&itm->option, &g->entries[r], sizeof(itm->option));
return;

std::shared_ptr<s_random_opt_group> group = random_option_group.find(mobdrop->randomopt_group);

if (group != nullptr) {
// Apply Must options
for (size_t i = 0; i < group->slots.size(); i++) {
// Try to apply an entry
for (size_t j = 0, max = group->slots[static_cast<uint16>(i)].size() * 3; j < max; j++) {
std::shared_ptr<s_random_opt_group_entry> option = util::vector_random(group->slots[static_cast<uint16>(i)]);

if (rnd() % 10000 < option->chance) {
mob_setitem_option(item->option[i], option);
break;
}
}

// If no entry was applied, assign one
if (item->option[i].id == 0) {
std::shared_ptr<s_random_opt_group_entry> option = util::vector_random(group->slots[static_cast<uint16>(i)]);

// Apply an entry without checking the chance
mob_setitem_option(item->option[i], option);
}
}

// Apply Random options (if available)
if (group->max_random > 0) {
for (size_t i = 0; i < min(group->max_random, MAX_ITEM_RDM_OPT); i++) {
// If item already has an option in this slot, skip it
if (item->option[i].id > 0)
continue;

std::shared_ptr<s_random_opt_group_entry> option = util::vector_random(group->random_options);

if (rnd() % 10000 < option->chance)
mob_setitem_option(item->option[i], option);
}
}
}
}
@@ -2723,7 +2766,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
continue;
if ( !(it = itemdb_exists(md->db->dropitem[i].nameid)) )
continue;
drop_rate = md->db->dropitem[i].p;
drop_rate = md->db->dropitem[i].rate;
if (drop_rate <= 0) {
if (battle_config.drop_rate0item)
continue;
@@ -2800,7 +2843,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
}
// Announce first, or else ditem will be freed. [Lance]
// By popular demand, use base drop rate for autoloot code. [Skotlex]
mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].p, homkillonly);
mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].rate, homkillonly);
}

// Ore Discovery [Celest]
@@ -2931,7 +2974,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
if(mdrop[i].nameid == 0 || !(i_data = itemdb_exists(mdrop[i].nameid)))
continue;

temp = mdrop[i].p;
temp = mdrop[i].rate;
if (temp != 10000) {
if(temp <= 0 && !battle_config.drop_rate0item)
temp = 1;
@@ -4274,7 +4317,7 @@ static bool mob_parse_dbrow(char** str)

if( entry.mvpitem[i].nameid ){
if( itemdb_search(entry.mvpitem[i].nameid) ){
entry.mvpitem[i].p = atoi(str[32+i*2]);
entry.mvpitem[i].rate = atoi(str[32+i*2]);
continue;
}else{
ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(MVP-Drop %d)\n", entry.name, mob_id, str[31+i*2], ( i / 2 ) + 1 );
@@ -4283,7 +4326,7 @@ static bool mob_parse_dbrow(char** str)

// Delete the item
entry.mvpitem[i].nameid = 0;
entry.mvpitem[i].p = 0;
entry.mvpitem[i].rate = 0;
}

for(i = 0; i < MAX_MOB_DROP; i++) {
@@ -4293,7 +4336,7 @@ static bool mob_parse_dbrow(char** str)

if( entry.dropitem[i].nameid ){
if( itemdb_search( entry.dropitem[i].nameid ) ){
entry.dropitem[i].p = atoi(str[k+1]);
entry.dropitem[i].rate = atoi(str[k+1]);
continue;
}else{
ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(Drop %d)\n", entry.name, mob_id, str[k], ( i / 2 ) + 1 );
@@ -4302,7 +4345,7 @@ static bool mob_parse_dbrow(char** str)

// Delete the item
entry.dropitem[i].nameid = 0;
entry.dropitem[i].p = 0;
entry.dropitem[i].rate = 0;
}

db = mob_db(mob_id);
@@ -5239,25 +5282,18 @@ static bool mob_readdb_drop(char* str[], int columns, int current) {
}

drop[i].nameid = nameid;
drop[i].p = rate;
drop[i].steal_protected = (flag) ? 1 : 0;
drop[i].rate = rate;
drop[i].steal_protected = (flag) ? true : false;
drop[i].randomopt_group = 0;

if (columns > 3) {
int64 randomopt_group_tmp = -1;
int randomopt_group = -1;
uint16 randomopt_group;

if (!script_get_constant(trim(str[3]), &randomopt_group_tmp)) {
if (!random_option_group.option_get_id(trim(str[3]), randomopt_group)) {
ShowError("mob_readdb_drop: Invalid 'randopt_groupid' '%s' for monster '%hu'.\n", str[3], mobid);
return false;
}
randomopt_group = static_cast<int>(randomopt_group_tmp);
if (randomopt_group == RDMOPTG_None)
return true;
if (!itemdb_randomopt_group_exists(randomopt_group)) {
ShowError("mob_readdb_drop: 'randopt_groupid' '%s' cannot be found in DB for monster '%hu'.\n", str[3], mobid);
return false;
}

drop[i].randomopt_group = randomopt_group;
}
}
@@ -5293,7 +5329,7 @@ static void mob_drop_ratio_adjust(void){

for( j = 0; j < MAX_MVP_DROP_TOTAL; j++ ){
nameid = mob->mvpitem[j].nameid;
rate = mob->mvpitem[j].p;
rate = mob->mvpitem[j].rate;

if( nameid == 0 || rate == 0 ){
continue;
@@ -5313,9 +5349,9 @@ static void mob_drop_ratio_adjust(void){

// Item is not known anymore(should never happen)
if( !id ){
ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %u)\n", mob->name, mob_id, nameid );
ShowWarning( "Monster \"%s\"(id:%u) is dropping an unknown item(id: %u)\n", mob->name, mob_id, nameid );
mob->mvpitem[j].nameid = 0;
mob->mvpitem[j].p = 0;
mob->mvpitem[j].rate = 0;
continue;
}

@@ -5325,15 +5361,15 @@ static void mob_drop_ratio_adjust(void){
}
}

mob->mvpitem[j].p = rate;
mob->mvpitem[j].rate = rate;
}

for( j = 0; j < MAX_MOB_DROP_TOTAL; j++ ){
unsigned short ratemin, ratemax;
bool is_treasurechest;

nameid = mob->dropitem[j].nameid;
rate = mob->dropitem[j].p;
rate = mob->dropitem[j].rate;

if( nameid == 0 || rate == 0 ){
continue;
@@ -5345,7 +5381,7 @@ static void mob_drop_ratio_adjust(void){
if( !id ){
ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %u)\n", mob->name, mob_id, nameid );
mob->dropitem[j].nameid = 0;
mob->dropitem[j].p = 0;
mob->dropitem[j].rate = 0;
continue;
}

@@ -5427,7 +5463,7 @@ static void mob_drop_ratio_adjust(void){
}
}

mob->dropitem[j].p = rate;
mob->dropitem[j].rate = rate;
}
}

@@ -5639,7 +5675,7 @@ void mob_reload_itemmob_data(void) {
id = itemdb_search(pair.second.dropitem[d].nameid);

for (k = 0; k < MAX_SEARCH; k++) {
if (id->mob[k].chance <= pair.second.dropitem[d].p)
if (id->mob[k].chance <= pair.second.dropitem[d].rate)
break;
}

@@ -5648,7 +5684,7 @@ void mob_reload_itemmob_data(void) {

if (id->mob[k].id != pair.first)
memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
id->mob[k].chance = pair.second.dropitem[d].p;
id->mob[k].chance = pair.second.dropitem[d].rate;
id->mob[k].id = pair.first;
}
}
@@ -162,9 +162,9 @@ struct s_mob_lootitem {
/// Struct for monster's drop item
struct s_mob_drop {
t_itemid nameid;
int p;
uint8 randomopt_group;
unsigned steal_protected : 1;
uint32 rate;
uint16 randomopt_group;
bool steal_protected;
};

struct mob_db {
@@ -5835,7 +5835,7 @@ bool pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 ski
// Try dropping one item, in the order from first to last possible slot.
// Droprate is affected by the skill success rate.
for( i = 0; i < MAX_STEAL_DROP; i++ )
if( md->db->dropitem[i].nameid > 0 && !md->db->dropitem[i].steal_protected && itemdb_exists(md->db->dropitem[i].nameid) && rnd() % 10000 < md->db->dropitem[i].p
if( md->db->dropitem[i].nameid > 0 && !md->db->dropitem[i].steal_protected && itemdb_exists(md->db->dropitem[i].nameid) && rnd() % 10000 < md->db->dropitem[i].rate
#ifndef RENEWAL
* rate/100.
#endif
@@ -5867,11 +5867,11 @@ bool pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 ski
log_pick_mob(md, LOG_TYPE_STEAL, -1, &tmp_item);

//A Rare Steal Global Announce by Lupus
if(md->db->dropitem[i].p<=battle_config.rare_drop_announce) {
if(md->db->dropitem[i].rate <= battle_config.rare_drop_announce) {
struct item_data *i_data;
char message[128];
i_data = itemdb_search(itemid);
sprintf (message, msg_txt(sd,542), (sd->status.name[0])?sd->status.name :"GM", md->db->jname, i_data->ename.c_str(), (float)md->db->dropitem[i].p/100);
sprintf (message, msg_txt(sd,542), (sd->status.name[0])?sd->status.name :"GM", md->db->jname, i_data->ename.c_str(), (float)md->db->dropitem[i].rate / 100);
//MSG: "'%s' stole %s's %s (chance: %0.02f%%)"
intif_broadcast(message, strlen(message) + 1, BC_DEFAULT);
}
@@ -10621,7 +10621,7 @@ BUILDIN_FUNC(getmobdrops)
continue;

mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid);
mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p);
mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].rate);

j++;
}
@@ -17424,7 +17424,7 @@ BUILDIN_FUNC(addmonsterdrop)
}
if(c) { //Fill in the slot with the item and rate
mob->dropitem[c].nameid = item_id;
mob->dropitem[c].p = (rate > 10000)?10000:rate;
mob->dropitem[c].rate = (rate > 10000)?10000:rate;
mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
script_pushint(st,1);
} else //No place to put the new drop
@@ -17466,7 +17466,7 @@ BUILDIN_FUNC(delmonsterdrop)
for(i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
if(mob->dropitem[i].nameid == item_id) {
mob->dropitem[i].nameid = 0;
mob->dropitem[i].p = 0;
mob->dropitem[i].rate = 0;
mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
@@ -23368,7 +23368,6 @@ BUILDIN_FUNC(getequiprandomoption) {
*/
BUILDIN_FUNC(setrandomoption) {
struct map_session_data *sd;
struct s_random_opt_data *opt;
int pos, index, id, value, param, ep;
int i = -1;
if (!script_charid2sd(7, sd))
@@ -23379,7 +23378,9 @@ BUILDIN_FUNC(setrandomoption) {
value = script_getnum(st, 5);
param = script_getnum(st, 6);

if ((opt = itemdb_randomopt_exists((short)id)) == NULL) {
std::shared_ptr<s_random_opt_data> opt = random_option_db.find(static_cast<uint16>(id));

if (opt == nullptr) {
ShowError("buildin_setrandomoption: Random option ID %d does not exists.\n", id);
script_pushint(st, 0);
return SCRIPT_CMD_FAILURE;
@@ -4564,10 +4564,6 @@
export_constant(MOBG_ClassChange);
export_constant(MOBG_Taekwon_Mission);

/* Item Random Option Group */
export_constant(RDMOPTG_None);
export_constant(RDMOPTG_Crimson_Weapon);

/* random option attributes */
export_constant(ROA_ID);
export_constant(ROA_VALUE);
@@ -4127,15 +4127,15 @@ int status_calc_pc_sub(struct map_session_data* sd, enum e_status_calc_opt opt)
continue;

if (sd->inventory_data[index]) {
int j;
struct s_random_opt_data *data;
for (j = 0; j < MAX_ITEM_RDM_OPT; j++) {
for (uint8 j = 0; j < MAX_ITEM_RDM_OPT; j++) {
short opt_id = sd->inventory.u.items_inventory[index].option[j].id;

if (!opt_id)
continue;
current_equip_opt_index = j;
data = itemdb_randomopt_exists(opt_id);

std::shared_ptr<s_random_opt_data> data = random_option_db.find(opt_id);

if (!data || !data->script)
continue;
if (!pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT) && itemdb_isNoEquip(sd->inventory_data[index], sd->bl.m))

Large diffs are not rendered by default.