Revamped Laphine UIs (#6625)

Fixes #3302
Closes #4348

Thanks for the initial release by @Cydh in #4348 and everyone that contributed to it.

All existing data was migrated and cleaned up where necessary.
Thanks to @Everade for his help here.

Laphine UIs are now fully yamlified and not dependent on the script engine.
They make use of new item group features and of the already existing random option group feature.
This way they will be far easier to be maintained, even though they are a little less customize able.

Thanks to @limitro, @CairoLee, @dimasshotta and everyone else who contributed!

Co-authored-by: Cydh <cydh.ramdh@gmail.com>
Co-authored-by: Everade <Everade@users.noreply.github.com>
Co-authored-by: Aleos <aleos89@users.noreply.github.com>
This commit is contained in:
Lemongrass3110 2022-02-22 21:52:27 +01:00 committed by GitHub
parent 5227167716
commit 08192a35bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 19587 additions and 468 deletions

View File

@ -35,10 +35,13 @@
# Stacked Whether stackable items are given stacked or not. (Default: true)
# Named Inscribes the item with the obtainer's name. (Default: false)
# Bound Binds the obtained item. (Default: None)
# RandomOptionGroup Applies random options of this group to all equipable items (Default: None)
# RefineMinimum Applies at least this refine level to all equipable items (Default: 0)
# RefineMaximum Applies at most this refine level to all equipable items (Default: 0)
# Clear Remove the given item. (Optional)
# Clear Remove the given SubGroup. (Optional)
###########################################################################
Header:
Type: ITEM_GROUP_DB
Version: 1
Version: 2

View File

@ -0,0 +1,37 @@
# This file is a part of rAthena.
# Copyright(C) 2022 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/>.
#
###########################################################################
# Laphine Synthesis Database
###########################################################################
#
# Laphine Synthesis Settings
#
###########################################################################
# - Item Item that triggers Laphine Synthesis.
# RewardGroup Item Group that will be given out as a reward.
# RequiredRequirementsCount Amount of requirements that have to be fulfilled. (Default: 1)
# MinimumRefine Minimum refine level of the required items. (Default: 0)
# MaximumRefine Maximum refine level of the required items. (Default: MAX_REFINE)
# Requirements: List of possible requirement items.
# - Item Item name of the required item
# Amount Amount of specific required item. (Default: 1)
###########################################################################
Header:
Type: LAPHINE_SYNTHESIS_DB
Version: 1

View File

@ -0,0 +1,40 @@
# This file is a part of rAthena.
# Copyright(C) 2022 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/>.
#
###########################################################################
# Laphine Upgrade Database
###########################################################################
#
# Laphine Upgrade Settings
#
###########################################################################
# - Item Item that triggers Laphine Upgrade.
# RandomOptionGroup Name of the random option group that will be applied. (Default: none)
# ResultRefine Absolute refine level after the upgrade. (Default: none)
# ResultRefineMinimum Minimum refine level after the upgrade. (Default: none)
# ResultRefineMaximum Maximum refine level after the upgrade. (Default: none)
# MinimumRefine Minimum refine level of the required items. (Default: 0)
# MaximumRefine Maximum refine level of the required items. (Default: MAX_REFINE)
# RequiredRandomOptions How many random options have to be in the item? (Default: 0)
# CardsAllowed Are cards allowed in the target item? (Default: no)
# TargetItems: List of possible target item(s)
# - Item Item name of the possible target item
###########################################################################
Header:
Type: LAPHINE_UPGRADE_DB
Version: 1

View File

@ -35,13 +35,16 @@
# Stacked Whether stackable items are given stacked or not. (Default: true)
# Named Inscribes the item with the obtainer's name. (Default: false)
# Bound Binds the obtained item. (Default: None)
# RandomOptionGroup Applies random options of this group to all equipable items (Default: None)
# RefineMinimum Applies at least this refine level to all equipable items (Default: 0)
# RefineMaximum Applies at most this refine level to all equipable items (Default: 0)
# Clear Remove the given item. (Optional)
# Clear Remove the given SubGroup. (Optional)
###########################################################################
Header:
Type: ITEM_GROUP_DB
Version: 1
Version: 2
Footer:
Imports:

43
db/laphine_synthesis.yml Normal file
View File

@ -0,0 +1,43 @@
# This file is a part of rAthena.
# Copyright(C) 2022 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/>.
#
###########################################################################
# Laphine Synthesis Database
###########################################################################
#
# Laphine Synthesis Settings
#
###########################################################################
# - Item Item that triggers Laphine Synthesis.
# RewardGroup Item Group that will be given out as a reward.
# RequiredRequirementsCount Amount of requirements that have to be fulfilled. (Default: 1)
# MinimumRefine Minimum refine level of the required items. (Default: 0)
# MaximumRefine Maximum refine level of the required items. (Default: MAX_REFINE)
# Requirements: List of possible requirement items.
# - Item Item name of the required item
# Amount Amount of specific required item. (Default: 1)
###########################################################################
Header:
Type: LAPHINE_SYNTHESIS_DB
Version: 1
Footer:
Imports:
- Path: db/re/laphine_synthesis.yml
Mode: Renewal
- Path: db/import/laphine_synthesis.yml

46
db/laphine_upgrade.yml Normal file
View File

@ -0,0 +1,46 @@
# This file is a part of rAthena.
# Copyright(C) 2022 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/>.
#
###########################################################################
# Laphine Upgrade Database
###########################################################################
#
# Laphine Upgrade Settings
#
###########################################################################
# - Item Item that triggers Laphine Upgrade.
# RandomOptionGroup Name of the random option group that will be applied. (Default: none)
# ResultRefine Absolute refine level after the upgrade. (Default: none)
# ResultRefineMinimum Minimum refine level after the upgrade. (Default: none)
# ResultRefineMaximum Maximum refine level after the upgrade. (Default: none)
# MinimumRefine Minimum refine level of the required items. (Default: 0)
# MaximumRefine Maximum refine level of the required items. (Default: MAX_REFINE)
# RequiredRandomOptions How many random options have to be in the item? (Default: 0)
# CardsAllowed Are cards allowed in the target item? (Default: no)
# TargetItems: List of possible target item(s)
# - Item Item name of the possible target item
###########################################################################
Header:
Type: LAPHINE_UPGRADE_DB
Version: 1
Footer:
Imports:
- Path: db/re/laphine_upgrade.yml
Mode: Renewal
- Path: db/import/laphine_upgrade.yml

View File

@ -35,13 +35,16 @@
# Stacked Whether stackable items are given stacked or not. (Default: true)
# Named Inscribes the item with the obtainer's name. (Default: false)
# Bound Binds the obtained item. (Default: None)
# RandomOptionGroup Applies random options of this group to all equipable items (Default: None)
# RefineMinimum Applies at least this refine level to all equipable items (Default: 0)
# RefineMaximum Applies at most this refine level to all equipable items (Default: 0)
# Clear Remove the given item. (Optional)
# Clear Remove the given SubGroup. (Optional)
###########################################################################
Header:
Type: ITEM_GROUP_DB
Version: 1
Version: 2
Body:
- Group: ACCESORY

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4492
db/re/laphine_synthesis.yml Normal file

File diff suppressed because it is too large Load Diff

5065
db/re/laphine_upgrade.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2896,22 +2896,6 @@ is invalid or if there is no item in the equipment slot.
---------------------------------------
*refineui({<char id>})
Opens the refine UI for the attached player or the given character id.
This feature requires 2016-10-12aRagexeRE or newer.
---------------------------------------
*openstylist({<char id>})
Opens the stylist UI for the attached player or the given character id.
This feature requires packet version 2015-11-04 or newer.
---------------------------------------
*getareadropitem("<map name>",<x1>,<y1>,<x2>,<y2>,<item>)
This function will count all the items with the specified ID number lying on the
@ -8079,6 +8063,42 @@ The values given will be divided by 100 and transmitted as floating-point number
Default: -50000 (-50.0)
Maximum: -75000 (-75.0)
---------------------------------------
*refineui({<char id>})
Opens the refine UI for the attached player or the given character id.
This feature requires 2016-10-12aRagexeRE or newer.
---------------------------------------
*openstylist({<char id>})
Opens the stylist UI for the attached player or the given character id.
This feature requires packet version 2015-11-04 or newer.
---------------------------------------
*laphine_synthesis()
Opens the laphine synthesis UI for the attached player.
This feature requires packet version 2016-06-01 or newer.
This function is intended for use in item scripts.
---------------------------------------
*laphine_upgrade()
Opens the laphine upgrade UI for the attached player.
This feature requires packet version 2017-07-26 or newer.
This function is intended for use in item scripts.
---------------------------------------
\\
6,1.- Unit-related commands

View File

@ -18,6 +18,9 @@
# Stacked Whether stackable items are given stacked or not. (Default: true)
# Named Inscribes the item with the obtainer's name. (Default: false)
# Bound Binds the obtained item. (Default: None)
# RandomOptionGroup Applies random options of this group to all equipable items (Default: None)
# RefineMinimum Applies at least this refine level to all equipable items (Default: 0)
# RefineMaximum Applies at most this refine level to all equipable items (Default: 0)
# Clear Remove the given item. (Optional)
# Clear Remove the given SubGroup. (Optional)
###########################################################################

View File

@ -167,12 +167,13 @@ CREATE TABLE IF NOT EXISTS `npclog` (
# (Q)uest
# Private Airs(H)ip
# Barter Shop (J)
# Laphine systems (W)
CREATE TABLE IF NOT EXISTS `picklog` (
`id` int(11) NOT NULL auto_increment,
`time` datetime NOT NULL,
`char_id` int(11) NOT NULL default '0',
`type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J') NOT NULL default 'P',
`type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J','W') NOT NULL default 'P',
`nameid` int(10) unsigned NOT NULL default '0',
`amount` int(11) NOT NULL default '1',
`refine` tinyint(3) unsigned NOT NULL default '0',

View File

@ -0,0 +1,7 @@
ALTER TABLE `picklog`
MODIFY `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J') NOT NULL default 'P'
;
ALTER TABLE `zenylog`
MODIFY `type` enum('T','V','P','M','S','N','D','C','A','E','I','B','K','J') NOT NULL default 'S'
;

View File

@ -23085,6 +23085,345 @@ void clif_summon_hp_bar(struct mob_data& md) {
#endif
}
void clif_laphine_synthesis_open( struct map_session_data *sd, std::shared_ptr<s_laphine_synthesis> synthesis ){
#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO)
nullpo_retv( sd );
sd->state.laphine_synthesis = synthesis->item_id;
struct PACKET_ZC_LAPINEDDUKDDAK_OPEN p = {};
p.packetType = HEADER_ZC_LAPINEDDUKDDAK_OPEN;
p.itemId = client_nameid( synthesis->item_id );
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
void clif_parse_laphine_synthesis_close( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO)
sd->state.laphine_synthesis = 0;
#endif
}
enum e_laphine_synthesis_result : int16{
LAPHINE_SYNTHESIS_SUCCESS = 0,
LAPHINE_SYNTHESIS_AMOUNT = 5,
LAPHINE_SYNTHESIS_ITEM = 7
};
void clif_laphine_synthesis_result( struct map_session_data* sd, enum e_laphine_synthesis_result result ){
#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO)
nullpo_retv( sd );
struct PACKET_ZC_LAPINEDDUKDDAK_RESULT p = {};
p.packetType = HEADER_ZC_LAPINEDDUKDDAK_RESULT;
p.result = result;
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
void clif_parse_laphine_synthesis( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO)
if( sd->state.laphine_synthesis == 0 ){
return;
}
struct PACKET_CZ_LAPINEDDUKDDAK_ACK* p = (struct PACKET_CZ_LAPINEDDUKDDAK_ACK*)RFIFOP( fd, 0 );
if( sd->state.laphine_synthesis != p->itemId ){
return;
}
std::shared_ptr<s_laphine_synthesis> synthesis = laphine_synthesis_db.find( sd->state.laphine_synthesis );
if( synthesis == nullptr ){
return;
}
size_t count = ( p->packetLength - sizeof( struct PACKET_CZ_LAPINEDDUKDDAK_ACK ) ) / sizeof( struct PACKET_CZ_LAPINEDDUKDDAK_ACK_sub );
// Player sent more or less than actually required
if( count != synthesis->requiredRequirements ){
return;
}
// Check for duplicates
for( size_t i = 0; i < count; i++ ){
for( size_t j = i + 1; j < count; j++ ){
if( p->items[i].index == p->items[j].index ){
return;
}
}
}
for( size_t i = 0; i < count; i++ ){
int16 index = server_index( p->items[i].index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[i] == nullptr ){
return;
}
struct item* item = &sd->inventory.u.items_inventory[index];
std::shared_ptr<s_laphine_synthesis_requirement> requirement = util::umap_find( synthesis->requirements, item->nameid );
if( requirement == nullptr ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_ITEM );
return;
}
if( p->items[i].count != requirement->amount ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_AMOUNT );
return;
}
if( item->amount < requirement->amount ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_AMOUNT );
return;
}
if( item->refine < synthesis->minimumRefine ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_ITEM );
return;
}
if( item->refine > synthesis->maximumRefine ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_ITEM );
return;
}
}
int16 index = pc_search_inventory( sd, sd->state.laphine_synthesis );
if( index < 0 ){
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_ITEM );
return;
}
if( ( sd->inventory_data[index]->flag.delay_consume & DELAYCONSUME_NOCONSUME ) == 0 ){
if( pc_delitem( sd, index, 1, 0, 0, LOG_TYPE_CONSUME ) != 0 ){
return;
}
}
for( size_t i = 0; i < count; i++ ){
index = server_index( p->items[i].index );
if( pc_delitem( sd, index, p->items[i].count, 0, 0, LOG_TYPE_LAPHINE ) != 0 ){
return;
}
}
itemdb_group.pc_get_itemgroup( synthesis->rewardGroupId, true, sd );
clif_laphine_synthesis_result( sd, LAPHINE_SYNTHESIS_SUCCESS );
#endif
}
void clif_laphine_upgrade_open( struct map_session_data* sd, std::shared_ptr<s_laphine_upgrade> upgrade ){
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
nullpo_retv( sd );
sd->state.laphine_upgrade = upgrade->item_id;
struct PACKET_ZC_LAPINEUPGRADE_OPEN p = {};
p.packetType = HEADER_ZC_LAPINEUPGRADE_OPEN;
p.itemId = client_nameid( upgrade->item_id );
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
void clif_parse_laphine_upgrade_close( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
sd->state.laphine_upgrade = 0;
#endif
}
void clif_laphine_upgrade_result( struct map_session_data *sd, bool failed ){
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
struct PACKET_ZC_LAPINEUPGRADE_RESULT p = {};
p.packetType = HEADER_ZC_LAPINEUPGRADE_RESULT;
p.result = failed;
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
static void clif_item_preview( struct map_session_data *sd, int16 index ){
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
nullpo_retv( sd );
struct item* item = &sd->inventory.u.items_inventory[index];
struct PACKET_ZC_ITEM_PREVIEW p = {};
p.packetType = HEADER_ZC_ITEM_PREVIEW;
p.index = client_index( index );
#if PACKETVER_MAIN_NUM >= 20181017 || PACKETVER_RE_NUM >= 20181017 || PACKETVER_ZERO_NUM >= 20181024
p.isDamaged = item->attribute != 0;
#endif
p.refiningLevel = item->refine;
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200723
p.enchantgrade = item->enchantgrade;
#endif
clif_addcards( &p.slot, item );
clif_add_random_options( p.option_data, item );
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
void clif_parse_laphine_upgrade( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
if( sd->state.laphine_upgrade == 0 ){
return;
}
struct PACKET_CZ_LAPINEUPGRADE_MAKE_ITEM* p = (struct PACKET_CZ_LAPINEUPGRADE_MAKE_ITEM*)RFIFOP( fd, 0 );
if( sd->state.laphine_upgrade != p->itemId ){
return;
}
std::shared_ptr<s_laphine_upgrade> upgrade = laphine_upgrade_db.find( sd->state.laphine_upgrade );
if( upgrade == nullptr ){
return;
}
uint16 index = server_index( p->index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[index] == nullptr ){
return;
}
struct item* item = &sd->inventory.u.items_inventory[index];
// Not a valid target item
if( !util::vector_exists( upgrade->target_item_ids, item->nameid ) ){
clif_laphine_upgrade_result( sd, true );
return;
}
// If target item is not identified
if( item->identify == 0 ){
clif_laphine_upgrade_result( sd, true );
return;
}
// If target item is equipped
if( item->equip != 0 ){
clif_laphine_upgrade_result( sd, true );
return;
}
// Check minimum refine requirement
if( item->refine < upgrade->minimumRefine ){
clif_laphine_upgrade_result( sd, true );
return;
}
// Check maximum refine requirement
if( item->refine > upgrade->maximumRefine ){
clif_laphine_upgrade_result( sd, true );
return;
}
// If no cards are allowed
if( !upgrade->cardsAllowed ){
for( int i = 0; i < MAX_SLOTS; i++ ){
if( item->card[i] != 0 ){
clif_laphine_upgrade_result( sd, true );
return;
}
}
}
// If random options are required
if( upgrade->requiredRandomOptions > 0 ){
int i;
for( i = MAX_ITEM_RDM_OPT - 1; i >= 0; i-- ){
if( item->option[i].id != 0 ){
break;
}
}
if( ( i + 1 ) < upgrade->requiredRandomOptions ){
clif_laphine_upgrade_result( sd, true );
return;
}
}
int16 index2 = pc_search_inventory( sd, sd->state.laphine_upgrade );
if( index2 < 0 ){
clif_laphine_upgrade_result( sd, true );
return;
}
if( ( sd->inventory_data[index2]->flag.delay_consume & DELAYCONSUME_NOCONSUME ) == 0 ){
if( pc_delitem( sd, index2, 1, 0, 0, LOG_TYPE_CONSUME ) != 0 ){
return;
}
}
// Log removal of item
log_pick_pc( sd, LOG_TYPE_LAPHINE, -1, item );
// Visually remove it from the client
clif_delitem( sd, index, 1, 0 );
// Apply the random options
if( upgrade->randomOptionGroup != nullptr ){
upgrade->randomOptionGroup->apply( *item );
}
// Change the refine rate if needed
if( upgrade->resultRefine > 0 ){
// Absolute refine level change
item->refine = max( item->refine, upgrade->resultRefine );
}else if( upgrade->resultRefineMaximum > 0 ){
// If a minimum is specified it can also downgrade
if( upgrade->resultRefineMinimum ){
item->refine = rnd_value( upgrade->resultRefineMinimum, upgrade->resultRefineMaximum );
}else{
// Otherwise it can only be upgraded until the maximum, but not downgraded
item->refine = rnd_value( item->refine, upgrade->resultRefineMaximum );
}
}else if( upgrade->resultRefineMinimum > 0 ){
// No maximum has been specified, so it can be anything between minimum and MAX_REFINE
item->refine = rnd_value( upgrade->resultRefineMinimum, MAX_REFINE );
}
// Log retrieving the item again -> with the new options
log_pick_pc( sd, LOG_TYPE_LAPHINE, 1, item );
// Make it visible for the client again
clif_additem( sd, index, 1, 0 );
// Open a preview of the item
clif_item_preview( sd, index );
// Tell the client we are done
clif_laphine_upgrade_result( sd, false );
#endif
}
/*==========================================
* Main client packet processing function
*------------------------------------------*/

View File

@ -45,6 +45,8 @@ struct guild_log_entry;
enum e_guild_storage_log : uint16;
enum e_bg_queue_apply_ack : uint16;
enum e_instance_notify : uint8;
struct s_laphine_synthesis;
struct s_laphine_upgrade;
enum e_PacketDBVersion { // packet DB
MIN_PACKET_DB = 0x064,
@ -1190,4 +1192,8 @@ void clif_barter_extended_open( struct map_session_data& sd, struct npc_data& nd
void clif_summon_init(struct mob_data& md);
void clif_summon_hp_bar(struct mob_data& md);
// Laphine System
void clif_laphine_synthesis_open( struct map_session_data *sd, std::shared_ptr<s_laphine_synthesis> synthesis );
void clif_laphine_upgrade_open( struct map_session_data* sd, std::shared_ptr<s_laphine_upgrade> upgrade );
#endif /* CLIF_HPP */

View File

@ -2292,6 +2292,11 @@
packet(0x0A7D,-1);
#endif
#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO)
parseable_packet( HEADER_CZ_LAPINEDDUKDDAK_CLOSE, sizeof( struct PACKET_CZ_LAPINEDDUKDDAK_CLOSE ), clif_parse_laphine_synthesis_close, 0 );
parseable_packet( HEADER_CZ_LAPINEDDUKDDAK_ACK, -1, clif_parse_laphine_synthesis, 0 );
#endif
// 2016-06-22aRagexeRE
#if PACKETVER >= 20160622
packet(0x0A84,94);
@ -2344,6 +2349,11 @@
parseable_packet(0x0ACE,4,clif_parse_equipswitch_request_single,0);
#endif
#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO)
parseable_packet( HEADER_CZ_LAPINEUPGRADE_CLOSE, sizeof( struct PACKET_CZ_LAPINEUPGRADE_CLOSE ), clif_parse_laphine_upgrade_close, 0 );
parseable_packet( HEADER_CZ_LAPINEUPGRADE_MAKE_ITEM, sizeof( struct PACKET_CZ_LAPINEUPGRADE_MAKE_ITEM ), clif_parse_laphine_upgrade, 0 );
#endif
// 2017-08-30bRagexeRE
#if PACKETVER >= 20170830
packet(0x0ACB,12);

View File

@ -1259,6 +1259,392 @@ int16 ItemGroupDatabase::item_exists_pc(map_session_data *sd, uint16 group_id)
return -1;
}
const std::string LaphineSynthesisDatabase::getDefaultLocation(){
return std::string( db_path ) + "/laphine_synthesis.yml";
}
uint64 LaphineSynthesisDatabase::parseBodyNode( const YAML::Node& node ){
t_itemid item_id;
{
std::string name;
if( !this->asString( node, "Item", name ) ){
return 0;
}
std::shared_ptr<item_data> id = item_db.search_aegisname( name.c_str() );
if( id == nullptr ){
this->invalidWarning( node["Item"], "Unknown item \"%s\".\n", name.c_str() );
return 0;
}
item_id = id->nameid;
}
std::shared_ptr<s_laphine_synthesis> entry = this->find( item_id );
bool exists = entry != nullptr;
if( !exists ){
if( !this->nodesExist( node, { "RewardGroup", "Requirements" } ) ){
return 0;
}
entry = std::make_shared<s_laphine_synthesis>();
entry->item_id = item_id;
}
if( this->nodeExists( node, "RewardGroup" ) ){
std::string name;
if( !this->asString( node, "RewardGroup", name ) ){
return 0;
}
int64 constant;
if( !script_get_constant( ( "IG_" + name ).c_str(), &constant ) ){
this->invalidWarning( node["RewardGroup"], "Unknown reward group \"%s\".\n", name.c_str() );
return 0;
}
if( !itemdb_group.exists( (uint16)constant ) ){
this->invalidWarning( node["RewardGroup"], "Unknown reward group ID \"%" PRId64 "\".\n", constant );
return 0;
}
entry->rewardGroupId = (uint16)constant;
}else{
if( !exists ){
entry->rewardGroupId = 0;
}
}
if( this->nodeExists( node, "RequiredRequirementsCount" ) ){
uint16 amount;
if( !this->asUInt16( node, "RequiredRequirementsCount", amount ) ){
return 0;
}
entry->requiredRequirements = amount;
}else{
if( !exists ){
entry->requiredRequirements = 1;
}
}
if( this->nodeExists( node, "Requirements" ) ){
for( const YAML::Node& requirementNode : node["Requirements"] ){
std::string name;
if( !this->asString( requirementNode, "Item", name ) ){
return 0;
}
std::shared_ptr<item_data> id = item_db.search_aegisname( name.c_str() );
if( id == nullptr ){
this->invalidWarning( requirementNode["Item"], "Unknown item \"%s\".\n", name.c_str() );
return 0;
}
std::shared_ptr<s_laphine_synthesis_requirement> requirement = util::umap_find( entry->requirements, id->nameid );
bool requirement_exists = requirement != nullptr;
if( !requirement_exists ){
requirement = std::make_shared<s_laphine_synthesis_requirement>();
requirement->item_id = id->nameid;
}
if( this->nodeExists( requirementNode, "Amount" ) ){
uint16 amount;
if( !this->asUInt16( requirementNode, "Amount", amount ) ){
return 0;
}
if( amount > MAX_AMOUNT ){
this->invalidWarning( requirementNode["Amount"], "Amount %hu is too high, capping to MAX_AMOUNT...\n", amount );
amount = MAX_AMOUNT;
}
requirement->amount = amount;
}else{
if( !requirement_exists ){
requirement->amount = 1;
}
}
if( !requirement_exists ){
entry->requirements[id->nameid] = requirement;
}
}
}
if( this->nodeExists( node, "MinimumRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "MinimumRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["MinimumRefine"], "Minimum refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->minimumRefine = refine;
}else{
if( !exists ){
entry->minimumRefine = 0;
}
}
if( this->nodeExists( node, "MaximumRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "MaximumRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["MaximumRefine"], "Maximum refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->maximumRefine = refine;
}else{
if( !exists ){
entry->maximumRefine = MAX_REFINE;
}
}
if( !exists ){
this->put( entry->item_id, entry );
}
return 1;
}
LaphineSynthesisDatabase laphine_synthesis_db;
const std::string LaphineUpgradeDatabase::getDefaultLocation(){
return std::string( db_path ) + "/laphine_upgrade.yml";
}
uint64 LaphineUpgradeDatabase::parseBodyNode( const YAML::Node& node ){
t_itemid item_id;
{
std::string name;
if( !this->asString( node, "Item", name ) ){
return 0;
}
std::shared_ptr<item_data> id = item_db.search_aegisname( name.c_str() );
if( id == nullptr ){
this->invalidWarning( node["Item"], "Unknown item \"%s\".\n", name.c_str() );
return 0;
}
item_id = id->nameid;
}
std::shared_ptr<s_laphine_upgrade> entry = this->find( item_id );
bool exists = entry != nullptr;
if( !exists ){
if( !this->nodesExist( node, { "TargetItems" } ) ){
return 0;
}
entry = std::make_shared<s_laphine_upgrade>();
entry->item_id = item_id;
}
if( this->nodeExists( node, "RandomOptionGroup" ) ){
std::string name;
if( !this->asString( node, "RandomOptionGroup", name ) ){
return 0;
}
uint16 id;
if( !random_option_group.option_get_id( name, id ) ){
this->invalidWarning( node["RandomOptionGroup"], "Unknown random option group \"%s\".\n", name.c_str() );
return 0;
}
entry->randomOptionGroup = random_option_group.find( id );
}else{
if( !exists ){
entry->randomOptionGroup = nullptr;
}
}
if( this->nodeExists( node, "TargetItems" ) ){
for( const YAML::Node& targetNode : node["TargetItems"] ){
std::string name;
if( !this->asString( targetNode, "Item", name ) ){
return 0;
}
std::shared_ptr<item_data> id = item_db.search_aegisname( name.c_str() );
if( id == nullptr ){
this->invalidWarning( targetNode["Item"], "Unknown item \"%s\".\n", name.c_str() );
return 0;
}
if( !util::vector_exists( entry->target_item_ids, id->nameid ) ){
entry->target_item_ids.push_back( id->nameid );
}
}
}
if( this->nodeExists( node, "MinimumRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "MinimumRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["MinimumRefine"], "Minimum refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->minimumRefine = refine;
}else{
if( !exists ){
entry->minimumRefine = 0;
}
}
if( this->nodeExists( node, "MaximumRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "MaximumRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["MaximumRefine"], "Maximum refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->maximumRefine = refine;
}else{
if( !exists ){
entry->maximumRefine = MAX_REFINE;
}
}
if( this->nodeExists( node, "RequiredRandomOptions" ) ){
uint16 amount;
if( !this->asUInt16( node, "RequiredRandomOptions", amount ) ){
return 0;
}
if( amount > MAX_ITEM_RDM_OPT ){
this->invalidWarning( node["RequiredRandomOptions"], "Required random option amount %hu is too high, capping to MAX_ITEM_RDM_OPT...\n", amount );
amount = MAX_ITEM_RDM_OPT;
}
entry->requiredRandomOptions = amount;
}else{
if( !exists ){
entry->requiredRandomOptions = 0;
}
}
if( this->nodeExists( node, "CardsAllowed" ) ){
bool allowed;
if( !this->asBool( node, "CardsAllowed", allowed ) ){
return 0;
}
entry->cardsAllowed = true;
}else{
if( !exists ){
entry->cardsAllowed = false;
}
}
if( this->nodeExists( node, "ResultRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "ResultRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["ResultRefine"], "Result refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->resultRefine = refine;
}else{
if( !exists ){
entry->resultRefine = 0;
}
}
if( this->nodeExists( node, "ResultRefineMinimum" ) ){
uint16 refine;
if( !this->asUInt16( node, "ResultRefineMinimum", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["ResultRefineMinimum"], "Result refine minimum %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->resultRefineMinimum = refine;
}else{
if( !exists ){
entry->resultRefineMinimum = 0;
}
}
if( this->nodeExists( node, "ResultRefineMaximum" ) ){
uint16 refine;
if( !this->asUInt16( node, "ResultRefineMaximum", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["ResultRefineMaximum"], "Result refine maximum %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->resultRefineMaximum = refine;
}else{
if( !exists ){
entry->resultRefineMaximum = 0;
}
}
if( !exists ){
this->put( entry->item_id, entry );
}
return 1;
}
LaphineUpgradeDatabase laphine_upgrade_db;
/*==========================================
* Return item data from item name. (lookup)
* @param str Item Name
@ -1392,6 +1778,25 @@ static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std
for (uint16 i = 0; i < data->amount; i += get_amt) {
char flag = 0;
tmp.unique_id = data->GUID ? pc_generate_unique_id(sd) : 0; // Generate GUID
if( itemdb_isequip( data->nameid ) ){
if( data->refineMinimum > 0 && data->refineMaximum > 0 ){
tmp.refine = rnd_value( data->refineMinimum, data->refineMaximum );
}else if( data->refineMinimum > 0 ){
tmp.refine = rnd_value( data->refineMinimum, MAX_REFINE );
}else if( data->refineMaximum > 0 ){
tmp.refine = rnd_value( 1, data->refineMaximum );
}else{
tmp.refine = 0;
}
if( data->randomOptionGroup != nullptr ){
memset( tmp.option, 0, sizeof( tmp.option ) );
data->randomOptionGroup->apply( tmp );
}
}
if ((flag = pc_additem(sd, &tmp, get_amt, LOG_TYPE_SCRIPT))) {
clif_additem(sd, 0, 0, flag);
if (pc_candrop(sd, &tmp))
@ -1877,6 +2282,65 @@ uint64 ItemGroupDatabase::parseBodyNode(const YAML::Node &node) {
if (!entry_exists)
entry->bound = BOUND_NONE;
}
if( this->nodeExists( listit, "RandomOptionGroup" ) ){
std::string name;
if( !this->asString( listit, "RandomOptionGroup", name ) ){
return 0;
}
uint16 id;
if( !random_option_group.option_get_id( name, id ) ){
this->invalidWarning( listit["RandomOptionGroup"], "Unknown random option group \"%s\".\n", name.c_str() );
return 0;
}
entry->randomOptionGroup = random_option_group.find( id );
}else{
if( !entry_exists ){
entry->randomOptionGroup = nullptr;
}
}
if( this->nodeExists( listit, "RefineMinimum" ) ){
uint16 refine;
if( !this->asUInt16( listit, "RefineMinimum", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( listit["RefineMinimum"], "Minimum refine % hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->refineMinimum = refine;
}else{
if( !exists ){
entry->refineMinimum = 0;
}
}
if( this->nodeExists( listit, "RefineMaximum" ) ){
uint16 refine;
if( !this->asUInt16( listit, "RefineMaximum", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( listit["RefineMaximum"], "Maximum refine %hu is too high, capping to MAX_REFINE...\n", refine );
refine = MAX_REFINE;
}
entry->refineMaximum = refine;
}else{
if( !exists ){
entry->refineMaximum = 0;
}
}
}
}
}
@ -2734,6 +3198,51 @@ bool RandomOptionGroupDatabase::add_option(const YAML::Node &node, std::shared_p
return true;
}
void s_random_opt_group::apply( struct item& item ){
auto apply_sub = []( 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;
};
// Apply Must options
for( size_t i = 0; i < this->slots.size(); i++ ){
// Try to apply an entry
for( size_t j = 0, max = this->slots[static_cast<uint16>(i)].size() * 3; j < max; j++ ){
std::shared_ptr<s_random_opt_group_entry> option = util::vector_random( this->slots[static_cast<uint16>(i)] );
if( rnd() % 10000 < option->chance ){
apply_sub( 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( this->slots[static_cast<uint16>(i)] );
// Apply an entry without checking the chance
apply_sub( item.option[i], option );
}
}
// Apply Random options (if available)
if( this->max_random > 0 ){
for( size_t i = 0; i < min( this->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( this->random_options );
if( rnd() % 10000 < option->chance ){
apply_sub( item.option[i], option );
}
}
}
}
/**
* Reads and parses an entry from the item_randomopt_group.
* @param node: YAML node containing the entry.
@ -2906,10 +3415,15 @@ static void itemdb_read(void) {
aFree(dbsubpath2);
}
itemdb_group.load();
itemdb_combo.load();
random_option_db.load();
random_option_group.load();
itemdb_group.load();
itemdb_combo.load();
laphine_synthesis_db.load();
laphine_upgrade_db.load();
if (battle_config.feature_roulette)
itemdb_parse_roulette_db();
}
/*==========================================
@ -2941,21 +3455,12 @@ void itemdb_reload(void) {
struct s_mapiterator* iter;
struct map_session_data* sd;
item_db.clear();
itemdb_combo.clear();
itemdb_group.clear();
random_option_db.clear();
random_option_group.clear();
if (battle_config.feature_roulette)
itemdb_roulette_free();
do_final_itemdb();
// read new data
itemdb_read();
cashshop_reloaddb();
if (battle_config.feature_roulette)
itemdb_parse_roulette_db();
mob_reload_itemmob_data();
// readjust itemdb pointer cache for each player
@ -2980,6 +3485,8 @@ void do_final_itemdb(void) {
itemdb_group.clear();
random_option_db.clear();
random_option_group.clear();
laphine_synthesis_db.clear();
laphine_upgrade_db.clear();
if (battle_config.feature_roulette)
itemdb_roulette_free();
}
@ -2989,7 +3496,4 @@ void do_final_itemdb(void) {
*/
void do_init_itemdb(void) {
itemdb_read();
if (battle_config.feature_roulette)
itemdb_parse_roulette_db();
}

View File

@ -778,6 +778,118 @@ enum e_random_item_group {
IG_THIRD_JOB_STONE_MIDDLE_BOX2,
IG_THIRD_JOB_STONE_BOTTOM_BOX,
IG_THIRD_JOB_STONE_BOTTOM_BOX2,
IG_SHADOW_EXCHANGE_BOX,
IG_DROOPING_GUNSLINGER_SCROLL,
IG_ENCHANTSTONE_RECIPE,
IG_PET_EGG_BOX,
IG_COSTUME_EXCHANGE_BOX,
IG_FAN_MODIFICATION_KIT,
IG_UNIFORM_REPAIR_KIT,
IG_SCROLL_OF_FALLEN_ANGEL_WINGS,
IG_CLASS_SHADOW_BOX_WEAPON,
IG_CLASS_SHADOW_BOX_ARMOR,
IG_CLASS_SHADOW_BOX_SHOES,
IG_CLASS_SHADOW_BOX_SHIELD,
IG_CLASS_SHADOW_BOX_PENDANT,
IG_CLASS_SHADOW_BOX_EARRING,
IG_STATUSSHADOW_MIX,
IG_GEMSTONESHADOW_MIX,
IG_BEARERSSHADOW_MIX,
IG_COMPOSESHADOW_MIX,
IG_RACESHADOW_MIX,
IG_CANDY_POUCH_BLESSING_SCROLL_MELEE,
IG_CANDY_POUCH_BLESSING_SCROLL_RANGE,
IG_CANDY_POUCH_BLESSING_SCROLL_MAGIC,
IG_MAGICAL_BOOSTER_AMPLIFIER,
IG_MAGIC_CAT_HAND_SCROLL,
IG_INFINITYSHADOW_MIX,
IG_SILVER_STATUE,
IG_PHYSICALMAGICAL_MIX,
IG_IMMUNEDATHENA_MIX,
IG_HARDCHAMPTION_MIX,
IG_KINGBIRDANCIENT_MIX,
IG_CRITICALHIT_MIX,
IG_BS_ITEM_M_S_2,
IG_BS_ITEM_M_S_8,
IG_BS_ITEM_M_S_10,
IG_BS_ITEM_M_S_11,
IG_BS_ITEM_M_S_34,
IG_BS_ITEM_M_S_41,
IG_BS_ITEM_M_S_42,
IG_BS_ITEM_M_S_43,
IG_BS_ITEM_M_S_44,
IG_BS_SHA_M_S_1,
IG_BS_SHA_M_S_17,
IG_BS_SHA_M_S_18,
IG_BS_SHA_M_S_19,
IG_BS_SHA_M_S_20,
IG_BS_ITEM_M_S_4,
IG_BS_ITEM_M_S_6,
IG_BS_ITEM_M_S_7,
IG_BS_ITEM_M_S_12,
IG_BS_ITEM_M_S_13,
IG_BS_ITEM_M_S_15,
IG_BS_ITEM_M_S_28,
IG_BS_ITEM_M_S_29,
IG_BS_ITEM_M_S_31,
IG_BS_ITEM_M_S_32,
IG_BS_ITEM_M_S_33,
IG_BS_ITEM_M_S_36,
IG_BS_ITEM_M_S_37,
IG_BS_ITEM_M_S_38,
IG_BS_ITEM_M_S_39,
IG_BS_ITEM_M_S_40,
IG_BS_ITEM_M_S_45,
IG_BS_ITEM_M_S_46,
IG_BS_ITEM_M_S_47,
IG_BS_ITEM_M_S_48,
IG_BS_ITEM_M_S_49,
IG_BS_ITEM_M_S_50,
IG_BS_SHA_M_S_5,
IG_BS_SHA_M_S_6,
IG_BS_SHA_M_S_7,
IG_BS_SHA_M_S_8,
IG_BS_SHA_M_S_13,
IG_BS_SHA_M_S_15,
IG_BS_SHA_M_S_16,
IG_BS_SHA_M_S_23,
IG_BS_ITEM_M_S_5,
IG_BS_ITEM_M_S_9,
IG_BS_ITEM_M_S_14,
IG_BS_ITEM_M_S_16,
IG_BS_ITEM_M_S_17,
IG_BS_ITEM_M_S_19,
IG_BS_ITEM_M_S_27,
IG_BS_ITEM_M_S_35,
IG_BS_SHA_M_S_9,
IG_BS_SHA_M_S_10,
IG_BS_SHA_M_S_11,
IG_BS_SHA_M_S_21,
IG_BS_ITEM_M_S_1,
IG_BS_ITEM_M_S_3,
IG_BS_ITEM_M_S_18,
IG_BS_ITEM_M_S_20,
IG_BS_ITEM_M_S_21,
IG_BS_ITEM_M_S_22,
IG_BS_ITEM_M_S_23,
IG_BS_ITEM_M_S_24,
IG_BS_ITEM_M_S_25,
IG_BS_ITEM_M_S_26,
IG_BS_ITEM_M_S_30,
IG_BS_SHA_M_S_3,
IG_BS_SHA_M_S_4,
IG_BS_SHA_M_S_12,
IG_BS_SHA_M_S_14,
IG_BS_SHA_M_S_24,
IG_BS_SHA_M_S_25,
IG_BS_ITEM_M_S_51,
IG_ENCHANTSTONE_RECIPE_9M,
IG_IDTEST_SPECIAL,
IG_PERFECTSIZE_MIX,
IG_MAGICPIERCING_MIX,
IG_PIERCING_MIX,
IG_HASTY_MIX,
IG_ENCHANTSTONE_RECIPE_4M,
IG_MAX,
};
@ -866,6 +978,73 @@ public:
extern ComboDatabase itemdb_combo;
// Struct for item random option [Secret]
struct s_random_opt_data
{
uint16 id;
std::string name;
script_code *script;
~s_random_opt_data() {
if (script)
script_free_code(script);
}
};
/// Struct for random option group entry
struct s_random_opt_group_entry {
uint16 id;
int16 min_value, max_value;
int8 param;
uint16 chance;
};
/// Struct for Random Option Group
struct s_random_opt_group {
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;
public:
void apply( struct item& item );
};
class RandomOptionDatabase : public TypesafeYamlDatabase<uint16, s_random_opt_data> {
public:
RandomOptionDatabase() : TypesafeYamlDatabase("RANDOM_OPTION_DB", 1) {
}
const std::string getDefaultLocation() override;
uint64 parseBodyNode(const YAML::Node &node) override;
void loadingFinished() override;
// 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() override;
uint64 parseBodyNode(const YAML::Node &node) override;
// 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;
/// Struct of item group entry
struct s_item_group_entry
{
@ -878,6 +1057,9 @@ struct s_item_group_entry
isStacked, /// Whether stackable items are given stacked
isNamed; /// Named the item (if possible)
uint8 bound; /// Makes the item as bound item (according to bound type)
std::shared_ptr<s_random_opt_group> randomOptionGroup;
uint16 refineMinimum;
uint16 refineMaximum;
};
/// Struct of random group
@ -999,70 +1181,6 @@ struct item_data
int inventorySlotNeeded(int quantity);
};
// Struct for item random option [Secret]
struct s_random_opt_data
{
uint16 id;
std::string name;
script_code *script;
~s_random_opt_data() {
if (script)
script_free_code(script);
}
};
/// Struct for random option group entry
struct s_random_opt_group_entry {
uint16 id;
int16 min_value, max_value;
int8 param;
uint16 chance;
};
/// Struct for Random Option Group
struct s_random_opt_group {
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() override;
uint64 parseBodyNode(const YAML::Node &node) override;
void loadingFinished() override;
// 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() override;
uint64 parseBodyNode(const YAML::Node &node) override;
// 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> {
private:
std::unordered_map<std::string, std::shared_ptr<item_data>> nameToItemDataMap;
@ -1094,7 +1212,7 @@ extern ItemDatabase item_db;
class ItemGroupDatabase : public TypesafeCachedYamlDatabase<uint16, s_item_group_db> {
public:
ItemGroupDatabase() : TypesafeCachedYamlDatabase("ITEM_GROUP_DB", 1) {
ItemGroupDatabase() : TypesafeCachedYamlDatabase("ITEM_GROUP_DB", 2, 1) {
}
@ -1112,6 +1230,57 @@ public:
extern ItemGroupDatabase itemdb_group;
struct s_laphine_synthesis_requirement{
t_itemid item_id;
uint16 amount;
};
struct s_laphine_synthesis{
t_itemid item_id;
uint16 minimumRefine;
uint16 maximumRefine;
uint16 requiredRequirements;
std::unordered_map<t_itemid, std::shared_ptr<s_laphine_synthesis_requirement>> requirements;
uint16 rewardGroupId;
};
class LaphineSynthesisDatabase : public TypesafeYamlDatabase<t_itemid, s_laphine_synthesis>{
public:
LaphineSynthesisDatabase() : TypesafeYamlDatabase( "LAPHINE_SYNTHESIS_DB", 1 ){
}
const std::string getDefaultLocation();
uint64 parseBodyNode( const YAML::Node& node );
};
extern LaphineSynthesisDatabase laphine_synthesis_db;
struct s_laphine_upgrade{
t_itemid item_id;
std::vector<t_itemid> target_item_ids;
uint16 minimumRefine;
uint16 maximumRefine;
uint16 requiredRandomOptions;
bool cardsAllowed;
std::shared_ptr<s_random_opt_group> randomOptionGroup;
uint16 resultRefine;
uint16 resultRefineMinimum;
uint16 resultRefineMaximum;
};
class LaphineUpgradeDatabase : public TypesafeYamlDatabase<t_itemid, s_laphine_upgrade>{
public:
LaphineUpgradeDatabase() : TypesafeYamlDatabase( "LAPHINE_UPGRADE_DB", 1 ){
}
const std::string getDefaultLocation();
uint64 parseBodyNode( const YAML::Node& node );
};
extern LaphineUpgradeDatabase laphine_upgrade_db;
int itemdb_searchname_array(struct item_data** data, int size, const char *str);
struct item_data* itemdb_search(t_itemid nameid);
struct item_data* itemdb_exists(t_itemid nameid);

View File

@ -86,6 +86,7 @@ static char log_picktype2char(e_log_pick_type type)
case LOG_TYPE_QUEST: return 'Q'; // (Q)uest Item
case LOG_TYPE_PRIVATE_AIRSHIP: return 'H'; // Private Airs(H)ip
case LOG_TYPE_BARTER: return 'J'; // Barter Shop
case LOG_TYPE_LAPHINE: return 'W'; // Laphine UI
}
// should not get here, fallback

View File

@ -52,6 +52,7 @@ enum e_log_pick_type : uint32
LOG_TYPE_QUEST = 0x0400000,
LOG_TYPE_PRIVATE_AIRSHIP = 0x0800000,
LOG_TYPE_BARTER = 0x1000000,
LOG_TYPE_LAPHINE = 0x2000000,
// combinations
LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME,
// all

View File

@ -319,6 +319,8 @@
<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\job_noenter_map.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\job_noenter_map.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\job_stats.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\job_stats.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\laphine_synthesis.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\laphine_synthesis.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\laphine_upgrade.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\laphine_upgrade.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\level_penalty.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\level_penalty.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\magicmushroom_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\magicmushroom_db.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\map_cache.dat" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\map_cache.dat')" />

View File

@ -2102,17 +2102,6 @@ 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 item: Item data
@ -2126,40 +2115,7 @@ void mob_setdropitem_option(item *item, s_mob_drop *mobdrop) {
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);
}
}
group->apply( *item );
}
}

View File

@ -389,6 +389,8 @@ struct map_session_data {
bool refineui_open;
t_itemid inventory_expansion_confirmation;
uint16 inventory_expansion_amount;
t_itemid laphine_synthesis;
t_itemid laphine_upgrade;
} state;
struct {
unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
@ -1058,7 +1060,8 @@ static bool pc_cant_act2( struct map_session_data* sd ){
return sd->state.vending || sd->state.buyingstore || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING)
|| sd->state.trading || sd->state.storage_flag || sd->state.prevend || sd->state.refineui_open
|| sd->state.stylist_open || sd->state.inventory_expansion_confirmation || sd->npc_shopid
|| sd->state.barter_open || sd->state.barter_extended_open;
|| sd->state.barter_open || sd->state.barter_extended_open
|| sd->state.laphine_synthesis || sd->state.laphine_upgrade;
}
// equals pc_cant_act2 and additionally checks for chat rooms and npcs
static bool pc_cant_act( struct map_session_data* sd ){

View File

@ -25755,6 +25755,74 @@ BUILDIN_FUNC(getitempos) {
return SCRIPT_CMD_SUCCESS;
}
BUILDIN_FUNC( laphine_synthesis ){
struct map_session_data* sd;
if( !script_rid2sd( sd ) ){
return SCRIPT_CMD_FAILURE;
}
if( sd->itemid == 0 ){
ShowError( "buildin_laphine_synthesis: Called outside of an item script without item id.\n" );
return SCRIPT_CMD_FAILURE;
}
if( sd->inventory_data[sd->itemindex]->flag.delay_consume == 0 ){
ShowError( "buildin_laphine_synthesis: Called from item %u, which is not a consumed delayed.\n", sd->itemid );
return SCRIPT_CMD_FAILURE;
}
if( sd->state.laphine_synthesis != 0 ){
ShowError( "buildin_laphine_synthesis: Laphine Synthesis window was already open. Player %s (AID: %u, CID: %u) with item id %u.\n", sd->status.name, sd->status.account_id, sd->status.char_id, sd->itemid );
return SCRIPT_CMD_FAILURE;
}
std::shared_ptr<s_laphine_synthesis> synthesis = laphine_synthesis_db.find( sd->itemid );
if( synthesis == nullptr ){
ShowError( "buildin_laphine_synthesis: %u is not a valid Laphine Synthesis item.\n", sd->itemid );
return SCRIPT_CMD_FAILURE;
}
clif_laphine_synthesis_open( sd, synthesis );
return SCRIPT_CMD_SUCCESS;
}
BUILDIN_FUNC( laphine_upgrade ){
struct map_session_data* sd;
if( !script_rid2sd( sd ) ){
return SCRIPT_CMD_FAILURE;
}
if( sd->itemid == 0 ){
ShowError( "buildin_laphine_upgrade: Called outside of an item script without item id.\n" );
return SCRIPT_CMD_FAILURE;
}
if( sd->inventory_data[sd->itemindex]->flag.delay_consume == 0 ){
ShowError( "buildin_laphine_upgrade: Called from item %u, which is not a consumed delayed.\n", sd->itemid );
return SCRIPT_CMD_FAILURE;
}
if( sd->state.laphine_upgrade != 0 ){
ShowError( "buildin_laphine_upgrade: Laphine Upgrade window was already open. Player %s (AID: %u, CID: %u) with item id %u.\n", sd->status.name, sd->status.account_id, sd->status.char_id, sd->itemid );
return SCRIPT_CMD_FAILURE;
}
std::shared_ptr<s_laphine_upgrade> upgrade = laphine_upgrade_db.find( sd->itemid );
if( upgrade == nullptr ){
ShowError( "buildin_laphine_upgrade: %u is not a valid Laphine Upgrade item.\n", sd->itemid );
return SCRIPT_CMD_FAILURE;
}
clif_laphine_upgrade_open( sd, upgrade );
return SCRIPT_CMD_SUCCESS;
}
#include "../custom/script.inc"
// declarations that were supposed to be exported from npc_chat.cpp
@ -26464,6 +26532,8 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(openstylist, "?"),
BUILDIN_DEF(getitempos,""),
BUILDIN_DEF(laphine_synthesis, ""),
BUILDIN_DEF(laphine_upgrade, ""),
#include "../custom/script_def.inc"
{NULL,NULL,NULL},

View File

@ -5526,6 +5526,118 @@
export_constant(IG_THIRD_JOB_STONE_MIDDLE_BOX2);
export_constant(IG_THIRD_JOB_STONE_BOTTOM_BOX);
export_constant(IG_THIRD_JOB_STONE_BOTTOM_BOX2);
export_constant(IG_SHADOW_EXCHANGE_BOX);
export_constant(IG_DROOPING_GUNSLINGER_SCROLL);
export_constant(IG_ENCHANTSTONE_RECIPE);
export_constant(IG_PET_EGG_BOX);
export_constant(IG_COSTUME_EXCHANGE_BOX);
export_constant(IG_FAN_MODIFICATION_KIT);
export_constant(IG_UNIFORM_REPAIR_KIT);
export_constant(IG_SCROLL_OF_FALLEN_ANGEL_WINGS);
export_constant(IG_CLASS_SHADOW_BOX_WEAPON);
export_constant(IG_CLASS_SHADOW_BOX_ARMOR);
export_constant(IG_CLASS_SHADOW_BOX_SHOES);
export_constant(IG_CLASS_SHADOW_BOX_SHIELD);
export_constant(IG_CLASS_SHADOW_BOX_PENDANT);
export_constant(IG_CLASS_SHADOW_BOX_EARRING);
export_constant(IG_STATUSSHADOW_MIX);
export_constant(IG_GEMSTONESHADOW_MIX);
export_constant(IG_BEARERSSHADOW_MIX);
export_constant(IG_COMPOSESHADOW_MIX);
export_constant(IG_RACESHADOW_MIX);
export_constant(IG_CANDY_POUCH_BLESSING_SCROLL_MELEE);
export_constant(IG_CANDY_POUCH_BLESSING_SCROLL_RANGE);
export_constant(IG_CANDY_POUCH_BLESSING_SCROLL_MAGIC);
export_constant(IG_MAGICAL_BOOSTER_AMPLIFIER);
export_constant(IG_MAGIC_CAT_HAND_SCROLL);
export_constant(IG_INFINITYSHADOW_MIX);
export_constant(IG_SILVER_STATUE);
export_constant(IG_PHYSICALMAGICAL_MIX);
export_constant(IG_IMMUNEDATHENA_MIX);
export_constant(IG_HARDCHAMPTION_MIX);
export_constant(IG_KINGBIRDANCIENT_MIX);
export_constant(IG_CRITICALHIT_MIX);
export_constant(IG_BS_ITEM_M_S_2);
export_constant(IG_BS_ITEM_M_S_8);
export_constant(IG_BS_ITEM_M_S_10);
export_constant(IG_BS_ITEM_M_S_11);
export_constant(IG_BS_ITEM_M_S_34);
export_constant(IG_BS_ITEM_M_S_41);
export_constant(IG_BS_ITEM_M_S_42);
export_constant(IG_BS_ITEM_M_S_43);
export_constant(IG_BS_ITEM_M_S_44);
export_constant(IG_BS_SHA_M_S_1);
export_constant(IG_BS_SHA_M_S_17);
export_constant(IG_BS_SHA_M_S_18);
export_constant(IG_BS_SHA_M_S_19);
export_constant(IG_BS_SHA_M_S_20);
export_constant(IG_BS_ITEM_M_S_4);
export_constant(IG_BS_ITEM_M_S_6);
export_constant(IG_BS_ITEM_M_S_7);
export_constant(IG_BS_ITEM_M_S_12);
export_constant(IG_BS_ITEM_M_S_13);
export_constant(IG_BS_ITEM_M_S_15);
export_constant(IG_BS_ITEM_M_S_28);
export_constant(IG_BS_ITEM_M_S_29);
export_constant(IG_BS_ITEM_M_S_31);
export_constant(IG_BS_ITEM_M_S_32);
export_constant(IG_BS_ITEM_M_S_33);
export_constant(IG_BS_ITEM_M_S_36);
export_constant(IG_BS_ITEM_M_S_37);
export_constant(IG_BS_ITEM_M_S_38);
export_constant(IG_BS_ITEM_M_S_39);
export_constant(IG_BS_ITEM_M_S_40);
export_constant(IG_BS_ITEM_M_S_45);
export_constant(IG_BS_ITEM_M_S_46);
export_constant(IG_BS_ITEM_M_S_47);
export_constant(IG_BS_ITEM_M_S_48);
export_constant(IG_BS_ITEM_M_S_49);
export_constant(IG_BS_ITEM_M_S_50);
export_constant(IG_BS_SHA_M_S_5);
export_constant(IG_BS_SHA_M_S_6);
export_constant(IG_BS_SHA_M_S_7);
export_constant(IG_BS_SHA_M_S_8);
export_constant(IG_BS_SHA_M_S_13);
export_constant(IG_BS_SHA_M_S_15);
export_constant(IG_BS_SHA_M_S_16);
export_constant(IG_BS_SHA_M_S_23);
export_constant(IG_BS_ITEM_M_S_5);
export_constant(IG_BS_ITEM_M_S_9);
export_constant(IG_BS_ITEM_M_S_14);
export_constant(IG_BS_ITEM_M_S_16);
export_constant(IG_BS_ITEM_M_S_17);
export_constant(IG_BS_ITEM_M_S_19);
export_constant(IG_BS_ITEM_M_S_27);
export_constant(IG_BS_ITEM_M_S_35);
export_constant(IG_BS_SHA_M_S_9);
export_constant(IG_BS_SHA_M_S_10);
export_constant(IG_BS_SHA_M_S_11);
export_constant(IG_BS_SHA_M_S_21);
export_constant(IG_BS_ITEM_M_S_1);
export_constant(IG_BS_ITEM_M_S_3);
export_constant(IG_BS_ITEM_M_S_18);
export_constant(IG_BS_ITEM_M_S_20);
export_constant(IG_BS_ITEM_M_S_21);
export_constant(IG_BS_ITEM_M_S_22);
export_constant(IG_BS_ITEM_M_S_23);
export_constant(IG_BS_ITEM_M_S_24);
export_constant(IG_BS_ITEM_M_S_25);
export_constant(IG_BS_ITEM_M_S_26);
export_constant(IG_BS_ITEM_M_S_30);
export_constant(IG_BS_SHA_M_S_3);
export_constant(IG_BS_SHA_M_S_4);
export_constant(IG_BS_SHA_M_S_12);
export_constant(IG_BS_SHA_M_S_14);
export_constant(IG_BS_SHA_M_S_24);
export_constant(IG_BS_SHA_M_S_25);
export_constant(IG_BS_ITEM_M_S_51);
export_constant(IG_ENCHANTSTONE_RECIPE_9M);
export_constant(IG_IDTEST_SPECIAL);
export_constant(IG_PERFECTSIZE_MIX);
export_constant(IG_MAGICPIERCING_MIX);
export_constant(IG_PIERCING_MIX);
export_constant(IG_HASTY_MIX);
export_constant(IG_ENCHANTSTONE_RECIPE_4M);
/* unit stop walking */
export_constant(USW_NONE);

View File

@ -427,14 +427,14 @@ int do_init( int argc, char** argv ){
}
item_group_txt_data(path_db_mode, path_db);
if (!process("ITEM_GROUP_DB", 1, { path_db_mode }, "item_group_db", [](const std::string &path, const std::string &name_ext) -> bool {
if (!process("ITEM_GROUP_DB", 2, { path_db_mode }, "item_group_db", [](const std::string &path, const std::string &name_ext) -> bool {
return itemdb_read_group_yaml();
})) {
return 0;
}
item_group_txt_data(path_db_import, path_db_import);
if (!process("ITEM_GROUP_DB", 1, { path_db_import }, "item_group_db", [](const std::string &path, const std::string &name_ext) -> bool {
if (!process("ITEM_GROUP_DB", 2, { path_db_import }, "item_group_db", [](const std::string &path, const std::string &name_ext) -> bool {
return itemdb_read_group_yaml();
})) {
return 0;